diff --git a/WebDisplay/package.json b/WebDisplay/package.json index 7971fa8..873745a 100644 --- a/WebDisplay/package.json +++ b/WebDisplay/package.json @@ -17,6 +17,7 @@ "axios": "^1.7.9", "core-js": "^3.39.0", "date-fns": "^3.6.0", + "home-assistant-js-websocket": "^9.4.0", "pinia": "^2.3.0", "roboto-fontface": "^0.10.0", "suncalc": "^1.9.0", @@ -45,7 +46,6 @@ "vite": "^5.4.11", "vite-plugin-vuetify": "^2.0.4", "vue-router": "^4.5.0", - "vue-tsc": "^2.1.10", - "websocketstream-polyfill": "^1.0.1" + "vue-tsc": "^2.1.10" } } diff --git a/WebDisplay/pnpm-lock.yaml b/WebDisplay/pnpm-lock.yaml index 60650fc..dc7a3fa 100644 --- a/WebDisplay/pnpm-lock.yaml +++ b/WebDisplay/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: date-fns: specifier: ^3.6.0 version: 3.6.0 + home-assistant-js-websocket: + specifier: ^9.4.0 + version: 9.4.0 pinia: specifier: ^2.3.0 version: 2.3.0(typescript@5.6.2)(vue@3.5.13(typescript@5.6.2)) @@ -114,9 +117,6 @@ importers: vue-tsc: specifier: ^2.1.10 version: 2.1.10(typescript@5.6.2) - websocketstream-polyfill: - specifier: ^1.0.1 - version: 1.0.1 packages: @@ -1310,6 +1310,9 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true + home-assistant-js-websocket@9.4.0: + resolution: {integrity: sha512-312TuI63IfKf8G+iWvKmPYIdxWMNojwVk03o9OSpQFFDjSCNAYdCUfuPCFs73SuJ1Xpd4D1Eo11CB33MGMqZ+Q==} + ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} @@ -2044,9 +2047,6 @@ packages: webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - websocketstream-polyfill@1.0.1: - resolution: {integrity: sha512-SV0AG8yN3YDwCVCex/oBQli7vrezGQ0stgo5suKMTthjPQv2wuMsNvwUWDAvNmDGGb9VIm5WsW99x57GW06gLQ==} - whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} @@ -3406,6 +3406,8 @@ snapshots: he@1.2.0: {} + home-assistant-js-websocket@9.4.0: {} + ignore@5.3.2: {} immutable@5.0.3: {} @@ -4167,8 +4169,6 @@ snapshots: webpack-virtual-modules@0.6.2: {} - websocketstream-polyfill@1.0.1: {} - whatwg-url@5.0.0: dependencies: tr46: 0.0.3 diff --git a/WebDisplay/src/models/calendar/calendar-day.ts b/WebDisplay/src/models/calendar/calendar-day.ts new file mode 100644 index 0000000..3a6580f --- /dev/null +++ b/WebDisplay/src/models/calendar/calendar-day.ts @@ -0,0 +1,11 @@ +import CalendarEntry from './calendar-entry'; + +export default class CalendarDay { + date: Date; + entries: CalendarEntry[]; + + constructor(date: Date, entries: CalendarEntry[]) { + this.date = date; + this.entries = entries; + } +} diff --git a/WebDisplay/src/models/calendar/calendar-entry.ts b/WebDisplay/src/models/calendar/calendar-entry.ts new file mode 100644 index 0000000..4648f80 --- /dev/null +++ b/WebDisplay/src/models/calendar/calendar-entry.ts @@ -0,0 +1,6 @@ +export default interface CalendarEntry { + summary: string; + isAllDay: boolean; + start: Date; + end: Date; +} diff --git a/WebDisplay/src/models/home-assistant/home-assistant.ts b/WebDisplay/src/models/home-assistant/home-assistant.ts deleted file mode 100644 index 079e7a8..0000000 --- a/WebDisplay/src/models/home-assistant/home-assistant.ts +++ /dev/null @@ -1,64 +0,0 @@ -let messageId: number = 1; - -export enum MessageType { - auth_invalid = 'auth_invalid', - auth_ok = 'auth_ok', - auth_required = 'auth_required', - event = 'event', - pong = 'pong', - result = 'result' -} - -export interface OutgoingMessage { - id: number | undefined; - type: string; -} - -export interface IncomingMessage { - id: number | undefined; - type: string; - success: boolean | undefined; - result: any; -} - -export class AuthMessage implements OutgoingMessage { - id = undefined; - type: string = 'auth'; - access_token: string | null = null; - - constructor(access_token: string) { - this.access_token = access_token; - } -} - -export class SubscribeEntitiesMessage implements OutgoingMessage { - id = messageId++; - type = 'subscribe_entities'; - entity_ids: string[] = []; - - constructor(entity_ids: string[]) { - this.entity_ids = entity_ids; - } -} - -export interface EventMessage extends IncomingMessage { - event: StatesUpdates; -} - -export interface EntityState { - /** state */ - s: string; - /** attributes */ - a: { [key: string]: any }; - /** last_changed; if set, also applies to lu */ - lc: number; - /** last_updated */ - lu: number; -} - -export interface StatesUpdates { - /** add */ - a?: Record; - /** remove */ - r?: string[]; // remove -} diff --git a/WebDisplay/src/pages/kiosk.vue b/WebDisplay/src/pages/kiosk.vue index 351cf2b..2be5788 100644 --- a/WebDisplay/src/pages/kiosk.vue +++ b/WebDisplay/src/pages/kiosk.vue @@ -4,7 +4,13 @@ import { useLaundryStore } from '@/stores/laundryStore'; import { usePowerStore } from '@/stores/powerStore'; import { useHomeAssistantStore } from '@/stores/homeAssistantStore'; - import Environment from '@/environment'; + import { useCalendarStore } from '@/stores/calendarStore'; + import { format, startOfDay, endOfDay } from 'date-fns'; + import CalendarDay from '@/models/calendar/calendar-day'; + + const calendarDayCount = 7; + + const calendarReady = ref(false); const weatherStore = useWeatherStore(); weatherStore.start(); @@ -18,12 +24,61 @@ const homeAssistantStore = useHomeAssistantStore(); homeAssistantStore.start(); + const calendarStore = useCalendarStore(); + const currentTime = ref(new Date()); + const calendarDays = ref([] as CalendarDay[]); const timeFormatter = new Intl.DateTimeFormat('en-US', { hour: 'numeric', minute: '2-digit' }); const dateFormatter = new Intl.DateTimeFormat('en-US', { weekday: 'long', month: 'long', day: 'numeric' }); - setInterval(() => (currentTime.value = new Date()), 1000); + function alarmState(state: string): string { + switch (state) { + case 'armed_home': + return 'Armed'; + case 'armed_away': + return 'Armed'; + case 'disarmed': + return 'Disarmed'; + default: + return 'Unknown'; + } + } + + function loadCalendar() { + const newCalendarDays = [] as CalendarDay[]; + + calendarStore.getUpcoming(calendarDayCount).then((upcoming) => { + const currentDay = startOfDay(currentTime.value); + + for (let i = 0; i < calendarDayCount; i++) { + const day = new Date(currentDay); + day.setDate(day.getDate() + i); + + const entries = upcoming.filter((entry) => { + const entryStart = startOfDay(entry.start); + const entryEnd = endOfDay(entry.end); + + if (entry.isAllDay) { + return day > entryStart && day < entryEnd; + } + + return day >= entryStart && day <= entryEnd; + }); + + newCalendarDays.push(new CalendarDay(day, entries)); + } + + calendarDays.value = newCalendarDays; + + calendarReady.value = true; + }); + } + + loadCalendar(); + + setInterval(() => currentTime.value = new Date(), 1000); + setInterval(() => loadCalendar(), 60000);