octopusenergy

package module
v0.1.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 22, 2022 License: GPL-3.0 Imports: 13 Imported by: 0

README

logo

Octopus Energy Golang API client


PkgGoDev License Release tests Go Report Card GitHub go.mod Go version GitHub issues

This package provides a Golang client to Octopus Energy's API. Octopus Energy provides a REST API for customers to interact with our platform. Amongst other things, it provides functionality for:

  • Browsing energy products, tariffs and their charges.
  • Retrieving details about a UK electricity meter-point.
  • Browsing the half-hourly consumption of an electricity or gas meter.
  • Determining the grid-supply-point (GSP) for a UK postcode.

If you are an Octopus Energy customer, you can generate an API key from your online dashboard.

Authentication

Authentication is required for all API end-points when using this API client. This is performed via HTTP Basic Auth. This is configured when you instantiate a new client with a config object. Warning: Do not share your secret API keys with anyone.

Not an Octopus Energy customer?

Please read about the Octopus tariffs and ensure they are right for you, if you think they are, then please use my referral link. (at the time of writing this we will both receive £50 credit)

Usage

More in the examples folder

ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()

var netClient = http.Client{
    Timeout: time.Second * 10,
}

client := octopusenergy.NewClient(octopusenergy.NewConfig().
    WithApiKeyFromEnvironments().
    WithHTTPClient(netClient),
)

consumption, err := client.Consumption.GetPagesWithContext(ctx, &octopusenergy.ConsumptionGetOptions{
    MPN:          "1111111111", // <--- replace
    SerialNumber: "1111111111", // <--- replace
    FuelType:     octopusenergy.FuelTypeElectricity,
    PeriodFrom:   octopusenergy.Time(time.Now().Add(-48 * time.Hour)),
})

if err != nil {
    log.Fatalf("failed to getting consumption: %s", err.Error())
}

Documentation

Overview

Package octopusenergy proves an interface for Octopus Energy REST APIs.

Index

Examples

Constants

View Source
const (
	// ProductCodeAgile180221 is the product code to Octopus current Agile tariff
	ProductCodeAgile180221 = "AGILE-18-02-21"
)

Variables

This section is empty.

Functions

func Bool

func Bool(v bool) *bool

Bool returns a pointer to the bool value passed in. This is a helper function when you need to provide pointers to optional fields in the input options object.

Example
package main

import (
	"fmt"

	"github.com/totaldebug/octopusenergy"
)

func main() {
	boolPtr := octopusenergy.Bool(true)
	fmt.Println(*boolPtr)
}
Output:

true

func Int

func Int(v int) *int

Int returns a pointer to the int value passed in. This is a helper function when you need to provide pointers to optional fields in the input options object.

Example
package main

import (
	"fmt"

	"github.com/totaldebug/octopusenergy"
)

func main() {
	intPtr := octopusenergy.Int(5)
	fmt.Println(*intPtr)
}
Output:

5

func String

func String(v string) *string

String returns a pointer to the string value passed in. This is a helper function when you need to provide pointers to optional fields in the input options object.

Example
package main

import (
	"fmt"

	"github.com/totaldebug/octopusenergy"
)

func main() {
	stringPtr := octopusenergy.String("example")
	fmt.Println(*stringPtr)
}
Output:

example

func Time

func Time(v time.Time) *time.Time

Time returns a pointer to the time.Time value passed in. This is a helper function when you need to provide pointers to optional fields in the input options object.

Example
package main

import (
	"fmt"
	"time"

	"github.com/totaldebug/octopusenergy"
)

func main() {
	wayBack := time.Date(1974, time.May, 19, 1, 2, 3, 4, time.UTC)
	timePtr := octopusenergy.Time(wayBack)
	fmt.Println(*timePtr)
}
Output:

1974-05-19 01:02:03.000000004 +0000 UTC

Types

type AccountGetOptions

type AccountGetOptions struct {
	// The octopus account number to be retrieved.
	AccountNumber string `url:"-"`
}

AccountGetOptions is the options for GetTariffCharges.

type AccountGetOutput

