Compare commits

..

No commits in common. "3c43576b57f92d2d15d20ccab1c147baaf52eb03" and "305055936db43650f5efe4558c63dd9a05b2df7c" have entirely different histories.

11 changed files with 56 additions and 102 deletions

View File

@ -1,7 +1,5 @@
# Infoscreen # Infoscreen
[![Read the blog entry at c0ntroller.de](https://c0ntroller.de/img/read-blog.svg)](https://c0ntroller.de/blog/project/infoscreen)
This is a project for personal use. An infoscreen that shows: This is a project for personal use. An infoscreen that shows:
- time - time
- weather & weather radar - weather & weather radar

View File

@ -26,11 +26,10 @@ interface EventList {
} }
const Calendar = ({ secrets }: { secrets: SecretsCalendar }) => { const Calendar = ({ secrets }: { secrets: SecretsCalendar }) => {
const [calendarToken, setToken] = React.useState("") const [token, setToken] = React.useState("")
const [events, setEvents] = React.useState<EventList>({}) const [events, setEvents] = React.useState<EventList>({})
const mergeEvents = (currentList: EventList, toAdd: Event[], ownerIdx: number = 0) => { const mergeEvents = (currentList: EventList, toAdd: Event[], ownerIdx: number = 0) => {
if (!toAdd || toAdd.length === 0) return;
for(const event of toAdd) { for(const event of toAdd) {
const startDate = new Date(event.start.date ? event.start.date : event.start.dateTime) const startDate = new Date(event.start.date ? event.start.date : event.start.dateTime)
const startDateString = startDate.toDateString() const startDateString = startDate.toDateString()
@ -104,6 +103,45 @@ const Calendar = ({ secrets }: { secrets: SecretsCalendar }) => {
} }
} }
/*let lastDate = "";
let i = 0;
for (const event of events) {
const startDate = new Date(event.start.date ? event.start.date : event.start.dateTime)
const startDateString = `${startDate.getFullYear()}${startDate.getMonth()}${startDate.getDate()}`
if (startDateString !== lastDate) {
lastDate = startDateString;
const dayDiff = daysDifference(new Date(), startDate);
let dayDiffString: string;
switch (dayDiff) {
case 0: dayDiffString = "Heute"
break;
case 1: dayDiffString = "Morgen"
break;
default: dayDiffString = `${dayDiff} Tage`;
break;
}
eventTable.push(
<tr key={startDateString}><td colSpan={2} className={styles.calendarDateHeader}>
{dowToString(startDate.getDay())}, {startDate.getDate()}. {startDate.getMonth() + 1}
<span className={styles.calendarDateHeaderSub}>({dayDiffString})</span>
</td></tr>
);
}
if (event.start.date) {
eventTable.push(
<tr key={++i} className={styles.calendarEntry}><td colSpan={2}>{event.summary}</td></tr>
);
} else {
eventTable.push(
<tr key={++i} className={styles.calendarEntry}>
<td>{event.summary}</td>
<td className={styles.entryTime}>{startDate.getHours()}:{startDate.getMinutes().toString().padStart(2, "0")}</td>
</tr>
)
}
}*/
return eventTable; return eventTable;
} }
@ -122,8 +160,8 @@ const Calendar = ({ secrets }: { secrets: SecretsCalendar }) => {
return token.access_token; return token.access_token;
} }
const pullCalendar = async (calendarToken: string, calendarIndex: number = 0) => { const pullCalendar = async (provToken?: string, calendarIndex: number = 0) => {
const correctToken = calendarToken; const correctToken = provToken || token;
if (!correctToken || correctToken === "") return; if (!correctToken || correctToken === "") return;
const timeMin = new Date(); const timeMin = new Date();
@ -145,7 +183,7 @@ const Calendar = ({ secrets }: { secrets: SecretsCalendar }) => {
//setEvents([...processEventData(events.items)]); //setEvents([...processEventData(events.items)]);
} }
const pullAll = async (provToken: string) => { const pullAll = async (provToken?: string) => {
const eventList: EventList = {} const eventList: EventList = {}
for(let i = 0; i < secrets.calendarIds.length; i++) { for(let i = 0; i < secrets.calendarIds.length; i++) {
const events = await pullCalendar(provToken, i); const events = await pullCalendar(provToken, i);
@ -156,13 +194,8 @@ const Calendar = ({ secrets }: { secrets: SecretsCalendar }) => {
React.useEffect(() => { React.useEffect(() => {
requestToken().then(pullAll) requestToken().then(pullAll)
const calendarInterval = setInterval(() => { const calendarInterval = setInterval(pullAll, CALENDAR_REFRESH_INTERVAL);
setToken(token => { const calendarTokenInterval = setInterval(requestToken, CALENDAR_TOKEN_REFRESH_INTERVAL);
pullAll(token);
return token;
})
}, CALENDAR_REFRESH_INTERVAL);
const calendarTokenInterval = setInterval(requestToken.bind(this), CALENDAR_TOKEN_REFRESH_INTERVAL);
return () => { return () => {
clearInterval(calendarInterval); clearInterval(calendarInterval);

View File

@ -54,7 +54,7 @@ const DVB = ({ stopId }: { stopId: number }) => {
React.useEffect(() => { React.useEffect(() => {
pullDepartures(); pullDepartures();
const dvbInterval = setInterval(pullDepartures.bind(this), DVB_REFRESH_INTERVAL); const dvbInterval = setInterval(pullDepartures, DVB_REFRESH_INTERVAL);
return () => clearInterval(dvbInterval); return () => clearInterval(dvbInterval);
}, []) }, [])

View File

@ -1,60 +0,0 @@
import * as React from "react"
import { HAssStates } from "../lib/interfaces";
import * as styles from "../styles/containers/HomeAssistant.module.css";
const HASS_REFRESH_INTERVAL = 5 * 60 * 1000;
const HomeAssistant = ({ hassUrl, token }: { hassUrl: string, token: string }) => {
const [states, setStates] = React.useState<HAssStates>({
daniel: false,
vicki: false,
nextbikes: 0
});
const fetchState = async (entityId: string) => {
const response = await fetch(`${hassUrl}/api/states/${entityId}`, {
method: "GET",
headers: {
"Authorization": `Bearer ${token}`,
"Content-Type": "application/json"
}
});
return await response.json();
}
const pullStates = async () => {
const daniel = await fetchState("person.daniel")
const vicki = await fetchState("person.vicki")
const nextbikes = await fetchState("sensor.nextbikes")
const location = (l: string|undefined) => { switch (l) {
case "home": return "Zuhause";
case "not_home": return "Unterwegs";
case undefined:
case "unknown": return "Unbekannt";
default: return l;
}
}
setStates({
daniel: location(daniel?.state),
vicki: location(vicki?.state),
nextbikes: nextbikes?.state
});
}
React.useEffect(() => {
pullStates()
const statesInterval = setInterval(pullStates.bind(this), HASS_REFRESH_INTERVAL);
return () => clearInterval(statesInterval);
}, [])
return <div className={`container ${styles.container}`}>
<span>Daniel: {states.daniel}</span>
<span>Vicki: {states.vicki}</span>
<span>NextBikes: {states.nextbikes}</span>
</div>
}
export default HomeAssistant;

View File

@ -15,7 +15,7 @@ const News = () => {
for (const n of news) { for (const n of news) {
if (!n.title || n.title === "") continue; if (!n.title || n.title === "") continue;
const updated = new Date(n["dc:date"]); const updated = new Date(n.updated);
newsTable.push( newsTable.push(
<tr key={++i}> <tr key={++i}>
<td>{n.title}</td> <td>{n.title}</td>
@ -31,7 +31,7 @@ const News = () => {
const updated = new Date(n.pubDate); const updated = new Date(n.pubDate);
newsTable.splice(randTablePos, 0, newsTable.splice(randTablePos, 0,
<tr key={n.title}> <tr key={n.title}>
<td>{n.title.replace(/&amp;/g, "&").replace(/&quot;/g, "\"")}</td> <td>{n.title}</td>
<td>{updated.getHours()}:{updated.getMinutes().toString().padStart(2, "0")}</td> <td>{updated.getHours()}:{updated.getMinutes().toString().padStart(2, "0")}</td>
</tr> </tr>
) )
@ -43,7 +43,7 @@ const News = () => {
const pullNews = async () => { const pullNews = async () => {
const xml = new XMLParser(); const xml = new XMLParser();
const response = await fetch("https://www.tagesschau.de/xml/atom/"); const response = await fetch("https://www.tagesschau.de/xml/atom/");
const feed: { title: string; "dc:date": string; }[] = xml.parse(await response.text()).feed.entry; const feed: { title: string; updated: string; }[] = xml.parse(await response.text()).feed.entry;
// Feedburner does not allow cors but at least we get JSON // Feedburner does not allow cors but at least we get JSON
const postResponse = await fetch("https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FrkEL"); const postResponse = await fetch("https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Ffeeds.feedburner.com%2Fblogspot%2FrkEL");
@ -54,7 +54,7 @@ const News = () => {
React.useEffect(() => { React.useEffect(() => {
pullNews() pullNews()
const newsInterval = setInterval(pullNews.bind(this), NEWS_REFRESH_INTERVAL); const newsInterval = setInterval(pullNews, NEWS_REFRESH_INTERVAL);
return () => clearInterval(newsInterval); return () => clearInterval(newsInterval);
}, []) }, [])

View File

@ -29,7 +29,7 @@ const PlantState = ({ hassUrl, token, plants }: { hassUrl: string, token: string
React.useEffect(() => { React.useEffect(() => {
pullPlants() pullPlants()
const plantInterval = setInterval(pullPlants.bind(this), PLANT_REFRESH_INTERVAL); const plantInterval = setInterval(pullPlants, PLANT_REFRESH_INTERVAL);
return () => clearInterval(plantInterval); return () => clearInterval(plantInterval);
}, []) }, [])

View File

@ -67,7 +67,7 @@ const WeatherAndTime = ({ secrets }: { secrets: SecretsWeather }) => {
}, 1000); }, 1000);
pullWeather() pullWeather()
const weatherInterval = setInterval(pullWeather.bind(this), WEATHER_REFRESH_INTERVAL); const weatherInterval = setInterval(pullWeather, WEATHER_REFRESH_INTERVAL);
return () => { return () => {
clearInterval(dateInterval); clearInterval(dateInterval);

View File

@ -41,7 +41,7 @@ export interface Event {
export interface News { export interface News {
title: string; title: string;
"dc:date": string; updated: string;
} }
export interface PostillonNews { export interface PostillonNews {
@ -73,12 +73,6 @@ export interface PlantState {
} }
} }
export interface HAssStates {
daniel: string|number|boolean;
vicki: string|number|boolean;
nextbikes: string|number|boolean;
}
export type SongInfo = { export type SongInfo = {
playbackState: "PLAYING" | "PAUSE" | "STOPPED"; playbackState: "PLAYING" | "PAUSE" | "STOPPED";
title?: string; title?: string;

View File

@ -7,7 +7,6 @@ import Spotify from "../components/Spotify";
import PlantState from "../components/PlantState"; import PlantState from "../components/PlantState";
import WeatherAndTimeContainer from "../components/WeatherAndTime" import WeatherAndTimeContainer from "../components/WeatherAndTime"
import WeatherRadar from "../components/WeatherRadar"; import WeatherRadar from "../components/WeatherRadar";
import HomeAssistant from "../components/HAssOverview";
function importAll(r) { function importAll(r) {
return r.keys().map(r); return r.keys().map(r);
@ -41,7 +40,7 @@ const IndexPage = () => {
<WeatherRadar /> <WeatherRadar />
<News /> <News />
<DVB stopId={secrets.dvb.stopId} /> <DVB stopId={secrets.dvb.stopId} />
<Spotify mqtt={secrets.mqtt} Alternative={<HomeAssistant hassUrl={secrets.hass.url} token={secrets.hass.token} />} /> <Spotify mqtt={secrets.mqtt} Alternative={<PlantState hassUrl={secrets.hass.url} token={secrets.hass.token} plants={["Basilikum", "Chili"]} />} />
</main>) </main>)
} }

View File

@ -37,9 +37,9 @@
} }
.color0 { .color0 {
background: rgba(185, 43, 255, 0.2); background: rgba(255, 170, 0, 0.2);
} }
.color1 { .color1 {
background: rgba(0, 93, 255, 0.2); background: rgba(255, 0, 0, 0.2);
} }

View File

@ -1,10 +0,0 @@
.container {
display: flex;
flex-direction: column;
justify-content: space-evenly;
height: 100%;
font-size: 2em;
font-weight: bold;
line-height: 1.5;
padding: 20px;
}