@powercalc/power-router 1.2.4 → 1.2.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,7 +2,7 @@ import { parseStringPromise } from 'xml2js';
2
2
  import { readFile, writeFile } from 'node:fs/promises';
3
3
 
4
4
  import { join } from 'node:path';
5
- import { add, format, sub } from 'date-fns'
5
+ import { add, format, sub } from 'date-fns';
6
6
  import { Entsoe } from '../interfaces/entsoe';
7
7
  import { Format, QueryOptions } from '../interfaces/queryoptions';
8
8
 
@@ -12,7 +12,7 @@ export interface AllEntsoe {
12
12
 
13
13
  export interface EntsoeConfig {
14
14
  entsoeDomain: string;
15
- countries: Country[]
15
+ countries: Country[];
16
16
  }
17
17
 
18
18
  export interface Country {
@@ -26,13 +26,13 @@ export interface Data {
26
26
  }
27
27
 
28
28
  export interface LoaderConfig {
29
- source?: string
30
- securityToken: string
31
- cacheDir?: string
29
+ source?: string;
30
+ securityToken: string;
31
+ cacheDir?: string;
32
32
  entsoeDomain: string;
33
33
  }
34
34
  interface KV {
35
- [key: string]: string
35
+ [key: string]: string;
36
36
  }
37
37
 
38
38
  export class EntsoeLoader {
@@ -42,19 +42,33 @@ export class EntsoeLoader {
42
42
  this.config = loaderConfig;
43
43
  }
44
44
 
45
- async load(wanted: string[], countryCode: string, year: number, month: number, queryOptions?: QueryOptions): Promise<any> {
45
+ async load(
46
+ wanted: string[],
47
+ countryCode: string,
48
+ year: number,
49
+ month: number,
50
+ queryOptions?: QueryOptions
51
+ ): Promise<any> {
46
52
  const urls = this.makeUrls(wanted, countryCode, year, month);
47
- if(queryOptions?.format === Format.SOURCES){
53
+ if (queryOptions?.format === Format.SOURCES) {
48
54
  return urls;
49
55
  }
50
- const allJsons = await this.fetchAndMakeJson(Object.values(urls), queryOptions?.reload);
51
- const result: AllEntsoe = {}
52
- Object.keys(urls).forEach((key, i) => {
53
- if (allJsons[i]) {
54
- result[key] = allJsons[i] as Entsoe;
55
- }
56
- })
57
- return result;
56
+ try {
57
+ const allJsons = await this.fetchAndMakeJson(
58
+ Object.values(urls),
59
+ queryOptions?.reload
60
+ );
61
+ const result: AllEntsoe = {};
62
+ Object.keys(urls).forEach((key, i) => {
63
+ if (allJsons[i]) {
64
+ result[key] = allJsons[i] as Entsoe;
65
+ }
66
+ });
67
+ return result;
68
+ } catch (e) {
69
+ console.error(e);
70
+ throw e;
71
+ }
58
72
  }
59
73
 
60
74
  makeUrls(wanted: string[], countryCode: string, year: number, month: number) {
@@ -62,16 +76,22 @@ export class EntsoeLoader {
62
76
  start = sub(start, { hours: 3 });
63
77
  let end = new Date(Date.UTC(year, month));
64
78
  end = add(end, { hours: 3 });
65
- const periodStart = format(start, 'yyyyMMddHHmm')
66
- const periodEnd = format(end, 'yyyyMMddHHmm')
79
+ const periodStart = format(start, 'yyyyMMddHHmm');
80
+ const periodEnd = format(end, 'yyyyMMddHHmm');
67
81
 
68
82
  const hydroStart = sub(start, { days: 7 });
69
- const hydroPeriodStart = format(hydroStart, 'yyyyMMddHHmm')
70
- const hydroPeriodEnd = format(add(hydroStart, { days: 380 }), 'yyyyMMddHHmm')
83
+ const hydroPeriodStart = format(hydroStart, 'yyyyMMddHHmm');
84
+ const hydroPeriodEnd = format(
85
+ add(hydroStart, { days: 380 }),
86
+ 'yyyyMMddHHmm'
87
+ );
71
88
 
72
89
  let priceCountryCode = countryCode;
73
90
  //germany luxemburg
74
- if (countryCode === '10Y1001A1001A83F' || countryCode === '10YLU-CEGEDEL-NQ') {
91
+ if (
92
+ countryCode === '10Y1001A1001A83F' ||
93
+ countryCode === '10YLU-CEGEDEL-NQ'
94
+ ) {
75
95
  priceCountryCode = '10Y1001A1001A82H';
76
96
  }
77
97
  //italy
@@ -84,51 +104,56 @@ export class EntsoeLoader {
84
104
  }
85
105
  //denmark dk1
86
106
  if (countryCode === '10Y1001A1001A65H') {
87
- priceCountryCode = '10YDK-1--------W'
107
+ priceCountryCode = '10YDK-1--------W';
88
108
  }
89
109
  //norway no1
90
110
  if (countryCode === '10YNO-0--------C') {
91
- priceCountryCode = '10YNO-1--------2'
111
+ priceCountryCode = '10YNO-1--------2';
92
112
  }
93
113
  //ireland
94
114
  if (countryCode === '10Y1001A1001A59C') {
95
- priceCountryCode = '10YIE-1001A00010'
115
+ priceCountryCode = '10YIE-1001A00010';
96
116
  }
97
- const generation = `${this.config.entsoeDomain}/api?documentType=A75&processType=A16&in_Domain=${countryCode}&outBiddingZone_Domain=${countryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`
98
- const price = `${this.config.entsoeDomain}/api?documentType=A44&in_Domain=${priceCountryCode}&out_Domain=${priceCountryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`
99
- const consumption = `${this.config.entsoeDomain}/api?documentType=A65&processType=A16&in_Domain=${countryCode}&outBiddingZone_Domain=${countryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`
100
- const hydroFill = `${this.config.entsoeDomain}/api?documentType=A72&processType=A16&in_Domain=${countryCode}&periodStart=${hydroPeriodStart}&periodEnd=${hydroPeriodEnd}`
117
+ const generation = `${this.config.entsoeDomain}/api?documentType=A75&processType=A16&in_Domain=${countryCode}&outBiddingZone_Domain=${countryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`;
118
+ const price = `${this.config.entsoeDomain}/api?documentType=A44&in_Domain=${priceCountryCode}&out_Domain=${priceCountryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`;
119
+ const congestion = `${this.config.entsoeDomain}/api?documentType=A92&in_Domain=${priceCountryCode}&out_Domain=${priceCountryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`;
120
+ const consumption = `${this.config.entsoeDomain}/api?documentType=A65&processType=A16&in_Domain=${countryCode}&outBiddingZone_Domain=${countryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`;
121
+ const hydroFill = `${this.config.entsoeDomain}/api?documentType=A72&processType=A16&in_Domain=${countryCode}&periodStart=${hydroPeriodStart}&periodEnd=${hydroPeriodEnd}`;
101
122
  //const crossBorder = `${this.config.entsoeDomain}/api?documentType=A88&acquiring_Domain=10YCZ-CEPS-----N&connecting_Domain=10YSK-SEPS-----K&periodStart=${periodStart}&periodEnd=${periodEnd}`
102
123
  const urls = {
103
124
  generation,
104
125
  price,
105
126
  consumption,
106
- hydroFill
107
- }
127
+ hydroFill,
128
+ congestion,
129
+ };
108
130
  const filteredUrls = this.filterURLs(urls, wanted);
109
131
  return filteredUrls;
110
132
  }
111
133
 
112
134
  async loadInstalled(countryCode: string, queryOptions?: QueryOptions) {
113
- const currentYear = (new Date()).getFullYear() ;
114
- const urls = []
135
+ const currentYear = new Date().getFullYear();
136
+ const urls = [];
115
137
  for (let year = 2015; year <= currentYear; year++) {
116
- const periodStart = `${year -2}12302200`
117
- const periodEnd = `${year }01020400`
138
+ const periodStart = `${year - 1}12302200`;
139
+ const periodEnd = `${year}01020400`;
118
140
  const installed = `${this.config.entsoeDomain}/api?documentType=A68&processType=A33&in_Domain=${countryCode}&periodStart=${periodStart}&periodEnd=${periodEnd}`;
119
- urls.push(installed)
141
+ urls.push(installed);
120
142
  }
121
- if(queryOptions?.format===Format.SOURCES){
143
+ if (queryOptions?.format === Format.SOURCES) {
122
144
  return urls;
123
145
  }
124
- const allJsons = await this.fetchAndMakeJson(Object.values(urls), queryOptions?.reload);
146
+ const allJsons = await this.fetchAndMakeJson(
147
+ Object.values(urls),
148
+ queryOptions?.reload
149
+ );
125
150
  return allJsons;
126
151
  }
127
152
  async fetchAndMakeJson(urls: string[], reload?: boolean): Promise<Entsoe[]> {
128
153
  const promises: Promise<Entsoe>[] = [];
129
- urls.forEach(url => {
130
- promises.push(this.fetchAndCache(url, reload))
131
- })
154
+ urls.forEach((url) => {
155
+ promises.push(this.fetchAndCache(url, reload));
156
+ });
132
157
  return await Promise.all(promises);
133
158
  /*
134
159
  const settled = await Promise.allSettled(promises);
@@ -138,11 +163,11 @@ export class EntsoeLoader {
138
163
  }
139
164
 
140
165
  filterEmpty(all: Data) {
141
- Object.keys(all).forEach(name => {
142
- if (!all[name].some(item => item !== 0)) {
166
+ Object.keys(all).forEach((name) => {
167
+ if (!all[name].some((item) => item !== 0)) {
143
168
  delete all[name];
144
169
  }
145
- })
170
+ });
146
171
  }
147
172
 
148
173
  async fetchAndCache(url: string, reload?: boolean): Promise<Entsoe> {
@@ -160,26 +185,27 @@ export class EntsoeLoader {
160
185
  return JSON.parse(jsonString) as Entsoe;
161
186
  } catch (e) {
162
187
  */
163
- const urlWithToken = `${url}&securityToken=${this.config.securityToken}`
164
- const response = await fetch(urlWithToken);
165
- if (response.status !== 200) {
166
- return Promise.reject(new Error(`${response.status} ${response.statusText}`));
167
-
168
- }
169
- const data = await response.text();
170
- const json = await parseStringPromise(data) as Entsoe;
171
- /*
188
+ const urlWithToken = `${url}&securityToken=${this.config.securityToken}`;
189
+ const response = await fetch(urlWithToken);
190
+ if (response.status !== 200) {
191
+ return Promise.reject(
192
+ new Error(`${response.status} ${response.statusText}`)
193
+ );
194
+ }
195
+ const data = await response.text();
196
+ const json = (await parseStringPromise(data)) as Entsoe;
197
+ /*
172
198
  if (filename) {
173
199
  //writeFile(filename, JSON.stringify(json), 'utf-8')
174
200
  }
175
201
  */
176
- return json;
202
+ return json;
177
203
  //}
178
204
  }
179
205
  filterURLs(obj: KV, keys: string[]) {
180
206
  return keys.reduce((acc: KV, key: string) => {
181
207
  acc[key] = obj[key];
182
208
  return acc;
183
- }, {})
209
+ }, {});
184
210
  }
185
- }
211
+ }
@@ -0,0 +1,98 @@
1
+ import { readFileSync } from "fs"
2
+ import { join } from "path"
3
+ import { Country } from "./Loader"
4
+ import { Format, QueryOptions } from "../interfaces/queryoptions"
5
+
6
+ export class Temperature {
7
+ countries: Country[] = []
8
+
9
+ // Capital city coordinates for all European countries
10
+ private readonly coordinates: { [key: string]: { lat: number; lon: number } } = {
11
+ AT: { lat: 48.2082, lon: 16.3738 }, // Vienna, Austria
12
+ BE: { lat: 50.8503, lon: 4.3517 }, // Brussels, Belgium
13
+ BA: { lat: 43.8564, lon: 18.4131 }, // Sarajevo, Bosnia and Herzegovina
14
+ HR: { lat: 45.8150, lon: 15.9819 }, // Zagreb, Croatia
15
+ BG: { lat: 42.6977, lon: 23.3219 }, // Sofia, Bulgaria
16
+ CY: { lat: 35.1856, lon: 33.3823 }, // Nicosia, Cyprus
17
+ CZ: { lat: 50.0755, lon: 14.4378 }, // Prague, Czech Republic
18
+ DK: { lat: 55.6761, lon: 12.5683 }, // Copenhagen, Denmark
19
+ EE: { lat: 59.4370, lon: 24.7536 }, // Tallinn, Estonia
20
+ FI: { lat: 60.1699, lon: 24.9384 }, // Helsinki, Finland
21
+ FR: { lat: 48.8566, lon: 2.3522 }, // Paris, France
22
+ DE: { lat: 52.5200, lon: 13.4050 }, // Berlin, Germany
23
+ GR: { lat: 37.9838, lon: 23.7275 }, // Athens, Greece
24
+ HU: { lat: 47.4979, lon: 19.0402 }, // Budapest, Hungary
25
+ IE: { lat: 53.3498, lon: -6.2603 }, // Dublin, Ireland
26
+ IT: { lat: 41.9028, lon: 12.4964 }, // Rome, Italy
27
+ LV: { lat: 56.9496, lon: 24.1052 }, // Riga, Latvia
28
+ LT: { lat: 54.6872, lon: 25.2797 }, // Vilnius, Lithuania
29
+ LU: { lat: 49.6116, lon: 6.1319 }, // Luxembourg City, Luxembourg
30
+ ME: { lat: 42.4304, lon: 19.2594 }, // Podgorica, Montenegro
31
+ GB: { lat: 51.5074, lon: -0.1278 }, // London, United Kingdom
32
+ NL: { lat: 52.3676, lon: 4.9041 }, // Amsterdam, Netherlands
33
+ MK: { lat: 41.9973, lon: 21.4280 }, // Skopje, North Macedonia
34
+ NO: { lat: 59.9139, lon: 10.7522 }, // Oslo, Norway
35
+ PL: { lat: 52.2297, lon: 21.0122 }, // Warsaw, Poland
36
+ PT: { lat: 38.7223, lon: -9.1393 }, // Lisbon, Portugal
37
+ RO: { lat: 44.4268, lon: 26.1025 }, // Bucharest, Romania
38
+ RS: { lat: 44.7866, lon: 20.4489 }, // Belgrade, Serbia
39
+ SK: { lat: 48.1486, lon: 17.1077 }, // Bratislava, Slovakia
40
+ SI: { lat: 46.0569, lon: 14.5058 }, // Ljubljana, Slovenia
41
+ ES: { lat: 40.4168, lon: -3.7038 }, // Madrid, Spain
42
+ SE: { lat: 59.3293, lon: 18.0686 }, // Stockholm, Sweden
43
+ CH: { lat: 46.9480, lon: 7.4474 }, // Bern, Switzerland
44
+ }
45
+
46
+ constructor() {
47
+ const countryPath = join(__dirname, '..', 'config/countries.json')
48
+ const countriesString = readFileSync(countryPath, 'utf-8')
49
+ this.countries = JSON.parse(countriesString)
50
+ }
51
+
52
+ async load(countryCode: string, queryOptions: QueryOptions) {
53
+ const countryShort = this.countries.find(
54
+ (item) => item.code === countryCode
55
+ )?.short
56
+
57
+ if (!countryShort || !this.coordinates[countryShort]) {
58
+ throw new Error(`Country ${countryCode} not supported for temperature data`)
59
+ }
60
+
61
+ const { lat, lon } = this.coordinates[countryShort]
62
+
63
+ // Calculate date range for the month
64
+ //const startDate = new Date(year, month - 1, 1)
65
+ //const endDate = new Date(year, month, 0)
66
+ const startDate = new Date('2015-01-01');
67
+ const endDate = new Date();
68
+ const startDateStr = startDate.toISOString().split('T')[0]
69
+ const endDateStr = endDate.toISOString().split('T')[0]
70
+
71
+ // Open-Meteo API - free historical weather data
72
+ const url = `https://archive-api.open-meteo.com/v1/archive?latitude=${lat}&longitude=${lon}&start_date=${startDateStr}&end_date=${endDateStr}&hourly=temperature_2m&timezone=auto`
73
+ console.log(url)
74
+
75
+ if (queryOptions.format === Format.SOURCES) {
76
+ return [url]
77
+ }
78
+
79
+ const response = await fetch(url)
80
+ const json = await response.json()
81
+ const r = this.convert(json)
82
+ return r
83
+ }
84
+
85
+ convert(x: any) {
86
+ const timeStrings = x?.hourly?.time || []
87
+ const temperatures = x?.hourly?.temperature_2m || []
88
+
89
+ const time = timeStrings.map((item: string) => {
90
+ return new Date(item).getTime()
91
+ })
92
+
93
+ return {
94
+ time: time,
95
+ temperature: temperatures
96
+ }
97
+ }
98
+ }