type AccountGetOutput struct {
	Number     string `json:"number"`
	Properties []struct {
		ID                     int                      `json:"id"`
		MovedInAt              time.Time                `json:"moved_in_at"`
		MovedOutAt             *time.Time               `json:"moved_out_at"`
		AddressLine1           string                   `json:"address_line_1"`
		AddressLine2           string                   `json:"address_line_2"`
		AddressLine3           string                   `json:"address_line_3"`
		Town                   string                   `json:"town"`
		County                 string                   `json:"county"`
		Postcode               string                   `json:"postcode"`
		ElectricityMeterPoints []ElectricityMeterPoints `json:"electricity_meter_points"`
		GasMeterPoints         []GasMeterPoints         `json:"gas_meter_points"`
	} `json:"properties"`
}

AccountGetOutput is the returned struct from GetTariffCharges.

type AccountService

type AccountService service

AccountService handles communication with the tariff related Octopus API.

func (*AccountService) Get

Get retrieves the details of an account.

func (*AccountService) GetWithContext

func (s *AccountService) GetWithContext(ctx context.Context, options *AccountGetOptions) (*AccountGetOutput, error)

GetWithContext same as Get except it takes a Context

type ChargesData

type ChargesData struct {
	ValueExcVat float64    `json:"value_exc_vat"`
	ValueIncVat float64    `json:"value_inc_vat"`
	ValidFrom   time.Time  `json:"valid_from"`
	ValidTo     *time.Time `json:"valid_to"`
}

type Client

type Client struct {
	// Base URL for API requests. Defaults to the public Octopus API.
	BaseURL url.URL

	// HTTP client used to communicate with the API.
	HTTPClient *http.Client

	// Services used for talking to different parts of the Octopus API.
	TariffCharge    *TariffChargeService
	MeterPoint      *MeterPointService
	Product         *ProductService
	GridSupplyPoint *GridSupplyPointService
	Consumption     *ConsumptionService
	Account         *AccountService
	// contains filtered or unexported fields
}

func NewClient

func NewClient(cfg *Config) *Client

NewClient accepts a config object and returns an initiated client ready to use.

type Config

type Config struct {
	// Required for Authentication on non public API end-points when using this client.
	// If you are an Octopus Energy customer, you can generate an API key from your online dashboard
	// https://octopus.energy/dashboard/developer/.
	ApiKey *string

	// All API requests will use this base URL.
	Endpoint *string

	// The HTTP client to use when sending requests. Defaults to `http.DefaultClient`.
	HTTPClient *http.Client
}

Config provides service configuration for client.

func NewConfig

func NewConfig() *Config

NewConfig returns a new Config pointer that can be chained with builder methods to set multiple configuration values inline without using pointers.

client := octopusenergy.NewClient(octopusenergy.NewConfig().
    WithApiKey("your-api-key"),
))

func (*Config) WithApiKey

func (c *Config) WithApiKey(apiKey string) *Config

WithApiKey sets a config ApiKey value returning a Config pointer for chaining.

func (*Config) WithApiKeyFromEnvironments

func (c *Config) WithApiKeyFromEnvironments() *Config

WithApiKeyFromEnvironments sets a config ApiKey value from environments valuable returning a Config pointer for chaining.

func (*Config) WithEndpoint

func (c *Config) WithEndpoint(endpoint string) *Config

WithEndpoint sets a config Endpoint value returning a Config pointer for chaining.

func (*Config) WithHTTPClient

func (c *Config) WithHTTPClient(HTTPClient http.Client) *Config

WithHTTPClient sets a config HTTP Client value returning a Config pointer for chaining.

type ConsumptionData

type ConsumptionData struct {
	Consumption   float64 `json:"consumption"`
	IntervalStart string  `json:"interval_start"`
	IntervalEnd   string  `json:"interval_end"`
}

type ConsumptionGetOptions

