turkiyem 1.2.0 → 1.4.0
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.
- package/README.md +27 -0
- package/package.json +5 -2
- package/src/commands/hava.js +58 -0
- package/src/index.js +27 -0
- package/src/services/weatherService.js +274 -0
- package/src/utils/banner.js +3 -0
- package/src/utils/cache.js +3 -0
- package/src/utils/display.js +63 -0
package/README.md
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
Turkiye Toplu Tasima ve Deprem CLI araci.
|
|
4
4
|
|
|
5
5
|
AFAD deprem verileri, EGO (Ankara) otobus saatleri ve IETT (Istanbul) hat/saat bilgilerini terminalden sorgulayabilirsiniz.
|
|
6
|
+
Open-Meteo ile API key gerektirmeden guncel hava, saatlik tahmin ve hava kalitesi sorgulayabilirsiniz.
|
|
6
7
|
|
|
7
8
|
## Kurulum
|
|
8
9
|
|
|
@@ -81,6 +82,30 @@ turkiyem deprem buyukluk 4.0
|
|
|
81
82
|
|
|
82
83
|
Buyuklugu 4.0 ve ustu olan depremler kirmizi ile vurgulanir.
|
|
83
84
|
|
|
85
|
+
### Hava Durumu ve Hava Kalitesi
|
|
86
|
+
|
|
87
|
+
Open-Meteo uzerinden API key gerektirmeden sorgu yapar:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Secili sehir icin guncel hava
|
|
91
|
+
turkiyem hava guncel
|
|
92
|
+
|
|
93
|
+
# Sehir bazli guncel hava
|
|
94
|
+
turkiyem hava guncel istanbul
|
|
95
|
+
|
|
96
|
+
# Koordinat bazli guncel hava
|
|
97
|
+
turkiyem hava guncel 41.0082,28.9784
|
|
98
|
+
|
|
99
|
+
# Saatlik tahmin (varsayilan 2 gun)
|
|
100
|
+
turkiyem hava saatlik istanbul
|
|
101
|
+
|
|
102
|
+
# Saatlik tahmin gun sayisi (1-7)
|
|
103
|
+
turkiyem hava saatlik istanbul --gun 3
|
|
104
|
+
|
|
105
|
+
# Hava kalitesi
|
|
106
|
+
turkiyem hava kalite ankara
|
|
107
|
+
```
|
|
108
|
+
|
|
84
109
|
### Temizleme
|
|
85
110
|
|
|
86
111
|
Cache ve yapilandirmayi sifirlar:
|
|
@@ -107,6 +132,8 @@ Secili sehir `~/.turkiyem/config.json` dosyasinda saklanir. Bu dosya otomatik ol
|
|
|
107
132
|
| EGO | Ankara otobus sefer saatleri (ego.gov.tr) |
|
|
108
133
|
| IETT | Istanbul GTFS hat verileri (data.ibb.gov.tr) |
|
|
109
134
|
| IETT SOAP | Planlanan sefer saatleri (api.ibb.gov.tr) |
|
|
135
|
+
| Open-Meteo | Guncel hava ve saatlik tahmin (api.open-meteo.com) |
|
|
136
|
+
| Open-Meteo AQ | Hava kalitesi (air-quality-api.open-meteo.com) |
|
|
110
137
|
|
|
111
138
|
## npm Publish
|
|
112
139
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "turkiyem",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Türkiye Toplu Taşıma ve Deprem CLI aracı - AFAD deprem verileri, EGO hat saatleri ve IETT SOAP/GTFS bilgileri",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -21,7 +21,10 @@
|
|
|
21
21
|
"cli",
|
|
22
22
|
"earthquake",
|
|
23
23
|
"transit",
|
|
24
|
-
"soap"
|
|
24
|
+
"soap",
|
|
25
|
+
"weather",
|
|
26
|
+
"open-meteo",
|
|
27
|
+
"air-quality"
|
|
25
28
|
],
|
|
26
29
|
"author": "",
|
|
27
30
|
"license": "MIT",
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import { fetchAirQuality, fetchCurrentWeather, fetchHourlyForecast } from '../services/weatherService.js';
|
|
4
|
+
import {
|
|
5
|
+
createAirQualityTable,
|
|
6
|
+
createCurrentWeatherTable,
|
|
7
|
+
createHourlyWeatherTable,
|
|
8
|
+
} from '../utils/display.js';
|
|
9
|
+
|
|
10
|
+
function formatLocationHint(city) {
|
|
11
|
+
if (city) return city;
|
|
12
|
+
return 'seçili şehir';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function havaGuncel(city) {
|
|
16
|
+
const spinner = ora(`Güncel hava verisi alınıyor (${formatLocationHint(city)})...`).start();
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const result = await fetchCurrentWeather(city);
|
|
20
|
+
spinner.succeed('Güncel hava verisi alındı');
|
|
21
|
+
console.log('');
|
|
22
|
+
console.log(createCurrentWeatherTable(result));
|
|
23
|
+
} catch (err) {
|
|
24
|
+
spinner.fail(chalk.red(err.message));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function havaSaatlik(city, days) {
|
|
29
|
+
const parsedDays = Number.parseInt(days, 10);
|
|
30
|
+
if (!Number.isNaN(parsedDays) && (parsedDays < 1 || parsedDays > 7)) {
|
|
31
|
+
console.log(chalk.red('Gün sayısı 1 ile 7 arasında olmalıdır.'));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const spinner = ora(`Saatlik tahmin verisi alınıyor (${formatLocationHint(city)})...`).start();
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const result = await fetchHourlyForecast(city, parsedDays || 2);
|
|
39
|
+
spinner.succeed(`Saatlik tahmin verisi alındı (${result.forecastDays} gün)`);
|
|
40
|
+
console.log('');
|
|
41
|
+
console.log(createHourlyWeatherTable(result));
|
|
42
|
+
} catch (err) {
|
|
43
|
+
spinner.fail(chalk.red(err.message));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function havaKalitesi(city) {
|
|
48
|
+
const spinner = ora(`Hava kalitesi verisi alınıyor (${formatLocationHint(city)})...`).start();
|
|
49
|
+
|
|
50
|
+
try {
|
|
51
|
+
const result = await fetchAirQuality(city);
|
|
52
|
+
spinner.succeed('Hava kalitesi verisi alındı');
|
|
53
|
+
console.log('');
|
|
54
|
+
console.log(createAirQualityTable(result));
|
|
55
|
+
} catch (err) {
|
|
56
|
+
spinner.fail(chalk.red(err.message));
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import { printBanner, printHelp } from './utils/banner.js';
|
|
|
7
7
|
import { sehirSec } from './commands/sehir.js';
|
|
8
8
|
import { hatSorgula } from './commands/hat.js';
|
|
9
9
|
import { depremSon24, deprem7Gun, depremBuyukluk } from './commands/deprem.js';
|
|
10
|
+
import { havaGuncel, havaKalitesi, havaSaatlik } from './commands/hava.js';
|
|
10
11
|
import { temizle } from './commands/temizle.js';
|
|
11
12
|
|
|
12
13
|
const require = createRequire(import.meta.url);
|
|
@@ -70,6 +71,32 @@ depremCmd
|
|
|
70
71
|
await depremBuyukluk(deger);
|
|
71
72
|
});
|
|
72
73
|
|
|
74
|
+
const havaCmd = program
|
|
75
|
+
.command('hava')
|
|
76
|
+
.description('Hava durumu ve hava kalitesi sorgula');
|
|
77
|
+
|
|
78
|
+
havaCmd
|
|
79
|
+
.command('guncel [sehirVeyaKoordinat]')
|
|
80
|
+
.description('Güncel sıcaklık, rüzgar ve nem bilgisi')
|
|
81
|
+
.action(async (sehirVeyaKoordinat) => {
|
|
82
|
+
await havaGuncel(sehirVeyaKoordinat);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
havaCmd
|
|
86
|
+
.command('saatlik [sehirVeyaKoordinat]')
|
|
87
|
+
.description('Saatlik hava tahmini (varsayılan 2 gün)')
|
|
88
|
+
.option('-g, --gun <gun>', 'Tahmin gün sayısı (1-7)', '2')
|
|
89
|
+
.action(async (sehirVeyaKoordinat, options) => {
|
|
90
|
+
await havaSaatlik(sehirVeyaKoordinat, options.gun);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
havaCmd
|
|
94
|
+
.command('kalite [sehirVeyaKoordinat]')
|
|
95
|
+
.description('Güncel hava kalitesi (PM10, PM2.5, CO, NO2)')
|
|
96
|
+
.action(async (sehirVeyaKoordinat) => {
|
|
97
|
+
await havaKalitesi(sehirVeyaKoordinat);
|
|
98
|
+
});
|
|
99
|
+
|
|
73
100
|
program
|
|
74
101
|
.command('temizle')
|
|
75
102
|
.description('Cache ve yapılandırmayı temizle')
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { CACHE_TTL, getCached, setCached } from '../utils/cache.js';
|
|
3
|
+
import { getCity } from '../utils/config.js';
|
|
4
|
+
|
|
5
|
+
const FORECAST_URL = 'https://api.open-meteo.com/v1/forecast';
|
|
6
|
+
const AIR_QUALITY_URL = 'https://air-quality-api.open-meteo.com/v1/air-quality';
|
|
7
|
+
const GEOCODING_URL = 'https://geocoding-api.open-meteo.com/v1/search';
|
|
8
|
+
|
|
9
|
+
const REQUEST_TIMEOUT = 15000;
|
|
10
|
+
|
|
11
|
+
function isCoordinateInput(value) {
|
|
12
|
+
return /^-?\d+(\.\d+)?,\s*-?\d+(\.\d+)?$/.test(value);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function parseCoordinates(value) {
|
|
16
|
+
const [latRaw, lonRaw] = value.split(',');
|
|
17
|
+
const latitude = Number.parseFloat(latRaw.trim());
|
|
18
|
+
const longitude = Number.parseFloat(lonRaw.trim());
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
Number.isNaN(latitude)
|
|
22
|
+
|| Number.isNaN(longitude)
|
|
23
|
+
|| latitude < -90
|
|
24
|
+
|| latitude > 90
|
|
25
|
+
|| longitude < -180
|
|
26
|
+
|| longitude > 180
|
|
27
|
+
) {
|
|
28
|
+
throw new Error('Koordinatlar geçersiz. Örnek: 41.0082,28.9784');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return { latitude, longitude };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function fetchGeocodeByName(cityName) {
|
|
35
|
+
const cacheKey = `geo_${cityName.toLowerCase()}`;
|
|
36
|
+
const cached = getCached(cacheKey);
|
|
37
|
+
if (cached) return cached;
|
|
38
|
+
|
|
39
|
+
const response = await axios.get(GEOCODING_URL, {
|
|
40
|
+
params: {
|
|
41
|
+
name: cityName,
|
|
42
|
+
count: 1,
|
|
43
|
+
language: 'tr',
|
|
44
|
+
format: 'json',
|
|
45
|
+
},
|
|
46
|
+
timeout: REQUEST_TIMEOUT,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (response.status !== 200 || !response.data) {
|
|
50
|
+
throw new Error('Open-Meteo geocoding yanıtı alınamadı.');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const first = response.data.results?.[0];
|
|
54
|
+
if (!first) {
|
|
55
|
+
throw new Error(`"${cityName}" için konum bulunamadı.`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const normalized = {
|
|
59
|
+
name: [first.name, first.admin1, first.country].filter(Boolean).join(', '),
|
|
60
|
+
latitude: first.latitude,
|
|
61
|
+
longitude: first.longitude,
|
|
62
|
+
timezone: first.timezone || 'auto',
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
setCached(cacheKey, normalized, CACHE_TTL.DEFAULT);
|
|
66
|
+
return normalized;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export async function resolveLocation(inputCity) {
|
|
70
|
+
const raw = (inputCity || getCity() || '').trim();
|
|
71
|
+
if (!raw) {
|
|
72
|
+
throw new Error('Şehir belirtilmedi. Örnek: turkiyem hava guncel istanbul');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (isCoordinateInput(raw)) {
|
|
76
|
+
const coords = parseCoordinates(raw);
|
|
77
|
+
return {
|
|
78
|
+
name: `${coords.latitude},${coords.longitude}`,
|
|
79
|
+
latitude: coords.latitude,
|
|
80
|
+
longitude: coords.longitude,
|
|
81
|
+
timezone: 'auto',
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return fetchGeocodeByName(raw);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function buildHourlyRows(hourly, limitCount) {
|
|
89
|
+
const times = hourly.time || [];
|
|
90
|
+
const temperatures = hourly.temperature_2m || [];
|
|
91
|
+
const apparent = hourly.apparent_temperature || [];
|
|
92
|
+
const precipitation = hourly.precipitation_probability || [];
|
|
93
|
+
|
|
94
|
+
const size = Math.min(times.length, temperatures.length, apparent.length, precipitation.length, limitCount);
|
|
95
|
+
const rows = [];
|
|
96
|
+
|
|
97
|
+
for (let i = 0; i < size; i += 1) {
|
|
98
|
+
rows.push({
|
|
99
|
+
time: times[i],
|
|
100
|
+
temperature: temperatures[i],
|
|
101
|
+
apparentTemperature: apparent[i],
|
|
102
|
+
precipitationProbability: precipitation[i],
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return rows;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function findClosestIndex(timeValues) {
|
|
110
|
+
if (!Array.isArray(timeValues) || timeValues.length === 0) return -1;
|
|
111
|
+
|
|
112
|
+
const nowTs = Date.now();
|
|
113
|
+
let bestIndex = 0;
|
|
114
|
+
let bestDelta = Number.POSITIVE_INFINITY;
|
|
115
|
+
|
|
116
|
+
for (let i = 0; i < timeValues.length; i += 1) {
|
|
117
|
+
const ts = new Date(timeValues[i]).getTime();
|
|
118
|
+
if (Number.isNaN(ts)) continue;
|
|
119
|
+
const delta = Math.abs(nowTs - ts);
|
|
120
|
+
if (delta < bestDelta) {
|
|
121
|
+
bestDelta = delta;
|
|
122
|
+
bestIndex = i;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return bestIndex;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function normalizeAxiosError(err, sourceName) {
|
|
130
|
+
if (err?.code === 'ECONNABORTED') {
|
|
131
|
+
throw new Error(`${sourceName} isteği zaman aşımına uğradı.`);
|
|
132
|
+
}
|
|
133
|
+
if (err?.code === 'ENOTFOUND' || err?.code === 'ECONNREFUSED') {
|
|
134
|
+
throw new Error(`${sourceName} sunucusuna bağlanılamadı.`);
|
|
135
|
+
}
|
|
136
|
+
if (err?.response?.status) {
|
|
137
|
+
throw new Error(`${sourceName} HTTP ${err.response.status} hatası döndürdü.`);
|
|
138
|
+
}
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function fetchCurrentWeather(inputCity) {
|
|
143
|
+
const location = await resolveLocation(inputCity);
|
|
144
|
+
const cacheKey = `weather_current_${location.latitude}_${location.longitude}`;
|
|
145
|
+
const cached = getCached(cacheKey);
|
|
146
|
+
if (cached) return cached;
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const response = await axios.get(FORECAST_URL, {
|
|
150
|
+
params: {
|
|
151
|
+
latitude: location.latitude,
|
|
152
|
+
longitude: location.longitude,
|
|
153
|
+
current: 'temperature_2m,wind_speed_10m,relative_humidity_2m',
|
|
154
|
+
timezone: location.timezone || 'auto',
|
|
155
|
+
},
|
|
156
|
+
timeout: REQUEST_TIMEOUT,
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
if (response.status !== 200 || !response.data?.current) {
|
|
160
|
+
throw new Error('Güncel hava verisi alınamadı.');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const result = {
|
|
164
|
+
locationName: location.name,
|
|
165
|
+
latitude: location.latitude,
|
|
166
|
+
longitude: location.longitude,
|
|
167
|
+
timezone: response.data.timezone || location.timezone || 'auto',
|
|
168
|
+
current: {
|
|
169
|
+
time: response.data.current.time,
|
|
170
|
+
temperature: response.data.current.temperature_2m,
|
|
171
|
+
windSpeed: response.data.current.wind_speed_10m,
|
|
172
|
+
humidity: response.data.current.relative_humidity_2m,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
setCached(cacheKey, result, CACHE_TTL.WEATHER_CURRENT);
|
|
177
|
+
return result;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
normalizeAxiosError(err, 'Open-Meteo');
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export async function fetchHourlyForecast(inputCity, forecastDays = 2) {
|
|
184
|
+
const location = await resolveLocation(inputCity);
|
|
185
|
+
const safeDays = Number.isFinite(forecastDays)
|
|
186
|
+
? Math.min(Math.max(Number.parseInt(forecastDays, 10), 1), 7)
|
|
187
|
+
: 2;
|
|
188
|
+
|
|
189
|
+
const cacheKey = `weather_hourly_${location.latitude}_${location.longitude}_${safeDays}`;
|
|
190
|
+
const cached = getCached(cacheKey);
|
|
191
|
+
if (cached) return cached;
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
const response = await axios.get(FORECAST_URL, {
|
|
195
|
+
params: {
|
|
196
|
+
latitude: location.latitude,
|
|
197
|
+
longitude: location.longitude,
|
|
198
|
+
hourly: 'temperature_2m,apparent_temperature,precipitation_probability',
|
|
199
|
+
forecast_days: safeDays,
|
|
200
|
+
timezone: location.timezone || 'auto',
|
|
201
|
+
},
|
|
202
|
+
timeout: REQUEST_TIMEOUT,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
if (response.status !== 200 || !response.data?.hourly) {
|
|
206
|
+
throw new Error('Saatlik tahmin verisi alınamadı.');
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const limitCount = Math.min(safeDays * 24, 48);
|
|
210
|
+
const rows = buildHourlyRows(response.data.hourly, limitCount);
|
|
211
|
+
|
|
212
|
+
const result = {
|
|
213
|
+
locationName: location.name,
|
|
214
|
+
latitude: location.latitude,
|
|
215
|
+
longitude: location.longitude,
|
|
216
|
+
timezone: response.data.timezone || location.timezone || 'auto',
|
|
217
|
+
rows,
|
|
218
|
+
forecastDays: safeDays,
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
setCached(cacheKey, result, CACHE_TTL.WEATHER_HOURLY);
|
|
222
|
+
return result;
|
|
223
|
+
} catch (err) {
|
|
224
|
+
normalizeAxiosError(err, 'Open-Meteo');
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function fetchAirQuality(inputCity) {
|
|
229
|
+
const location = await resolveLocation(inputCity);
|
|
230
|
+
const cacheKey = `weather_air_${location.latitude}_${location.longitude}`;
|
|
231
|
+
const cached = getCached(cacheKey);
|
|
232
|
+
if (cached) return cached;
|
|
233
|
+
|
|
234
|
+
try {
|
|
235
|
+
const response = await axios.get(AIR_QUALITY_URL, {
|
|
236
|
+
params: {
|
|
237
|
+
latitude: location.latitude,
|
|
238
|
+
longitude: location.longitude,
|
|
239
|
+
hourly: 'pm10,pm2_5,carbon_monoxide,nitrogen_dioxide',
|
|
240
|
+
timezone: 'auto',
|
|
241
|
+
},
|
|
242
|
+
timeout: REQUEST_TIMEOUT,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const hourly = response.data?.hourly;
|
|
246
|
+
if (response.status !== 200 || !hourly) {
|
|
247
|
+
throw new Error('Hava kalitesi verisi alınamadı.');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const idx = findClosestIndex(hourly.time);
|
|
251
|
+
if (idx < 0) {
|
|
252
|
+
throw new Error('Hava kalitesi saat verisi çözümlenemedi.');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const result = {
|
|
256
|
+
locationName: location.name,
|
|
257
|
+
latitude: location.latitude,
|
|
258
|
+
longitude: location.longitude,
|
|
259
|
+
timezone: response.data.timezone || 'auto',
|
|
260
|
+
current: {
|
|
261
|
+
time: hourly.time?.[idx] ?? '-',
|
|
262
|
+
pm10: hourly.pm10?.[idx] ?? null,
|
|
263
|
+
pm25: hourly.pm2_5?.[idx] ?? null,
|
|
264
|
+
carbonMonoxide: hourly.carbon_monoxide?.[idx] ?? null,
|
|
265
|
+
nitrogenDioxide: hourly.nitrogen_dioxide?.[idx] ?? null,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
setCached(cacheKey, result, CACHE_TTL.WEATHER_AIR);
|
|
270
|
+
return result;
|
|
271
|
+
} catch (err) {
|
|
272
|
+
normalizeAxiosError(err, 'Open-Meteo Air Quality');
|
|
273
|
+
}
|
|
274
|
+
}
|
package/src/utils/banner.js
CHANGED
|
@@ -22,6 +22,9 @@ export function printHelp() {
|
|
|
22
22
|
console.log(chalk.white.bold(' Komutlar:\n'));
|
|
23
23
|
console.log(chalk.cyan(' turkiyem sehir <ankara|istanbul>') + chalk.gray(' Şehir seç'));
|
|
24
24
|
console.log(chalk.cyan(' turkiyem hat <numara>') + chalk.gray(' Hat sorgula'));
|
|
25
|
+
console.log(chalk.cyan(' turkiyem hava guncel [sehir|lat,lon]') + chalk.gray(' Güncel hava'));
|
|
26
|
+
console.log(chalk.cyan(' turkiyem hava saatlik [sehir|lat,lon] -g 2') + chalk.gray(' Saatlik tahmin'));
|
|
27
|
+
console.log(chalk.cyan(' turkiyem hava kalite [sehir|lat,lon]') + chalk.gray(' Hava kalitesi'));
|
|
25
28
|
console.log(chalk.cyan(' turkiyem deprem son24') + chalk.gray(' Son 24 saat depremler'));
|
|
26
29
|
console.log(chalk.cyan(' turkiyem deprem 7gun') + chalk.gray(' Son 7 gün depremler'));
|
|
27
30
|
console.log(chalk.cyan(' turkiyem deprem buyukluk <deger>') + chalk.gray(' Büyüklüğe göre filtrele'));
|
package/src/utils/cache.js
CHANGED
|
@@ -3,6 +3,9 @@ import NodeCache from 'node-cache';
|
|
|
3
3
|
export const CACHE_TTL = Object.freeze({
|
|
4
4
|
DEFAULT: 300,
|
|
5
5
|
IETT_SOAP: 90,
|
|
6
|
+
WEATHER_CURRENT: 300,
|
|
7
|
+
WEATHER_HOURLY: 600,
|
|
8
|
+
WEATHER_AIR: 600,
|
|
6
9
|
});
|
|
7
10
|
|
|
8
11
|
const cache = new NodeCache({ stdTTL: CACHE_TTL.DEFAULT, checkperiod: 60 });
|
package/src/utils/display.js
CHANGED
|
@@ -135,3 +135,66 @@ export function createIettPlannedTimesTable(plannedTimes) {
|
|
|
135
135
|
|
|
136
136
|
return table.toString();
|
|
137
137
|
}
|
|
138
|
+
|
|
139
|
+
export function createCurrentWeatherTable(result) {
|
|
140
|
+
const table = new Table({
|
|
141
|
+
style: { head: [], border: ['gray'] },
|
|
142
|
+
wordWrap: true,
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
table.push(
|
|
146
|
+
{ [chalk.cyan('Konum')]: result.locationName || '-' },
|
|
147
|
+
{ [chalk.cyan('Koordinat')]: `${result.latitude}, ${result.longitude}` },
|
|
148
|
+
{ [chalk.cyan('Zaman Dilimi')]: result.timezone || '-' },
|
|
149
|
+
{ [chalk.cyan('Ölçüm Zamanı')]: result.current?.time || '-' },
|
|
150
|
+
{ [chalk.cyan('Sıcaklık (°C)')]: String(result.current?.temperature ?? '-') },
|
|
151
|
+
{ [chalk.cyan('Rüzgar (km/s)')]: String(result.current?.windSpeed ?? '-') },
|
|
152
|
+
{ [chalk.cyan('Nem (%)')]: String(result.current?.humidity ?? '-') },
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
return table.toString();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function createHourlyWeatherTable(result) {
|
|
159
|
+
const table = new Table({
|
|
160
|
+
head: [
|
|
161
|
+
chalk.white.bold('Saat'),
|
|
162
|
+
chalk.white.bold('Sıcaklık (°C)'),
|
|
163
|
+
chalk.white.bold('Hissedilen (°C)'),
|
|
164
|
+
chalk.white.bold('Yağış Olasılığı (%)'),
|
|
165
|
+
],
|
|
166
|
+
colWidths: [22, 15, 18, 22],
|
|
167
|
+
style: { head: [], border: ['gray'] },
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
for (const row of result.rows || []) {
|
|
171
|
+
table.push([
|
|
172
|
+
row.time || '-',
|
|
173
|
+
String(row.temperature ?? '-'),
|
|
174
|
+
String(row.apparentTemperature ?? '-'),
|
|
175
|
+
String(row.precipitationProbability ?? '-'),
|
|
176
|
+
]);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return table.toString();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function createAirQualityTable(result) {
|
|
183
|
+
const table = new Table({
|
|
184
|
+
style: { head: [], border: ['gray'] },
|
|
185
|
+
wordWrap: true,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
table.push(
|
|
189
|
+
{ [chalk.cyan('Konum')]: result.locationName || '-' },
|
|
190
|
+
{ [chalk.cyan('Koordinat')]: `${result.latitude}, ${result.longitude}` },
|
|
191
|
+
{ [chalk.cyan('Zaman Dilimi')]: result.timezone || '-' },
|
|
192
|
+
{ [chalk.cyan('Ölçüm Zamanı')]: result.current?.time || '-' },
|
|
193
|
+
{ [chalk.cyan('PM10 (µg/m3)')]: String(result.current?.pm10 ?? '-') },
|
|
194
|
+
{ [chalk.cyan('PM2.5 (µg/m3)')]: String(result.current?.pm25 ?? '-') },
|
|
195
|
+
{ [chalk.cyan('CO (µg/m3)')]: String(result.current?.carbonMonoxide ?? '-') },
|
|
196
|
+
{ [chalk.cyan('NO2 (µg/m3)')]: String(result.current?.nitrogenDioxide ?? '-') },
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
return table.toString();
|
|
200
|
+
}
|