commit 4491e4491316aed7365c2337981cd54189aa2080 Author: Nicolas MASSE Date: Fri Oct 28 22:03:40 2022 +0200 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8204c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © 2022 Nicolas Massé + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..26b3306 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Ego2mix + +This Go library calls the Eco2mix API from RTE and returns data about the French electricity grid. diff --git a/eco2mix.go b/eco2mix.go new file mode 100644 index 0000000..4eea960 --- /dev/null +++ b/eco2mix.go @@ -0,0 +1,159 @@ +package eco2mix + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" +) + +type Eco2mixClient struct { + BaseUrl string + httpClient *http.Client +} + +func NewEco2mixClient(baseUrl string, client *http.Client) *Eco2mixClient { + if baseUrl == "" { + baseUrl = ECO2MIX_NATIONAL_REALTIME_BASEURL + } + + if client == nil { + client = http.DefaultClient + } + + c := Eco2mixClient{ + BaseUrl: baseUrl, + httpClient: client, + } + + return &c +} + +func (client *Eco2mixClient) FetchNationalRealTimeData(maxResults int) ([]NationalRealTimeFields, error) { + resp, err := client.httpClient.Get(fmt.Sprintf(ECO2MIX_NATIONAL_REALTIME_PATH, client.BaseUrl, maxResults)) + if err != nil { + return nil, err + } + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, err + } + + var data NationalRealTimeResponse + err = json.Unmarshal(body, &data) + if err != nil { + return nil, err + } + + var fields []NationalRealTimeFields + for _, r := range data.Records { + fields = append(fields, r.Fields) + } + + return fields, nil +} + +const ( + ECO2MIX_NATIONAL_REALTIME_PATH = `%s/api/records/1.0/search/?dataset=eco2mix-national-tr&q=&facet=nature&facet=date_heure&start=0&rows=%d` + ECO2MIX_NATIONAL_REALTIME_BASEURL = `https://odre.opendatasoft.com` +) + +// Ce jeu de données, rafraîchi une fois par heure, présente des données +// "temps réel" issues de l'application éCO2mix. Elles proviennent des +// télémesures des ouvrages, complétées par des forfaits et estimations. +// +// Vous y trouverez au pas quart d'heure : +// +// - La consommation réalisée. +// - Les prévisions de consommation établies la veille (J-1) et celles +// réactualisées le jour même (J). +// - La production selon les différentes filières composant le mix énergétique. +// - La consommation des pompes dans les Stations de Transfert d'Energie +// par Pompage (STEP). +// - Les échanges physiques aux frontières. +// - Une estimation des émissions de carbone générées par la production +// d'électricité en France. +// - Le découpage en filière et technologie du mix de production. +// +// Vous y trouverez au pas demi-heure : +// +// - Les échanges commerciaux aux frontières. +// +type NationalRealTimeFields struct { + Bioenergies int64 `json:"bioenergies"` // Bioénergies (MW) + BioenergiesBiogaz int64 `json:"bioenergies_biogaz"` // Bioénergies - Biogaz (MW) - Production bioénergies réalisée à partir du biogaz + BioenergiesBiomasse int64 `json:"bioenergies_biomasse"` // Bioénergies - Biomasse (MW) - Production bioénergies réalisée à partir de la biomasse + BioenergiesDechets int64 `json:"bioenergies_dechets"` // Bioénergies - Déchets (MW) - Production bioénergies réalisée à partir des déchets + Charbon int64 `json:"charbon"` // Charbon (MW) + Consommation int64 `json:"consommation"` // Consommation (MW) + Date string `json:"date"` // Date + DateHeure string `json:"date_heure"` // Date - Heure + DestockageBatterie string `json:"destockage_batterie"` // Déstockage batterie (MW) + EchCommAllemagneBelgique string `json:"ech_comm_allemagne_belgique"` // Ech. comm. Allemagne-Belgique (MW) - Solde des échanges commerciaux entre la France et la zone Allemagne et Belgique. Exportateur si négatif, importateur si positif. + EchCommAngleterre string `json:"ech_comm_angleterre"` // Ech. comm. Angleterre (MW) - Solde des échanges commerciaux entre la France et l'Angleterre. Exportateur si négatif, importateur si positif. + EchCommEspagne int64 `json:"ech_comm_espagne"` // Ech. comm. Espagne (MW) - Solde des échanges commerciaux entre la France et l'Espagne. Exportateur si négatif, importateur si positif. + EchCommItalie int64 `json:"ech_comm_italie"` // Ech. comm. Italie (MW) - Solde des échanges commerciaux entre la France et l'Italie. Exportateur si négatif, importateur si positif. + EchCommSuisse int64 `json:"ech_comm_suisse"` // Ech. comm. Suisse (MW) - Solde des échanges commerciaux entre la France et la Suisse. Exportateur si négatif, importateur si positif. + EchPhysiques int64 `json:"ech_physiques"` // Ech. physiques (MW) - Solde des échanges physiques aux interconnexions avec les autres pays: Exportateur si négatif, importateur si positif. + Eolien int64 `json:"eolien"` // Eolien (MW) + EolienOffshore string `json:"eolien_offshore"` // Eolien offshore (MW) + EolienTerrestre string `json:"eolien_terrestre"` // Eolien terrestre (MW) + Fioul int64 `json:"fioul"` // Fioul (MW) + FioulAutres int64 `json:"fioul_autres"` // Fioul - Autres (MW) + FioulCogen int64 `json:"fioul_cogen"` // Fioul - Cogénération (MW) - Production des cogénérations fonctionnant au fioul + FioulTac int64 `json:"fioul_tac"` // Fioul - TAC (MW) - Production des Turbines à Combustion fonctionnant au fioul + Gaz int64 `json:"gaz"` // Gaz (MW) + GazAutres int64 `json:"gaz_autres"` // Gaz - Autres (MW) - Autres technologies fonctionnant au gaz + GazCcg int64 `json:"gaz_ccg"` // Gaz - CCG (MW) - Production des Cycles Combinés Gaz + GazCogen int64 `json:"gaz_cogen"` // Gaz - Cogénération (MW) - Production des cogénérations fonctionnant au gaz + GazTac int64 `json:"gaz_tac"` // Gaz - TAC (MW) - Production des Turbines à Combustion fonctionnant au gaz + Heure string `json:"heure"` // Heure + Hydraulique int64 `json:"hydraulique"` // Hydraulique (MW) + HydrauliqueFilEauEclusee int64 `json:"hydraulique_fil_eau_eclusee"` // Hydraulique - Fil de l'eau + éclusée (MW) + HydrauliqueLacs int64 `json:"hydraulique_lacs"` // Hydraulique - Lacs (MW) + HydrauliqueStepTurbinage int64 `json:"hydraulique_step_turbinage"` // Hydraulique - STEP turbinage (MW) - Production hydraulique issue des Stations de Transfert d'Energie par Pompage. + Nature string `json:"nature"` // Uniquement "Données temps réel" pour ce jeu de données. + Nucleaire int64 `json:"nucleaire"` // Nucléaire (MW) + Perimetre string `json:"perimetre"` // Uniquement France pour ce jeu de données. + Pompage int64 `json:"pompage"` // Pompage (MW) - Puissance consommée par les pompes dans les Stations de Transfert d'Energie par Pompage (STEP) + PrevisionJ int64 `json:"prevision_j"` // Prévision J (MW) - Prévision, réalisée le jour même, de la consommation . + PrevisionJ1 int64 `json:"prevision_j1"` // Prévision J-1 (MW) - Prévision, réalisée la veille pour le lendemain, de la consommation. + Solaire int64 `json:"solaire"` // Solaire (MW) + StockageBatterie string `json:"stockage_batterie"` // Stockage batterie (MW) + TauxCo2 int64 `json:"taux_co2"` // Taux de CO2 (g/kWh) - Estimation des émissions de carbone générées par la production d'électricité en France. +} + +type NationalRealTimeRecord struct { + Datasetid string `json:"datasetid"` + Fields NationalRealTimeFields `json:"fields"` + RecordTimestamp string `json:"record_timestamp"` + Recordid string `json:"recordid"` +} + +// NationalRealTimeResponse represents the response to the "eco2mix-national-tr" +// dataset from RTE. +// +// Documentations is available at: +// https://odre.opendatasoft.com/explore/dataset/eco2mix-national-tr/information/ +type NationalRealTimeResponse struct { + FacetGroups []struct { + Facets []struct { + Count int64 `json:"count"` + Name string `json:"name"` + Path string `json:"path"` + State string `json:"state"` + } `json:"facets"` + Name string `json:"name"` + } `json:"facet_groups"` + Nhits int64 `json:"nhits"` + Parameters struct { + Dataset string `json:"dataset"` + Facet []string `json:"facet"` + Format string `json:"format"` + Rows int64 `json:"rows"` + Start int64 `json:"start"` + Timezone string `json:"timezone"` + } `json:"parameters"` + Records []NationalRealTimeRecord `json:"records"` +} diff --git a/eco2mix_test.go b/eco2mix_test.go new file mode 100644 index 0000000..06eaa99 --- /dev/null +++ b/eco2mix_test.go @@ -0,0 +1,207 @@ +package eco2mix + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" +) + +func TestFetchNationalRealTimeData(t *testing.T) { + expected := `{ + "nhits": 14496, + "parameters": { + "dataset": "eco2mix-national-tr", + "rows": 3, + "start": 0, + "facet": [ + "nature", + "date_heure" + ], + "format": "json", + "timezone": "UTC" + }, + "records": [ + { + "datasetid": "eco2mix-national-tr", + "recordid": "19d24188796afae79904f3e38bda937415a7f65b", + "fields": { + "consommation": 52770, + "fioul_cogen": 33, + "eolien": 977, + "bioenergies_dechets": 149, + "ech_physiques": 4196, + "fioul_tac": 347, + "charbon": 28, + "bioenergies": 752, + "nucleaire": 28172, + "prevision_j": 53000, + "gaz_ccg": 4063, + "hydraulique_step_turbinage": 960, + "date": "2022-07-12", + "gaz": 4614, + "solaire": 8223, + "stockage_batterie": "ND", + "hydraulique": 5316, + "fioul_autres": 113, + "heure": "16:45", + "taux_co2": 57, + "gaz_cogen": 553, + "destockage_batterie": "ND", + "nature": "Données temps réel", + "date_heure": "2022-07-12T14:45:00+00:00", + "pompage": -1, + "bioenergies_biomasse": 329, + "bioenergies_biogaz": 275, + "fioul": 493, + "eolien_offshore": "ND", + "prevision_j1": 52200, + "gaz_tac": 0, + "gaz_autres": 0, + "hydraulique_lacs": 1831, + "eolien_terrestre": "ND", + "perimetre": "France", + "hydraulique_fil_eau_eclusee": 2525 + }, + "record_timestamp": "2022-10-28T19:00:00.369Z" + }, + { + "datasetid": "eco2mix-national-tr", + "recordid": "072df8b1f6df8c9fa3890becf14abc9a75cd0417", + "fields": { + "consommation": 52023, + "fioul_cogen": 36, + "eolien": 1101, + "bioenergies_dechets": 153, + "ech_physiques": 3968, + "fioul_tac": 410, + "charbon": 29, + "bioenergies": 758, + "nucleaire": 28143, + "prevision_j": 52250, + "gaz_ccg": 4295, + "hydraulique_step_turbinage": 1225, + "date": "2022-07-12", + "gaz": 4816, + "solaire": 6393, + "stockage_batterie": "ND", + "hydraulique": 6256, + "fioul_autres": 113, + "heure": "17:45", + "taux_co2": 60, + "gaz_cogen": 522, + "destockage_batterie": "ND", + "nature": "Données temps réel", + "date_heure": "2022-07-12T15:45:00+00:00", + "pompage": 0, + "bioenergies_biomasse": 329, + "bioenergies_biogaz": 275, + "fioul": 559, + "eolien_offshore": "ND", + "prevision_j1": 51450, + "gaz_tac": 0, + "gaz_autres": 0, + "hydraulique_lacs": 2074, + "eolien_terrestre": "ND", + "perimetre": "France", + "hydraulique_fil_eau_eclusee": 2957 + }, + "record_timestamp": "2022-10-28T19:00:00.369Z" + }, + { + "datasetid": "eco2mix-national-tr", + "recordid": "98ee9cdc438fd4e2ead5907025a36d210cf22cc1", + "fields": { + "ech_comm_suisse": 1193, + "consommation": 52387, + "fioul_cogen": 36, + "eolien": 1082, + "bioenergies_dechets": 154, + "ech_physiques": 6782, + "fioul_tac": 484, + "charbon": 29, + "bioenergies": 757, + "nucleaire": 28158, + "prevision_j": 52400, + "ech_comm_angleterre": "2585", + "gaz_ccg": 4327, + "ech_comm_allemagne_belgique": "3886", + "hydraulique_step_turbinage": 1695, + "date": "2022-07-12", + "gaz": 4852, + "solaire": 2561, + "stockage_batterie": "ND", + "hydraulique": 7569, + "fioul_autres": 113, + "heure": "19:30", + "taux_co2": 65, + "gaz_cogen": 526, + "destockage_batterie": "ND", + "nature": "Données temps réel", + "date_heure": "2022-07-12T17:30:00+00:00", + "pompage": -36, + "bioenergies_biomasse": 330, + "bioenergies_biogaz": 275, + "fioul": 633, + "eolien_offshore": "ND", + "prevision_j1": 51600, + "ech_comm_italie": -1565, + "gaz_tac": 0, + "ech_comm_espagne": 900, + "gaz_autres": 0, + "hydraulique_lacs": 2459, + "eolien_terrestre": "ND", + "perimetre": "France", + "hydraulique_fil_eau_eclusee": 3415 + }, + "record_timestamp": "2022-10-28T19:00:00.369Z" + } + ], + "facet_groups": [ + { + "name": "nature", + "facets": [ + { + "name": "Données temps réel", + "count": 14496, + "state": "displayed", + "path": "Données temps réel" + } + ] + }, + { + "name": "date_heure", + "facets": [ + { + "name": "2022", + "count": 14496, + "state": "displayed", + "path": "2022" + } + ] + } + ] + }` + + svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, expected) + })) + defer svr.Close() + + c := NewEco2mixClient(svr.URL, nil) + res, err := c.FetchNationalRealTimeData(3) + if err != nil { + t.Errorf("expected err to be nil got %v", err) + } + if len(res) != 3 { + t.Errorf("expected 3 records, got %d", len(res)) + } + + expectedTauxCo2 := []int64{57, 60, 65} + for i := range expectedTauxCo2 { + if res[i].TauxCo2 != expectedTauxCo2[i] { + t.Errorf("record %d: expected value %d, got %d", i, expectedTauxCo2[i], res[i].TauxCo2) + + } + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..e194a55 --- /dev/null +++ b/go.mod @@ -0,0 +1,6 @@ +module github.com/nmasse-itix/ego2mix + +go 1.18 + +require ( +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..442875a --- /dev/null +++ b/go.sum @@ -0,0 +1,10 @@ +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= +github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=