type ConsumptionGetOptions struct {
	// The Meter Point Number this is the electricity meter-point’s MPAN or gas meter-point’s MPRN
	MPN string `url:"-"`

	// The meter’s serial number.
	SerialNumber string `url:"-"`

	// Fueltype: electricity or gas
	FuelType FuelType `url:"-"`

	// Show consumption from the given datetime (inclusive). This parameter can be provided on its own.
	PeriodFrom *time.Time `url:"period_from,omitempty" layout:"2006-01-02T15:04:05Z" optional:"true"`

	// Show consumption to the given datetime (exclusive).
	// This parameter also requires providing the period_from parameter to create a range.
	PeriodTo *time.Time `url:"period_to,omitempty" layout:"2006-01-02T15:04:05Z" optional:"true"`

	// Page size of returned results.
	// Default is 100, maximum is 25,000 to give a full year of half-hourly consumption details.
	PageSize *int `url:"page_size,omitempty" optional:"true"`

	// Ordering of results returned.
	// Default is that results are returned in reverse order from latest available figure.
	// Valid values: * ‘period’, to give results ordered forward. * ‘-period’, (default), to give results ordered from most recent backwards.
	OrderBy *string `url:"order_by,omitempty" optional:"true"`

	// Aggregates consumption over a specified time period.
	// A day is considered to start and end at midnight in the server’s timezone.
	// The default is that consumption is returned in half-hour periods. Accepted values are: * ‘hour’ * ‘day’ * ‘week’ * ‘month’ * ‘quarter’
	GroupBy *string `url:"group_by,omitempty" optional:"true"`

	// Pagination page to be returned on this request
	Page *int `url:"page,omitempty" optional:"true"`
}

ConsumptionGetOptions is the options for GetConsumption.

type ConsumptionGetOutput

type ConsumptionGetOutput struct {
	Count    int    `json:"count"`
	Next     string `json:"next"`
	Previous string `json:"previous"`
	Results  []struct {
		ConsumptionData
	} `json:"results"`
}

ConsumptionGetOutput is the returned struct from GetConsumption.

type ConsumptionService

type ConsumptionService service

ConsumptionService handles communication with the consumption related Octopus API.

func (*ConsumptionService) Get

Get consumption data for give meter details. This endpoint is paginated, it will return next and previous links if returned data is larger than the set page size, you are responsible to request the next page if required.

func (*ConsumptionService) GetPages

GetPages same as Get except it returns all pages in one request.

func (*ConsumptionService) GetPagesWithContext

func (s *ConsumptionService) GetPagesWithContext(ctx context.Context, options *ConsumptionGetOptions) (*ConsumptionGetOutput, error)

GetPagesWithContext same as GetPages except it takes a Context.

func (*ConsumptionService) GetWithContext

GetWithContext same as Get except it takes a Context.

type ElectricityMeterPoints

type ElectricityMeterPoints struct {
	MPAN                string `json:"mpan"`
	ProfileClass        int    `json:"profile_class"`
	ConsumptionStandard int    `json:"consumption_standard"`
	Meters              []struct {
		SerialNumber string `json:"serial_number"`
		Registers    []struct {
			Identifier           string `json:"identifier"`
			Rate                 string `json:"rate"`
			IsSettlementRegister bool   `json:"is_settlement_register"`
		} `json:"registers"`
	} `json:"meters"`
	Agreements []struct {
		TariffCode string     `json:"tariff_code"`
		ValidFrom  time.Time  `json:"valid_from"`
		ValidTo    *time.Time `json:"valid_to"`
	} `json:"agreements"`
}

type FuelType

type FuelType int
const (
	FuelTypeElectricity FuelType = iota // electricity
	FuelTypeGas                         // gas
)

func (FuelType) String

func (i FuelType) String() string

type GasMeterPoints

type GasMeterPoints struct {
	MPRN                string `json:"mprn"`
	ProfileClass        int    `json:"profile_class"`
	ConsumptionStandard int    `json:"consumption_standard"`
	Meters              []struct {
		SerialNumber string `json:"serial_number"`
		Registers    []struct {
			Identifier           string `json:"identifier"`
			Rate                 string `json:"rate"`
			IsSettlementRegister bool   `json:"is_settlement_register"`
		} `json:"registers"`
	} `json:"meters"`
	Agreements []struct {
		TariffCode string     `json:"tariff_code"`
		ValidFrom  time.Time  `json:"valid_from"`
		ValidTo    *time.Time `json:"valid_to"`
	} `json:"agreements"`
}

type GridSupplyPointGetOptions

type GridSupplyPointGetOptions struct {
	// A postcode to filter on.
	// If Octopus are unable to map the passed postcode to a GSP, an empty list will be returned.
	Postcode *string `url:"postcode,omitempty" optional:"true"`
}

GridSupplyPointGetOptions is the options for GetGridSupplyPoint.

