UNPKG

node-red-contrib-home-assistant-websocket

Version:
161 lines (160 loc) 9.81 kB
"use strict"; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var _EventsCalendarController_instances, _EventsCalendarController_nextUpcomingTimer, _EventsCalendarController_queuedCalendarItemTimers, _EventsCalendarController_intervalLengthMs, _EventsCalendarController_headStartMs, _EventsCalendarController_isItemValid, _EventsCalendarController_calendarItemMatches; Object.defineProperty(exports, "__esModule", { value: true }); const ExposeAsMixin_1 = __importDefault(require("../../common/controllers/ExposeAsMixin")); const OutputController_1 = __importDefault(require("../../common/controllers/OutputController")); const globals_1 = require("../../globals"); const utils_1 = require("../../helpers/utils"); const CalendarItem_1 = require("./CalendarItem"); const ExposeAsController = (0, ExposeAsMixin_1.default)((OutputController_1.default)); class EventsCalendarController extends ExposeAsController { constructor() { super(...arguments); _EventsCalendarController_instances.add(this); _EventsCalendarController_nextUpcomingTimer.set(this, void 0); _EventsCalendarController_queuedCalendarItemTimers.set(this, {}); _EventsCalendarController_intervalLengthMs.set(this, 15 * 60000); // 15 minutes in milliseconds _EventsCalendarController_headStartMs.set(this, 20); // give the next queue 20 milliseconds head start } /** * Retrieve all calendar items in the upcoming window and queue them to fire at their allocated time. * * @returns undefined */ async queueUpcomingCalendarEvents(start) { if (typeof start === 'undefined') { // if start is not defined, this is the result of an initial or reset call. // so reset the upcoming timer and clear the queue cache. clearTimeout(__classPrivateFieldGet(this, _EventsCalendarController_nextUpcomingTimer, "f")); __classPrivateFieldSet(this, _EventsCalendarController_nextUpcomingTimer, undefined, "f"); Object.keys(__classPrivateFieldGet(this, _EventsCalendarController_queuedCalendarItemTimers, "f")).forEach((key) => { clearTimeout(__classPrivateFieldGet(this, _EventsCalendarController_queuedCalendarItemTimers, "f")[key]); delete __classPrivateFieldGet(this, _EventsCalendarController_queuedCalendarItemTimers, "f")[key]; }); } const now = new Date(); const nodeOffsetMs = await this.getOffsetMs(); const offsetIntervalStart = start || new Date(now.getTime() + nodeOffsetMs); const offsetIntervalEnd = new Date(offsetIntervalStart.getTime() + __classPrivateFieldGet(this, _EventsCalendarController_intervalLengthMs, "f")); const nextQueueTime = new Date(offsetIntervalEnd.getTime() - __classPrivateFieldGet(this, _EventsCalendarController_headStartMs, "f")); try { const items = await this.retrieveCalendarItems(offsetIntervalStart, offsetIntervalEnd); if (!Array.isArray(items)) { return; } // Create a timer for each matching item and place it in a queue cache items.forEach((item) => this.queueCalendarItem(item, now)); if (items.length > 1) { this.status.setSending(globals_1.RED._('ha-events-calendar.status.queued-multi', { count: items.length, })); } else if (items.length > 0) { this.status.setSending(globals_1.RED._('ha-events-calendar.status.queued-one')); } else { this.status.setSending(globals_1.RED._('ha-events-calendar.status.queued-none')); } } catch (exc) { this.status.setFailed(globals_1.RED._('ha-events-calendar.error.retrieval', { entity: this.node.config.entityId, error: exc.message, })); } // Queue a timer for the next interval starting at intervalEnd. __classPrivateFieldSet(this, _EventsCalendarController_nextUpcomingTimer, setTimeout(this.queueUpcomingCalendarEvents.bind(this, offsetIntervalEnd), nextQueueTime.getTime() - now.getTime()), "f"); } async retrieveCalendarItems(intervalStart, intervalEnd) { const rawItems = await this.homeAssistant.http.get(`/calendars/${this.node.config.entityId}`, { start: intervalStart.toISOString(), end: intervalEnd.toISOString(), }); if (!Array.isArray(rawItems)) { return; } const filterText = this.node.config.filter ? await this.typedInputService.getValue(this.node.config.filter, this.node.config.filterType) : undefined; const items = rawItems .map(CalendarItem_1.createCalendarItem) .filter(__classPrivateFieldGet(this, _EventsCalendarController_instances, "m", _EventsCalendarController_calendarItemMatches).bind(this, intervalStart, intervalEnd, filterText)); // TODO: allow more customisable conditions for filtering return items; } async queueCalendarItem(item, now) { let timeToFireMs = await this.calcFireMs(item.date(this.node.config.eventType), now); if (timeToFireMs < 0 - __classPrivateFieldGet(this, _EventsCalendarController_headStartMs, "f")) { // if time has significantly passed for this item, then don't bother queuing it. return; } else if (timeToFireMs < 0) { // if time has passed only a little bit but we have an item to queue, perhaps we should just fire it now. timeToFireMs = 0; } // If the timer is already in the queue cache, then remove it ready for replacement if (__classPrivateFieldGet(this, _EventsCalendarController_queuedCalendarItemTimers, "f")[item.queueIndex()]) { clearTimeout(__classPrivateFieldGet(this, _EventsCalendarController_queuedCalendarItemTimers, "f")[item.queueIndex()]); } // Queue/requeue it and set a timer to fire it at the appropriate time __classPrivateFieldGet(this, _EventsCalendarController_queuedCalendarItemTimers, "f")[item.queueIndex()] = setTimeout(this.fireCalendarItem.bind(this, item), timeToFireMs); } async calcFireMs(eventTime, now) { const nodeOffsetMs = await this.getOffsetMs(); const fireMs = eventTime.getTime() - nodeOffsetMs; const timeToFireMs = fireMs - now.getTime(); return timeToFireMs; } async getOffsetMs() { const offsetNum = await this.typedInputService.getValue(this.node.config.offset, this.node.config.offsetType); const nodeOffsetMs = (0, utils_1.getTimeInMilliseconds)(offsetNum, this.node.config.offsetUnits); return nodeOffsetMs; } async fireCalendarItem(item) { // Pull the item and timer off the queue cache so that it is only fired once const index = item.queueIndex(); delete __classPrivateFieldGet(this, _EventsCalendarController_queuedCalendarItemTimers, "f")[index]; if (this.isEnabled === false || !this.homeAssistant.isHomeAssistantRunning || !__classPrivateFieldGet(this, _EventsCalendarController_instances, "m", _EventsCalendarController_isItemValid).call(this, item)) { return; } // send the message including the calendar item const message = {}; await this.setCustomOutputs(this.node.config.outputProperties, message, { calendarItem: item, }); this.node.send(message); this.status.setSuccess(globals_1.RED._('ha-events-calendar.status.sent', { summary: item.summary })); } } _EventsCalendarController_nextUpcomingTimer = new WeakMap(), _EventsCalendarController_queuedCalendarItemTimers = new WeakMap(), _EventsCalendarController_intervalLengthMs = new WeakMap(), _EventsCalendarController_headStartMs = new WeakMap(), _EventsCalendarController_instances = new WeakSet(), _EventsCalendarController_isItemValid = function _EventsCalendarController_isItemValid(item) { if (!item) { return false; } return true; }, _EventsCalendarController_calendarItemMatches = function _EventsCalendarController_calendarItemMatches(currentStart, currentEnd, filter, calendarItem) { const itemDate = calendarItem.date(this.node.config.eventType); if (!(itemDate >= currentStart && itemDate < currentEnd)) { return false; } if (filter && !calendarItem.summary.includes(filter)) { return false; } return true; }; exports.default = EventsCalendarController;