import React, { useState, useEffect } from 'react'; import { Text, Box } from 'ink'; import fetch from 'node-fetch'; const locations = { Nancy: { latitude: 48.6921, longitude: 6.1844 }, Paris: { latitude: 48.8566, longitude: 2.3522 } }; const INTER_ID = 6684; // SC Internacional team id const PARIS_ID = 1045; // Paris FC team id const TASKTROVE_API = 'https://tasktrove.couraud.xyz/api/v1/tasks'; const TASKTROVE_TOKEN = 'eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJuYW1lIjoidGFza3Ryb3ZlIiwiaXNzIjoibWVtb3MiLCJzdWIiOiIzIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImlhdCI6MTc1OTk5MjI0N30.666jJ97j9a3d8c3a2a2a2a2a2a2a2a2a2a2a2a2a2a2'; // From your memo API const weatherCodeMap = { 0: { desc: 'Ciel clair', color: 'yellow' }, 1: { desc: 'Principalement clair', color: 'yellow' }, 2: { desc: 'Partiellement nuageux', color: 'cyan' }, 3: { desc: 'Couvert', color: 'gray' }, 45: { desc: 'Brouillard', color: 'gray' }, 48: { desc: 'Brouillard givrant', color: 'gray' }, 51: { desc: 'Bruine légère', color: 'blue' }, 53: { desc: 'Bruine modérée', color: 'blue' }, 55: { desc: 'Bruine dense', color: 'blue' }, 56: { desc: 'Bruine verglaçante légère', color: 'blue' }, 57: { desc: 'Bruine verglaçante dense', color: 'blue' }, 61: { desc: 'Pluie faible', color: 'blue' }, 63: { desc: 'Pluie modérée', color: 'blue' }, 65: { desc: 'Pluie forte', color: 'blue' }, 66: { desc: 'Pluie verglaçante légère', color: 'blue' }, 67: { desc: 'Pluie verglaçante forte', color: 'blue' }, 71: { desc: 'Chute de neige légère', color: 'white' }, 73: { desc: 'Chute de neige modérée', color: 'white' }, 75: { desc: 'Chute de neige forte', color: 'white' }, 77: { desc: 'Grains de neige', color: 'white' }, 80: { desc: 'Averses de pluie faibles', color: 'blue' }, 81: { desc: 'Averses de pluie modérées', color: 'blue' }, 82: { desc: 'Averses de pluie violentes', color: 'blue' }, 85: { desc: 'Averses de neige faibles', color: 'white' }, 86: { desc: 'Averses de neige fortes', color: 'white' }, 95: { desc: 'Orage', color: 'magenta' }, 96: { desc: 'Orage avec faible grêle', color: 'magenta' }, 99: { desc: 'Orage avec forte grêle', color: 'magenta' } }; function formatTime(date) { return date.toLocaleTimeString('fr-FR', { hour12: false }); } function formatDate(date) { return date.toLocaleDateString('fr-FR', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); } function formatDateTimeUTC(utcString) { const date = new Date(utcString); return { date: formatDate(date), time: formatTime(date) }; } async function fetchWeather(lat, lon) { const url = `https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t_weather=true&temperature_unit=celsius&timezone=Europe/Paris`; const response = await fetch(url); const data = await response.json(); return data.current_weather; } async function fetchScheduledMatches(teamId) { const url = `https://api.football-data.org/v4/teams/${teamId}/matches?status=SCHEDULED`; try { const response = await fetch(url, { headers: { "X-Auth-Token": "1535f68086e542528841b5e276f50b45" } }); const data = await response.json(); if (!data.matches) return []; return data.matches.map(match => { let opponent, homeAway; if (match.homeTeam.id === teamId) { opponent = match.awayTeam.name; homeAway = 'Home'; } else if (match.awayTeam.id === teamId) { opponent = match.homeTeam.name; homeAway = 'Away'; } else { return null; } const { date, time } = formatDateTimeUTC(match.utcDate); return { date, time, opponent, homeAway }; }).filter(Boolean); } catch { return []; } } function translateHomeAway(homeAway) { if (homeAway === 'Home') return 'domicile'; if (homeAway === 'Away') return 'extérieur'; return homeAway; } function renderWeather(location, weather) { if (!weather) return {location}: Loading weather...; const weatherInfo = weatherCodeMap[weather.weathercode] || { desc: 'Unknown', color: 'white' }; return ( {location}: {weatherInfo.desc},{' '} {weather.temperature}°C (Vent : {weather.windspeed} km/h) ); } async function fetchDeviceInfo() { const url = 'http://192.168.0.19:19837/device-info'; const response = await fetch(url); if (!response.ok) throw new Error('Erreur HTTP ' + response.status); return response.json(); } const LoadingBar = ({ percentage }) => { const width = 30; const filledWidth = Math.round((percentage / 100) * width); const emptyWidth = width - filledWidth; return ( [ {'='.repeat(filledWidth)} {' '.repeat(emptyWidth)} ] {percentage}% ); }; function renderDeviceInfo(deviceInfo, deviceError) { if (deviceError) { return {deviceError}; } if (!deviceInfo) { return Chargement des informations...; } const percentage = deviceInfo.percentage; if (percentage == null) { return Status : {deviceInfo.status || 'N/A'}; } else { return ( Temps restant : {deviceInfo.remainingTime || 'N/A'} Status : {deviceInfo.status || 'N/A'} ); } } function formatMemoContent(markdownContent) { if (!markdownContent) return ""; return markdownContent.split('\n').map(line => { const taskItemMatch = line.match(/^-\s*\[( |x|X)\]\s*/); if (taskItemMatch) { return '- ' + line.slice(taskItemMatch[0].length); } return line; }).join('\n'); } async function fetchLatestMemo() { const url = "https://memos.couraud.xyz/api/v1/memos?page=1&perPage=1"; try { const response = await fetch(url, { headers: { Authorization: "Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6InYxIiwidHlwIjoiSldUIn0.eyJuYW1lIjoiZ3JvY2VyaWVzIiwiaXNzIjoibWVtb3MiLCJzdWIiOiIyIiwiYXVkIjpbInVzZXIuYWNjZXNzLXRva2VuIl0sImlhdCI6MTc2MDI3NzQwMX0.H8m6LSaav7cuiQgt_rrzB7Fx4UM7Un11M2S0L5JJfPc" } }); if (!response.ok) { throw new Error(`HTTP error ${response.status}`); } const json = await response.json(); const memosArray = json.memos; if (memosArray && memosArray.length > 0) { return formatMemoContent(memosArray[0].content); } return "Aucune note trouvée."; } catch (error) { return `Erreur lors de la récupération de la note: ${error.message}`; } } // New function to fetch and render TaskTrove tasks async function fetchTaskTroveTasks() { try { const response = await fetch(TASKTROVE_API, { headers: { Authorization: `Bearer ${TASKTROVE_TOKEN}` } }); if (!response.ok) return []; const data = await response.json(); const tasks = data.tasks || []; const today = new Date().toISOString().split('T')[0]; return tasks .filter(task => { if (task.completed) return false; const isDueToday = task.dueDate === today; const isP1 = task.priority === 1; const isP2 = task.priority === 2; return isDueToday || isP1 || isP2; }) .map(task => ({ title: task.title, priority: task.priority, dueDate: task.dueDate })); } catch (err) { return []; } } function renderTaskTroveList(tasks) { if (!tasks) { return Chargement des tâches...; } if (tasks.length === 0) { return Aucune tâche à afficher.; } return ( {tasks.map((task, index) => { const color = task.priority === 1 ? 'red' : task.priority === 2 ? 'yellow' : 'white'; return ( - {task.title} ); })} ); } export default function App({ name = 'Mathias' }) { const [now, setNow] = useState(new Date()); const [weatherNancy, setWeatherNancy] = useState(null); const [weatherParis, setWeatherParis] = useState(null); const [matchesInter, setMatchesInter] = useState(null); const [matchesParis, setMatchesParis] = useState(null); const [deviceInfo, setDeviceInfo] = useState(null); const [deviceError, setDeviceError] = useState(null); const [latestMemo, setLatestMemo] = useState("Chargement de la dernière note..."); const [taskTroveTasks, setTaskTroveTasks] = useState(null); useEffect(() => { const timer = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(timer); }, []); async function updateWeatherData() { try { const [nancy, paris] = await Promise.all([ fetchWeather(locations.Nancy.latitude, locations.Nancy.longitude), fetchWeather(locations.Paris.latitude, locations.Paris.longitude) ]); setWeatherNancy(nancy); setWeatherParis(paris); } catch (err) {} } async function updateMatchesData() { try { const [interMatches, parisMatches] = await Promise.all([ fetchScheduledMatches(INTER_ID), fetchScheduledMatches(PARIS_ID) ]); setMatchesInter(interMatches); setMatchesParis(parisMatches); } catch (err) {} } async function updateDeviceInfo() { try { const data = await fetchDeviceInfo(); setDeviceInfo(data); setDeviceError(null); } catch (err) { setDeviceError("Impossible de récupérer les informations du device"); setDeviceInfo(null); } } async function updateLatestMemo() { const memo = await fetchLatestMemo(); setLatestMemo(memo); } async function updateTaskTroveTasks() { const tasks = await fetchTaskTroveTasks(); setTaskTroveTasks(tasks); } useEffect(() => { updateWeatherData(); updateMatchesData(); updateDeviceInfo(); updateLatestMemo(); updateTaskTroveTasks(); const weatherInterval = setInterval(updateWeatherData, 60000); const deviceInterval = setInterval(updateDeviceInfo, 60000); const memoInterval = setInterval(updateLatestMemo, 60000); const taskTroveInterval = setInterval(updateTaskTroveTasks, 60000); return () => { clearInterval(weatherInterval); clearInterval(deviceInterval); clearInterval(memoInterval); clearInterval(taskTroveInterval); }; }, []); return ( Hello, {name} ! {formatDate(now)} {formatTime(now)} {renderWeather('Nancy', weatherNancy)} {renderWeather('Paris', weatherParis)} Matches à venir de SC Internacional : {!matchesInter && Chargement des matchs...} {matchesInter && matchesInter.length === 0 && Aucun match trouvé.} {matchesInter && matchesInter.slice(0, 2).map((m, i) => ( {m.date} {m.time} - SC Internacional vs {m.opponent} ({translateHomeAway(m.homeAway)}) ))} Matches à venir de Paris FC : {!matchesParis && Chargement des matchs...} {matchesParis && matchesParis.length === 0 && Aucun match trouvé.} {matchesParis && matchesParis.slice(0, 2).map((m, i) => ( {m.date} {m.time} - Paris FC vs {m.opponent} ({translateHomeAway(m.homeAway)}) ))} Informations du device : {renderDeviceInfo(deviceInfo, deviceError)} Liste de courses {latestMemo.split('\n').map((line, index) => ( {line} ))} To-Do List {renderTaskTroveList(taskTroveTasks)} Press Ctrl+C to exit. ); }