type GridSupplyPointGetOutput

type GridSupplyPointGetOutput struct {
	Count   int `json:"count"`
	Results []struct {
		GroupID string `json:"group_id"`
	} `json:"results"`
}

GridSupplyPointGetOutput is the returned struct from GetGridSupplyPoint.

type GridSupplyPointService

type GridSupplyPointService service

GridSupplyPointService handles communication with the grid supply point related Octopus API.

func (*GridSupplyPointService) Get

Get gets the GSP and group ID, filtered by postcode if one is given.

func (*GridSupplyPointService) GetWithContext

GetWithContext same as Get except it takes a Context.

type MeterPointGetOptions

type MeterPointGetOptions struct {
	// The electricity meter-point’s MPAN.
	MPAN string `url:"-"`
}

MeterPointGetOptions is the options for GetMeterPoint.

type MeterPointGetOutput

type MeterPointGetOutput struct {
	// Grid Supply Point
	GSP string `json:"gsp"`

	// The electricity meter-point’s MPAN.
	MPAN string `json:"mpan"`

	// ProfileClass
	ProfileClass int `json:"profile_class"`
}

MeterPointGetOutput is the returned struct from GetMeterPoint.

type MeterPointService

type MeterPointService service

MeterPointService handles communication with the meter point related Octopus API.

func (*MeterPointService) Get

Get the GSP and profile of a given MPAN.

func (*MeterPointService) GetWithContext

func (s *MeterPointService) GetWithContext(ctx context.Context, options *MeterPointGetOptions) (*MeterPointGetOutput, error)

GetWithContext same as Get except it takes a Context

type ProductService

type ProductService service

ProductService handles communication with the product related Octopus API.

func (*ProductService) Get

Get retrieves the details of a product (including all its tariffs) for a particular point in time. This endpoint is paginated, it will return next and previous links if returned data is larger than the set page size, you are responsible to request the next page if required.

func (*ProductService) GetWithContext

func (s *ProductService) GetWithContext(ctx context.Context, options *ProductsGetOptions) (*ProductsGetOutput, error)

GetWithContext same as Get except it takes a Context

func (*ProductService) List

List return a list of energy products. This endpoint is paginated, it will return next and previous links if returned data is larger than the set page size, you are responsible to request the next page if required.

func (*ProductService) ListPages

func (s *ProductService) ListPages(options *ProductsListOptions) (*ProductsListOutput, error)

ListPages same as List except it returns all pages in one request.

func (*ProductService) ListPagesWithContext

func (s *ProductService) ListPagesWithContext(ctx context.Context, options *ProductsListOptions) (*ProductsListOutput, error)

ListPagesWithContext same as ListPages except it takes a Context.

func (*ProductService) ListWithContext

func (s *ProductService) ListWithContext(ctx context.Context, options *ProductsListOptions) (*ProductsListOutput, error)

ListWithContext same as ListProducts except it takes a Context.

type ProductsGetOptions

type ProductsGetOptions struct {
	// The code of the product to be retrieved.
	ProductCode string `url:"-"`

	// The point in time in which to show the active charges. Defaults to current datetime.
	TariffsActiveAt *time.Time `url:"tariffs_active_at" layout:"2006-01-02T15:04:05Z" optional:"true"`
}

ProductsGetOptions is the options for GetProduct.

type ProductsGetOutput

type ProductsGetOutput struct {
	Code                             string                 `json:"code"`
	FullName                         string                 `json:"full_name"`
	DisplayName                      string                 `json:"display_name"`
	Description                      string                 `json:"description"`
	IsVariable                       bool                   `json:"is_variable"`
	IsGreen                          bool                   `json:"is_green"`
	IsTracker                        bool                   `json:"is_tracker"`
	IsPrepay                         bool                   `json:"is_prepay"`
	IsBusiness                       bool                   `json:"is_business"`
	IsRestricted                     bool                   `json:"is_restricted"`
	Brand                            string                 `json:"brand"`
	Term                             int                    `json:"term"`
	AvailableFrom                    time.Time              `json:"available_from"`
	AvailableTo                      time.Time              `json:"available_to"`
	TariffsActiveAt                  time.Time              `json:"tariffs_active_at"`
	SingleRegisterElectricityTariffs map[string]Tariff      `json:"single_register_electricity_tariffs"`
	DualRegisterElectricityTariffs   map[string]Tariff      `json:"dual_register_electricity_tariffs"`
	SingleRegisterGasTariffs         map[string]Tariff      `json:"single_register_gas_tariffs"`
	SampleQuotes                     map[string]SampleQuote `json:"sample_quotes"`
	SampleConsumption                SampleConsumption      `json:"sample_consumption"`
	Links                            []struct {
		Href   string `json:"href"`
		Method string `json:"method"`
		Rel    string `json:"rel"`
	} `json:"links"`
}

