Display bus departures generated by script; get weather temperature from cloud

This commit is contained in:
rasta5man 2025-08-07 16:13:43 +02:00
parent 857ba29540
commit f88501078f
2 changed files with 126 additions and 60 deletions

View file

@ -5,11 +5,21 @@ import "./App.css";
import { TChildren, TRssItem, TRssRawItem, TDepartures } from "./types"; import { TChildren, TRssItem, TRssRawItem, TDepartures } from "./types";
import { convertXML } from "simple-xml-to-json"; import { convertXML } from "simple-xml-to-json";
const MAIN_URL = "http://10.0.0.30:3000/";
const MAIN_HEADER_FONT_SIZE = 50; const MAIN_HEADER_FONT_SIZE = 50;
const DATA_FONT_SIZE = 42; const DATA_FONT_SIZE = 42;
const TEMPERATURE_URL = "http://10.0.0.106:3000/gettemperature"; const TEMPERATURE_URL = `${MAIN_URL}gettemperature`;
const DEPARTURES_URL = "http://10.0.0.106:3000/getdepartures"; const DEPARTURES_URL = `${MAIN_URL}getdepartures`;
const GET_RSSFEED_FROM_FILE_URL = `${MAIN_URL}get_rssfeed_data`;
const SET_RSSFEED_TO_FILE_URL = `${MAIN_URL}set_rssfeed_data`;
const SOKOLOV_RSS_URL = "https://www.sokolov.cz/rss/"; const SOKOLOV_RSS_URL = "https://www.sokolov.cz/rss/";
const NUMBER_OF_RSSFEED_REQUESTS_BEFORE_LOADING_IT_FROM_FILE = 9;
let rssfeedRequestSuccessful = false;
let getRssFeedAttempts = 0;
const REPEAT_NORMAL_RSSFEED_REQUEST_TIME = 2; // After how long should I get RSS feed? In hours.
const REPEAT_RSS_REQUEST_TIME_AFTER_FAIL = 30 * 1000; //20 seconds
const DAYS_OF_WEEK = [ const DAYS_OF_WEEK = [
"Neděle", "Neděle",
"Pondělí", "Pondělí",
@ -19,8 +29,10 @@ const DAYS_OF_WEEK = [
"Pátek", "Pátek",
"Sobota", "Sobota",
]; ];
const INACTIVITY_LIMIT = 300000; const INACTIVITY_LIMIT = 300000;
const OPTIONS = { hour12: false }; const OPTIONS = { hour12: false };
// colors in sokolov website // colors in sokolov website
const LOGO_COLOR = "#82c55b"; // green const LOGO_COLOR = "#82c55b"; // green
const FONT_COLOR = "#00367b"; // blue const FONT_COLOR = "#00367b"; // blue
@ -43,10 +55,10 @@ function onInactive(ms: number, cb: () => void) {
window.onkeydown = window.onkeydown =
window.onkeyup = window.onkeyup =
window.onfocus = window.onfocus =
function () { function() {
clearTimeout(wait); clearTimeout(wait);
wait = setTimeout(cb, ms); wait = setTimeout(cb, ms);
}; };
} }
function App() { function App() {
@ -57,7 +69,7 @@ function App() {
var [chosenArticle, setChosenArticle] = useState({ clicked: false, url: "" }); var [chosenArticle, setChosenArticle] = useState({ clicked: false, url: "" });
var [feedData, setFeedData] = useState<TRssItem[]>([ var [feedData, setFeedData] = useState<TRssItem[]>([
{ {
title: "Chyba stažení dat, kontaktujte prosím administrátora", title: "",
description: "", description: "",
link: "", link: "",
pubDate: new Date(), pubDate: new Date(),
@ -67,7 +79,7 @@ function App() {
// check, how long is user inactive, if reading an article. If more than 5 minutes, we load default screen // check, how long is user inactive, if reading an article. If more than 5 minutes, we load default screen
function idle() { function idle() {
onInactive(INACTIVITY_LIMIT, function () { onInactive(INACTIVITY_LIMIT, function() {
window.onmousemove = window.onmousemove =
window.onmousedown = window.onmousedown =
window.onmousemove = window.onmousemove =
@ -78,7 +90,7 @@ function App() {
window.onkeydown = window.onkeydown =
window.onkeyup = window.onkeyup =
window.onfocus = window.onfocus =
null; null;
setChosenArticle({ clicked: false, url: "" }); setChosenArticle({ clicked: false, url: "" });
}); });
} }
@ -114,14 +126,14 @@ function App() {
"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Origin": "*",
}, },
}) })
.then((response) => { .then(response => {
let t = parseFloat(response.data); let t = parseFloat(response.data);
//console.log(t); //console.log(t);
if (isNaN(t)) return; if (isNaN(t)) return;
setTemperature(t); setTemperature(t);
}) })
.catch((error) => { .catch(error => {
console.log(error); console.log(error);
}); });
} }
@ -132,11 +144,11 @@ function App() {
"Access-Control-Allow-Origin": "*", "Access-Control-Allow-Origin": "*",
}, },
}) })
.then((response) => { .then(response => {
const departures: TDepartures = response.data; const departures: TDepartures = response.data;
setDepartures(departures); setDepartures(departures);
}) })
.catch((error) => { .catch(error => {
console.log(error); console.log(error);
}); });
} }
@ -156,7 +168,7 @@ function App() {
setDayOfWeek(DAYS_OF_WEEK[date.getDay()]); setDayOfWeek(DAYS_OF_WEEK[date.getDay()]);
msToMidnight = milisecondsToMidnight(); msToMidnight = milisecondsToMidnight();
console.log("setDay called, ms to midnight", msToMidnight); //console.log("setDay called, ms to midnight", msToMidnight);
updateDayOfWeek = setInterval(getDay, msToMidnight); updateDayOfWeek = setInterval(getDay, msToMidnight);
} }
@ -177,13 +189,49 @@ function App() {
}; };
}, []); }, []);
function getOrSetRssFeedWithFile(rssFeedData: TRssItem[]) {
if (rssFeedData.length === 0) {
axios(GET_RSSFEED_FROM_FILE_URL, {
headers: {
"Access-Control-Allow-Origin": "*",
},
})
.then(response => {
let rssFeedData: TRssItem[] = response.data;
console.log("rssfeed data loaded from file: ", rssFeedData);
rssFeedData = rssFeedData.map(item => {
return { ...item, pubDate: new Date(item.pubDate) };
})
setFeedData(rssFeedData);
})
.catch(error => {
console.log(error);
});
} else {
axios(SET_RSSFEED_TO_FILE_URL, {
method: "post",
headers: {
"Access-Control-Allow-Origin": "*",
},
data: rssFeedData
})
.then(response => {
console.log("set rssfeed data response: ", response);
})
.catch(error => {
console.log(error);
});
}
}
// rss feed to get articles from sokolov website // rss feed to get articles from sokolov website
const getRssFeed = () => { const getRssFeed = () => {
//console.log('volam get feed');
axios(SOKOLOV_RSS_URL) axios(SOKOLOV_RSS_URL)
.then((response) => { .then(response => {
const myJson = convertXML(response?.data); const myJson = convertXML(response?.data);
console.log("--------", JSON.stringify(myJson)); //console.log("--------", JSON.stringify(myJson));
let data: TChildren = myJson?.rss?.children[0]?.channel?.children; let data: TChildren = myJson?.rss?.children[0]?.channel?.children;
if (!Array.isArray(data)) return; if (!Array.isArray(data)) return;
@ -204,35 +252,36 @@ function App() {
}; };
}); });
console.log(result); rssfeedRequestSuccessful = true;
//console.log("rssfeed obtained");
//console.log(result);
//getOrSetRssFeedWithFile(result);
setFeedData(result); setFeedData(result);
}) })
.catch((e) => console.log(e)); .catch(_ => {
//getRssFeedAttempts++;
//if (!rssfeedRequestSuccessful && (getRssFeedAttempts > NUMBER_OF_RSSFEED_REQUESTS_BEFORE_LOADING_IT_FROM_FILE && getRssFeedAttempts < NUMBER_OF_RSSFEED_REQUESTS_BEFORE_LOADING_IT_FROM_FILE + 2)) {
// getOrSetRssFeedWithFile([]);
//}
if (!rssfeedRequestSuccessful) {
//console.log('Error obtaining rssfeed, repeating ...');
setTimeout(getRssFeed, REPEAT_RSS_REQUEST_TIME_AFTER_FAIL);
}
});
}; };
useEffect(() => { useEffect(() => {
getRssFeed(); getRssFeed();
var feed = setInterval(getRssFeed, 6 * 60 * 60 * 1000); //raz za 6 hodin // ODKOMENTUJ NASLEDUJUCE RIADKY, AK CHCES, ABY SA RSS FEEDY NACITAVALI CASTEJSIE
var feed = setInterval(getRssFeed, REPEAT_NORMAL_RSSFEED_REQUEST_TIME * 60 * 60 * 1000); //raz za REPEAT_NORMAL_RSSFEED_REQUEST_TIME hodin
return function cleanup() { return function cleanup() {
clearInterval(feed); clearInterval(feed);
}; };
}, []); }, []);
const buildFeedView = (): React.JSX.Element[] => { const buildFeedView = (): React.JSX.Element[] => {
if ( if (feedData[0].title === "") {
feedData[0].title === return [];
"Chyba stažení dat, kontaktujte prosím administrátora"
) {
return [
<Box sx={{ padding: 2 }}>
<Typography sx={{ color: LOGO_COLOR }} variant="h6">
{feedData[0].title}
</Typography>
<Typography sx={{ color: FONT_COLOR }} variant="subtitle2">
{feedData[0].pubDate.toLocaleDateString("cs-Cz")}
</Typography>
</Box>,
];
} }
return feedData.map((item, index) => { return feedData.map((item, index) => {
@ -259,6 +308,7 @@ function App() {
); );
}); });
}; };
sokolovFeed = buildFeedView(); sokolovFeed = buildFeedView();
return ( return (
@ -297,7 +347,7 @@ function App() {
<td align="left">SMĚR</td> <td align="left">SMĚR</td>
<td align="center">PŘÍJEZD</td> <td align="center">PŘÍJEZD</td>
</tr> </tr>
{departures.map(function (item, i) { {departures.map(function(item, i) {
return ( return (
<tr key={i} style={{ fontSize: DATA_FONT_SIZE }}> <tr key={i} style={{ fontSize: DATA_FONT_SIZE }}>
<td align="center">{item[0]}</td> <td align="center">{item[0]}</td>
@ -357,6 +407,15 @@ const departures = [
["3", "08:10", "Závodu míru", ["X", "32"]], ["3", "08:10", "Závodu míru", ["X", "32"]],
]; ];
// ziskavaju sa requestom na web sokolova
let departures_new = [
["6", "07:23", "Sídl.Michal škola", "X2"],
["3", "07:27", "Sídliště Michal", "X2"],
["2", "07:31", "Sídliště Michal", "X1"],
["3", "07:34", "Závodu míru", "X3"],
["4", "07:34", "Závodu míru", "X1"],
]
// pubDate is date object // pubDate is date object
const dummyData = [ const dummyData = [
{ {

59
src/types.d.ts vendored
View file

@ -6,43 +6,50 @@ export type TRssItem = {
link: string; link: string;
pubDate: Date; pubDate: Date;
guid: string; guid: string;
}; };
export type TContent = { content: string };
export type TRssRawItem = { export type TContent = { content: string };
export type TRssRawItem = {
item: { item: {
children: [ children: [
{ title: TContent }, { title: TContent },
{ link: TContent }, { link: TContent },
{ description: TContent }, { description: TContent },
{ pubDate: { content: Date } }, { pubDate: { content: Date } },
{ guid: TContent } { guid: TContent }
]; ];
}; };
}; };
export type TChildren = { export type TChildren = {
children: [ children: [
|{ | {
title: TContent; title: TContent;
} }
| { | {
link: TContent; link: TContent;
} }
| { | {
description: TContent; description: TContent;
} }
| { | {
ttl: TContent; ttl: TContent;
} }
| { | {
lastBuildDate: TContent; lastBuildDate: TContent;
} }
| { | {
item: RssRawItem; item: RssRawItem;
} }
]; ];
}; };
export type TDepartures = array<string, string, string, string[]>[]; export type TDepartures = array<number, string, string, string>[];
export type TSlackData = {
msg: string
bot_name: string
bot_icon: string
channel: string
};