ProductsGetOutput is the returned struct from GetProduct.

type ProductsListOptions

type ProductsListOptions struct {
	// Show only variable products.
	IsVariable *bool `url:"is_variable,omitempty" optional:"true"`

	// Show only green products.
	IsGreen *bool `url:"is_green,omitempty" optional:"true"`

	// Show only tracker products.
	IsTracker *bool `url:"is_tracker,omitempty" optional:"true"`

	// Show only pre-pay products.
	IsPrepay *bool `url:"is_prepay,omitempty" optional:"true"`

	// Show only business products.
	IsBusiness *bool `url:"is_business,omitempty" optional:"true"`

	// Show products available for new agreements on the given datetime.
	// Defaults to current datetime, effectively showing products that are currently available.
	AvailableAt *time.Time `url:"available_at,omitempty" layout:"2006-01-02T15:04:05Z" optional:"true"`

	// Pagination page to be returned on this request
	Page *int `url:"page,omitempty" optional:"true"`
}

ProductsListOptions is the options for ListProduct.

type ProductsListOutput

type ProductsListOutput struct {
	Count    int    `json:"count"`
	Next     string `json:"next"`
	Previous string `json:"previous"`
	Results  []struct {
		Code          string      `json:"code"`
		FullName      string      `json:"full_name"`
		DisplayName   string      `json:"display_name"`
		Description   string      `json:"description"`
		IsVariable    bool        `json:"is_variable"`
		IsGreen       bool        `json:"is_green"`
		IsTracker     bool        `json:"is_tracker"`
		IsPrepay      bool        `json:"is_prepay"`
		IsBusiness    bool        `json:"is_business"`
		IsRestricted  bool        `json:"is_restricted"`
		Term          int         `json:"term"`
		Brand         string      `json:"brand"`
		AvailableFrom time.Time   `json:"available_from"`
		AvailableTo   interface{} `json:"available_to"`
		Links         []struct {
			Href   string `json:"href"`
			Method string `json:"method"`
			Rel    string `json:"rel"`
		} `json:"links"`
	} `json:"results"`
}

ProductsListOutput is the returned struct from ListProduct.

type Rate

type Rate int

Rate is the type of charge, example standing-charges, day-unit-rates, standard-unit-rates

const (
	RateStandingCharge Rate = iota // standing-charges
	RateStandardUnit               // standard-unit-rates
	RateDayUnit                    // day-unit-rates
	RateNightUnit                  // night-unit-rates
)

func (Rate) String

func (i Rate) String() string

type SampleConsumption

type SampleConsumption struct {
	ElectricitySingleRate struct {
		ElectricityStandard int `json:"electricity_standard"`
	} `json:"electricity_single_rate"`
	ElectricityDualRate struct {
		ElectricityDay   int `json:"electricity_day"`
		ElectricityNight int `json:"electricity_night"`
	} `json:"electricity_dual_rate"`
	DualFuelSingleRate struct {
		ElectricityStandard int `json:"electricity_standard"`
		GasStandard         int `json:"gas_standard"`
	} `json:"dual_fuel_single_rate"`
	DualFuelDualRate struct {
		ElectricityDay   int `json:"electricity_day"`
		ElectricityNight int `json:"electricity_night"`
		GasStandard      int `json:"gas_standard"`
	} `json:"dual_fuel_dual_rate"`
}

type SampleQuote

type SampleQuote struct {
	DirectDebitMonthly   SampleQuoteDirectDebit `json:"direct_debit_monthly"`
	DirectDebitQuarterly SampleQuoteDirectDebit `json:"direct_debit_quarterly"`
}

type SampleQuoteDirectDebit

type SampleQuoteDirectDebit struct {
	ElectricitySingleRate SampleQuoteDirectDebitRate `json:"electricity_single_rate"`
	ElectricityDualRate   SampleQuoteDirectDebitRate `json:"electricity_dual_rate"`
	DualFuelSingleRate    SampleQuoteDirectDebitRate `json:"dual_fuel_single_rate"`
	DualFuelDualRate      SampleQuoteDirectDebitRate `json:"dual_fuel_dual_rate"`
}

type SampleQuoteDirectDebitRate

type SampleQuoteDirectDebitRate struct {
	AnnualCostIncVat int `json:"annual_cost_inc_vat"`
	AnnualCostExcVat int `json:"annual_cost_exc_vat"`
}

type Tariff

type Tariff struct {
	DirectDebitMonthly   TariffDirectDebit `json:"direct_debit_monthly"`
	DirectDebitQuarterly TariffDirectDebit `json:"direct_debit_quarterly"`
}

type TariffChargeService

type TariffChargeService service

TariffChargeService handles communication with the tariff related Octopus API.

func (*TariffChargeService) Get

Get retrieves the details of a tariffs changes. This endpoint is paginated, it will return next and previous links if returned data is larger than the set page size, you are responsible to request the next page if required.

func (*TariffChargeService) GetPages

GetPages same as Get except it returns all pages in one request

func (*TariffChargeService) GetPagesWithContext

func (s *TariffChargeService) GetPagesWithContext(ctx context.Context, options *TariffChargesGetOptions) (*TariffChargesGetOutput, error)

GetPagesWithContext same as GetPages except it takes a Context

func (*TariffChargeService) GetWithContext

GetWithContext same as Get except it takes a Context

type TariffChargesGetOptions

type TariffChargesGetOptions struct {
	// The code of the product to be retrieved.
	ProductCode string `url:"-"`

	// The code of the tariff to be retrieved.
	TariffCode string `url:"-"`

	// Fueltype: electricity or gas
	FuelType FuelType `url:"-"`

	// The type of charge
	Rate Rate `url:"-"`

	// Show charges active from the given datetime (inclusive). This parameter can be provided on its own.
	PeriodFrom *time.Time `url:"period_from,omitempty" layout:"2006-01-02T15:04:05Z" optional:"true"`

	// Show charges active up to the given datetime (exclusive).
	// You must also provide the period_from parameter in order to create a range.
	PeriodTo *time.Time `url:"period_to,omitempty" layout:"2006-01-02T15:04:05Z" optional:"true"`

	// Page size of returned results.
	// Default is 100, maximum is 1,500 to give up to a month of half-hourly prices.
	PageSize *int `url:"page_size,omitempty" optional:"true"`

	// Pagination page to be returned on this request
	Page *int `url:"page,omitempty" optional:"true"`
}

TariffChargesGetOptions is the options for GetTariffCharges.

type TariffChargesGetOutput

type TariffChargesGetOutput struct {
	Count    int    `json:"count"`
	Next     string `json:"next"`
	Previous string `json:"previous"`
	Results  []struct {
		ChargesData
	} `json:"results"`
}

TariffChargesGetOutput is the returned struct from GetTariffCharges.

type TariffDirectDebit

type TariffDirectDebit struct {
	Code                   string  `json:"code"`
	StandardUnitRateExcVat float64 `json:"standard_unit_rate_exc_vat"`
	StandardUnitRateIncVat float64 `json:"standard_unit_rate_inc_vat"`
	StandingChargeExcVat   float64 `json:"standing_charge_exc_vat"`
	StandingChargeIncVat   float64 `json:"standing_charge_inc_vat"`
	OnlineDiscountExcVat   int     `json:"online_discount_exc_vat"`
	OnlineDiscountIncVat   int     `json:"online_discount_inc_vat"`
	DualFuelDiscountExcVat int     `json:"dual_fuel_discount_exc_vat"`
	DualFuelDiscountIncVat int     `json:"dual_fuel_discount_inc_vat"`
	ExitFeesExcVat         int     `json:"exit_fees_exc_vat"`
	ExitFeesIncVat         int     `json:"exit_fees_inc_vat"`
	Links                  []struct {
		Href   string `json:"href"`
		Method string `json:"method"`
		Rel    string
	} `json:"direct_debit_monthly"`
}

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL