@sebbo2002/vestaboard2mqtt 4.0.3-develop.2 → 4.0.3-develop.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/start.cjs.map +1 -1
- package/dist/bin/start.d.cts +0 -0
- package/dist/bin/start.d.ts +0 -0
- package/dist/bin/start.js +1 -1
- package/dist/{chunk-ANA6MMQH.js → chunk-RW2UBCBQ.js} +1 -1
- package/dist/{chunk-ANA6MMQH.js.map → chunk-RW2UBCBQ.js.map} +1 -1
- package/dist/lib/index.cjs.map +1 -1
- package/dist/lib/index.js +1 -1
- package/package.json +11 -11
package/dist/bin/start.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/index.ts","../../src/lib/cache.ts","../../src/lib/config.ts","../../src/lib/message.ts","../../src/lib/pages/message.ts","../../src/lib/pages/calendar.ts","../../src/lib/pages/today.ts","../../src/lib/pages/index.ts","../../src/bin/start.ts"],"sourcesContent":["import mqtt from 'async-mqtt';\nimport Config from './config.js';\nimport Cache from './cache.js';\nimport { MqttClient } from 'mqtt';\nimport Message from './message.js';\nimport pages from './pages/index.js';\nimport Page from './page.js';\nimport { Vesta } from 'vestaboard-api';\n\nexport default class Vestaboard2MQTT {\n private readonly mqtt: MqttClient;\n private readonly board: Vesta;\n private readonly pageConfig = new Cache('page-config');\n private subscriptionIds?: string[];\n private currentMessage?: {page: Page<unknown>, payload: string};\n private currentTimer?: NodeJS.Timeout;\n\n public static async run(): Promise<Vestaboard2MQTT> {\n await Config.load();\n return new Vestaboard2MQTT();\n }\n\n constructor() {\n this.board = new Vesta({\n apiKey: Config.board.key,\n apiSecret: Config.board.secret\n });\n\n this.mqtt = mqtt.connect(Config.mqtt.url, {\n will: {\n topic: Config.mqtt.prefix + '/status',\n payload: 'offline',\n qos: 0,\n retain: true\n }\n });\n this.mqtt.on('connect', () => {\n this.mqtt.publish(Config.mqtt.prefix + '/status', 'online', {\n retain: true\n });\n\n this.setupBrokerSubscriptions().catch(error => {\n console.error(new Error(`Unable to setup mqtt subscriptions: ${error.stack}`));\n process.exit(1);\n });\n });\n this.mqtt.on('message', (topic, message) => {\n this.handleMessage(topic, message.toString()).catch(error => {\n this.debug(`Unable to handle message: ${error.stack}`);\n });\n });\n }\n\n private async setupBrokerSubscriptions() {\n if(!this.mqtt) {\n throw new Error('Unable to setup subscriptions: client not set.');\n }\n\n await this.mqtt.subscribe(Config.mqtt.prefix + '/#');\n }\n\n private debug(message: Error | string) {\n const msg = String(message);\n this.mqtt.publish(Config.mqtt.prefix + '/debug', msg);\n console.log(msg);\n }\n\n private async handleMessage(topic: string, payload: string): Promise<void> {\n if([Config.mqtt.prefix + '/debug', Config.mqtt.prefix + '/status'].includes(topic)) {\n // just ignore my own events\n }\n else if(topic === Config.mqtt.prefix + '/disabled') {\n const value = ['1', 'true', 'TRUE'].includes(payload);\n await Config.updateDisabled(value);\n if(!value) {\n this.debug('Board not disabled anymore, update with latest message');\n await this.updateCurrentMessage();\n }\n }\n else if(Object.keys(pages).map(id => Config.mqtt.prefix + '/' + id).includes(topic)) {\n const page = pages[ topic.substr(Config.mqtt.prefix.length + 1) ];\n await this.renderMessage(page, payload);\n }\n else {\n const page = Object.entries(pages)\n .find(([id]) => topic.startsWith(Config.mqtt.prefix + '/' + id + '/'));\n\n if(!page) {\n this.debug(`Unknown topic ${topic}, I'm sorry`);\n return;\n }\n\n const key = topic.substr((Config.mqtt.prefix + '/' + page[0] + '/').length);\n const cache = await this.pageConfig.get<Partial<unknown>>(page[0]);\n\n const config = page[1].parseConfig(key, payload, cache || {});\n const newConfig = Object.assign(cache || {}, config);\n await this.pageConfig.set(page[0], newConfig);\n await this.updateCurrentMessage();\n this.debug(`Update config for page module ${page[0]}: ${JSON.stringify(newConfig, null, ' ')}`);\n }\n }\n\n private async updateCurrentMessage() {\n if(this.currentMessage) {\n await this.renderMessage(\n this.currentMessage.page,\n this.currentMessage.payload\n );\n }\n }\n\n private async renderMessage(page: Page<unknown>, payload: string) {\n this.currentMessage = {\n page,\n payload\n };\n\n const pageEntry = Object.entries(pages).find(([,pageInstance]) => pageInstance === page);\n if(Config.board.disabled || !pageEntry) {\n return;\n }\n\n const parsePayload = await page.parsePayload(payload);\n const config = await this.pageConfig.get<Partial<unknown>>(pageEntry[0]);\n\n const response = await page.render(parsePayload, config || {});\n if (response.message) {\n await this.sendMessage(response.message);\n }\n\n if(this.currentTimer) {\n clearTimeout(this.currentTimer);\n delete this.currentTimer;\n }\n if(response.validTill) {\n this.debug(`Set timer for ${response.validTill.toString()} to update message`);\n this.currentTimer = setTimeout(() => {\n this.debug('Here I am again, updating the message now…');\n this.renderMessage(page, payload).catch(error => {\n this.debug(`Unable to update page: ${error}`);\n });\n }, response.validTill.getTime() - new Date().getTime());\n }\n }\n\n private async sendMessage(message: Message = new Message()): Promise<void> {\n const charArray = message.export();\n\n if(!this.subscriptionIds) {\n const subscriptions = await this.board.getSubscriptions();\n this.subscriptionIds = subscriptions.map(i => i._id);\n }\n\n this.debug('Sending Message:\\n\\n' + message.toString());\n\n await Promise.all(this.subscriptionIds.map(id => this.board.postMessage(id, charArray)));\n }\n}\n","import {join} from 'path';\nimport {existsSync} from 'fs';\nimport {promises} from 'fs';\n\ntype CacheData = Record<string, CacheNamespace>;\ntype CacheNamespace = Record<string, CacheItem>;\ntype CacheItem = CacheItemObject | CacheValue;\ntype CacheItemObject = {updated: number, value: CacheValue};\ntype CacheValue = unknown;\n\n// as node@12 has no 'fs/promises'\nconst {readFile, writeFile} = promises;\n\nexport default class Cache {\n private static readonly file: string = join(process.env.HOME || '~', '.vestaboard2mqtt');\n private readonly namespace: string;\n private static currentFetch?: Promise<void>;\n private static currentData?: CacheData = {};\n\n constructor(namespace: string) {\n this.namespace = namespace;\n }\n\n static async fetch(): Promise<void> {\n if(this.currentFetch) {\n return this.currentFetch;\n }\n\n const fetch = this.fetchFromFile();\n this.currentFetch = fetch;\n return fetch;\n }\n\n static async fetchFromFile (): Promise<void> {\n if(!existsSync(this.file)) {\n return;\n }\n\n try {\n const content = await readFile(this.file, {encoding: 'utf8'});\n this.currentData = JSON.parse(content);\n }\n catch(error) {\n console.warn(`Unable to parse cache file: ${error}`);\n }\n }\n\n static isCacheItemObject(obj: CacheItem): obj is CacheItemObject {\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return typeof obj === 'object' && typeof obj.updated === 'number' && typeof obj.value !== 'undefined';\n }\n\n async get<T>(key: string, maxAge = 0): Promise<T | null> {\n await Cache.fetch();\n\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace] ||\n !Cache.currentData[this.namespace][key]\n ) {\n return null;\n }\n\n const item = Cache.currentData[this.namespace][key];\n if(!Cache.isCacheItemObject(item) && maxAge > 0) {\n return null;\n }\n if(!Cache.isCacheItemObject(item)) {\n return item as T;\n }\n\n if(maxAge > 0 && new Date().getTime() - item.updated > maxAge) {\n return null;\n }\n\n return item.value as T;\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await Cache.fetch();\n\n if(!Cache.currentData) {\n throw new Error('Unable to set value: currentData is empty!');\n }\n\n Cache.currentData[this.namespace] = Cache.currentData[this.namespace] || {};\n Cache.currentData[this.namespace][key] = {\n updated: new Date().getTime(),\n value\n };\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n async delete(key: string): Promise<void> {\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace]\n ) {\n return;\n }\n\n delete Cache.currentData[this.namespace][key];\n\n if(Object.keys(Cache.currentData[this.namespace]).length === 0) {\n delete Cache.currentData[this.namespace];\n }\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n static async save(): Promise<void> {\n await writeFile(this.file, JSON.stringify(this.currentData, null, ' '));\n }\n}\n","import Cache from './cache.js';\n\nexport interface ConfigBoardData {\n id: string;\n key: string;\n secret: string;\n disabled: boolean;\n}\n\nexport interface ConfigMQTTData {\n url: string;\n prefix: string;\n}\n\nexport interface ConfigCalendarData {\n urls: Record<string, string>;\n cacheTTL: number;\n}\n\nexport interface ConfigData {\n board: ConfigBoardData;\n mqtt: ConfigMQTTData;\n calendar: ConfigCalendarData;\n}\n\nexport default class Config {\n private static readonly cache = new Cache('config');\n private static data?: ConfigData;\n\n public static get loaded(): boolean {\n return !!this.data && !!this.cache;\n }\n\n public static get board(): ConfigBoardData {\n if(this.loaded && this.data?.board) {\n return this.data.board;\n }\n\n throw new Error('Unable to access board config, is config loaded?');\n }\n\n public static get mqtt(): ConfigMQTTData {\n if(this.loaded && this.data?.mqtt) {\n return this.data.mqtt;\n }\n\n throw new Error('Unable to access mqtt config, is config loaded?');\n }\n\n public static get calendar(): ConfigCalendarData {\n if(this.loaded && this.data?.calendar) {\n return this.data.calendar;\n }\n\n throw new Error('Unable to access calendar config, is config loaded?');\n }\n\n public static async load(): Promise<void> {\n const data = await this.cache.get<ConfigData>('default');\n if(!data) {\n throw new Error('Unable to load configuration, try to run the setup script…');\n }\n\n this.data = data;\n }\n\n public static async save(data: ConfigData): Promise<void> {\n this.data = data;\n await this.cache.set('default', data);\n }\n\n public static async updateDisabled(newValue: boolean): Promise<void> {\n if(newValue === this.data?.board.disabled || !this.loaded || !this.data?.mqtt) {\n return;\n }\n\n this.data.board.disabled = newValue;\n await this.save(this.data);\n }\n}\n","import {BoardCharArray, characterCode, emptyBoard, LINE_LENGTH} from 'vestaboard-api/lib/cjs/values.js';\n\nexport enum MessageWriteOptionsLine {\n CURRENT = 'CURRENT',\n NEXT = 'NEXT'\n}\n\nexport interface MessageWriteOptions {\n line?: number | MessageWriteOptionsLine;\n row?: number;\n}\n\nexport interface MessageDrawOptions {\n line?: number;\n}\n\n\nconst SPECIAL_CHAR_MAP = [\n ['ä', [1, 5]],\n ['Ä', [1, 5]],\n ['ö', [15, 5]],\n ['Ö', [15, 5]],\n ['ü', [21, 5]],\n ['Ü', [21, 5]],\n ['ß', [19, 19]],\n ['🟥', [63]],\n ['🟧', [64]],\n ['🟨', [65]],\n ['🟩', [66]],\n ['🟦', [67]],\n ['🟪', [68]],\n ['⬜️', [69]],\n ['⬜', [69]],\n ['⬛️', [0]],\n ['⬛', [0]],\n ['﹫', [38]],\n ['@', [38]],\n ['0️⃣', [36]],\n ['1️⃣', [27]],\n ['2️⃣', [28]],\n ['3️⃣', [29]],\n ['4️⃣', [30]],\n ['5️⃣', [31]],\n ['6️⃣', [32]],\n ['7️⃣', [33]],\n ['8️⃣', [34]],\n ['9️⃣', [35]],\n ['❕', [37]],\n ['‼️', [37, 37]],\n ['❗️', [37]],\n ['⁉️', [37, 60]],\n ['⚠️', [65]],\n ['#️⃣', [39]],\n ['💸', [40]],\n ['💲', [40]],\n ['💵', [40]],\n ['💰', [40]],\n ['$', [40]],\n ['﹩', [40]],\n ['$', [40]],\n ['←', [44]],\n ['→', [44]],\n ['➡', [44]],\n ['⬅', [44]],\n ['➔', [44]],\n ['↔', [44]],\n ['–', [44]],\n ['➕', [46]],\n ['+', [46]],\n ['﹪', [54]],\n ['%', [54]],\n ['❓', [60]],\n ['❔', [60]],\n ['℃', [62, 3]],\n ['℉', [62, 6]]\n];\n\n\nexport default class Message {\n private board: BoardCharArray;\n private currentLine: number | null = null;\n private isFilled = false;\n\n constructor(board: BoardCharArray = Message.newBoardCharArray()) {\n this.board = board;\n }\n\n static newBoardCharArray(): BoardCharArray {\n return JSON.parse(JSON.stringify(emptyBoard));\n }\n\n write(text: string, options: MessageWriteOptions = {}) : void{\n let firstLine = 0;\n if(this.isFilled) {\n return;\n }\n\n if(options.line === MessageWriteOptionsLine.CURRENT) {\n firstLine = this.currentLine || 0;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT && this.currentLine === null) {\n this.currentLine = 0;\n firstLine = this.currentLine;\n }\n else if(\n options.line === MessageWriteOptionsLine.NEXT &&\n typeof this.currentLine === 'number' &&\n this.currentLine < this.board.length - 1\n ) {\n // console.log('write() → current line + 1');\n this.currentLine++;\n firstLine = this.currentLine;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT) {\n this.isFilled = true;\n return;\n }\n else if(options.line !== undefined) {\n firstLine = options.line;\n }\n\n const status = {\n firstLine,\n lastLine: this.board.length - 1,\n firstRow: options.row || 0,\n lastRow: LINE_LENGTH - 1\n };\n const pointer = [status.firstRow, status.firstLine];\n\n const words = text.split(/\\s+/);\n words.forEach((word, i) => {\n\n // Space\n if(i !== 0 && pointer[0] !== status.firstRow) {\n this.board[pointer[1]][pointer[0]] = 0;\n pointer[0]++;\n }\n\n let charsLeftInLine = status.lastRow - pointer[0] + 1;\n const chars = Message.word2chars(word);\n // console.log(\n // 'write()',\n // 'chars =', chars,\n // 'left =', charsLeftInLine,\n // `(${status.lastRow} - ${pointer[0]})`,\n // 'pointer =', pointer,\n // 'charsLeftInLine =', charsLeftInLine\n // );\n\n // Unsupported Word / emoji?\n if(chars.filter(c => c === 60).length === chars.length && chars.length > 0) {\n return;\n }\n\n // New Line?\n if(\n chars.length > charsLeftInLine &&\n chars.length <= status.lastRow - status.firstRow + 1 &&\n pointer[1] < status.lastLine\n ) {\n pointer[0] = status.firstRow;\n pointer[1]++;\n charsLeftInLine = status.lastRow - pointer[0];\n // console.log('write() → new line', 'chars =', chars, 'left =', charsLeftInLine, `(${status.lastRow} - ${pointer[0]})`, 'pointer =', pointer);\n }\n\n // Add Word\n if(chars.length <= charsLeftInLine) {\n this.board[pointer[1]].splice(pointer[0], chars.length, ...chars);\n pointer[0] += chars.length;\n }\n else if(chars.length > charsLeftInLine && charsLeftInLine > 5) {\n this.board[pointer[1]].splice(pointer[0], charsLeftInLine, ...chars.slice(0, charsLeftInLine));\n pointer[0] += charsLeftInLine;\n }\n });\n this.currentLine = pointer[1];\n }\n\n static word2chars(word: string): number[] {\n const result: number[] = [];\n for(const char of word) {\n result.push(...this.char2char(char));\n }\n\n return result;\n }\n\n static char2char(char: string): number[] {\n const fromVestaMap = Object.entries(characterCode)\n .find(([key]) => char === key);\n if(fromVestaMap) {\n return [ fromVestaMap[1]] ;\n }\n\n const fromMyMap = SPECIAL_CHAR_MAP\n .find(([mapChar]) => char === mapChar);\n if(fromMyMap && Array.isArray(fromMyMap[1])) {\n return fromMyMap[1];\n }\n\n return [60];\n }\n\n repeat(char: string, options: MessageDrawOptions = {}): void {\n const line = this.board[options.line || 0];\n line.fill(Message.char2char(char)[0]);\n }\n\n centerLines(): void {\n this.board.forEach(line => {\n const space = [line.findIndex(c => c !== 0), line.slice().reverse().findIndex(c => c !== 0)];\n\n // Line is completely full, continue…\n if(space[0] === -1 || space[1] === -1) {\n return;\n }\n\n const content = line.slice(space[0], line.length - space[1]);\n const padding = Math.floor((space[0] + space[1]) / 2);\n\n line.fill(0, 0, padding);\n line.splice(padding, content.length, ...content);\n line.fill(0, padding + content.length);\n });\n }\n\n center(): void {\n const space = [\n this.board.findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.slice().reverse().findIndex(c => c !== 0) : l.length)),\n this.board.slice().reverse().findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.findIndex(c => c !== 0) : l.length)),\n ];\n\n const padding = [\n Math.floor((space[0] + space[2]) / 2),\n Math.floor((space[1] + space[3]) / 2)\n ];\n\n // Move up/down\n if(space[0] !== padding[0]) {\n const add = padding[0] - space[0];\n this.board.splice(space[0] < padding[0] ? 0 : this.board.length - add, 0,\n ...this.board.splice(this.board.length - add, add)\n );\n }\n\n // Move left/right\n if(space[3] !== padding[1]) {\n const add = padding[1] - space[3];\n\n this.board.forEach(line => {\n line.splice(space[3] < padding[1] ? 0 : line.length - add, 0,\n ...line.splice(line.length - add, add)\n );\n });\n }\n }\n\n isEmpty (): boolean {\n return !this.board.find(line =>\n line.find(char => char !== 0)\n );\n }\n\n toString(): string {\n return '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n' +\n this.board\n .map(line => '# ' + line.map(char => Message.charToString(char)).join('') + ' #\\n')\n .join('') +\n '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n';\n }\n\n static charToString(char: number): string {\n const entry = Object.entries(characterCode)\n .filter(([name]) => name.length <= 2)\n .find(([, code]) => code === char);\n\n if (entry) {\n return entry[0].toUpperCase() + ' ';\n }\n\n switch (char) {\n case 63:\n return '🟥';\n case 64:\n return '🟧';\n case 65:\n return '🟨';\n case 66:\n return '🟩';\n case 67:\n return '🟦';\n case 68:\n return '🟪';\n case 69:\n return '⬜️';\n default:\n return ' ';\n }\n }\n\n export(): BoardCharArray {\n return this.board;\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\n\nexport default class MessagePage implements Page<string> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): string {\n return payload || '';\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async render (content: string): Promise<PageRenderResponse> {\n const message = new Message();\n\n content.split('\\n').forEach(line => {\n message.write(line, {line: MessageWriteOptionsLine.NEXT});\n });\n\n message.centerLines();\n message.center();\n return {message};\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\nimport ical, { VEvent } from 'node-ical';\nimport Config from '../config.js';\nimport TodayPage, {TodayPagePayload} from './today.js';\n\n\nexport interface CalendarPagePayload {\n calendars: string[];\n}\n\nexport type CalendarPageItem = {start: Date, end: Date, summary: string};\n\nexport default class CalendarPage implements Page<CalendarPagePayload> {\n static readonly cache = new Cache('calendar');\n\n public parsePayload(payload: string | null): CalendarPagePayload {\n return {\n calendars: (payload || '').split(',')\n };\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async fetchURL(url: string): Promise<CalendarPageItem[]> {\n let preCached = await CalendarPage.cache.get<CalendarPageItem[]>(url, 1000 * 60 * 10);\n if(!preCached) {\n const calendar = await ical.async.fromURL(url);\n const events = Object.values(calendar)\n .filter(entry =>\n entry.type === 'VEVENT' &&\n entry.start &&\n entry.end &&\n entry.summary\n ) as VEvent[];\n preCached = events\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary).trim()\n }))\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime())\n .slice(0, 6);\n\n await CalendarPage.cache.set(url, preCached);\n }\n\n return preCached\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary)\n }))\n .filter(entry =>\n entry.end > new Date()\n );\n }\n\n public static isSameDay (a: Date, b: Date): boolean {\n const result = Boolean(\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n\n return result;\n }\n\n public static isMidnight (date: Date): boolean {\n const result = Boolean(\n !date.getHours() &&\n !date.getMinutes() &&\n !date.getSeconds() &&\n !date.getMilliseconds()\n );\n\n return result;\n }\n\n public async fetchURLs(urls: string[]): Promise<CalendarPageItem[]> {\n const rawResult: {start: Date, end: Date, summary: string}[][] = await Promise.all(urls.map(url => this.fetchURL(url)));\n return ([] as CalendarPageItem[])\n .concat(...rawResult)\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime());\n }\n\n public static getTimeStr (event: CalendarPageItem, oneLine = false): string {\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(), event.start)\n ) {\n return 'Heute';\n }\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), event.start)\n ) {\n return oneLine ? 'Morgen' : 'Morgn';\n }\n\n return event.start.getHours().toString().padStart(2, '⬛️') + ':' +\n event.start.getMinutes().toString().padStart(2, '0');\n }\n\n public async render (payload: CalendarPagePayload): Promise<PageRenderResponse> {\n const message = new Message();\n const urls = payload.calendars\n .map(id => Config.calendar.urls[id])\n .filter(Boolean);\n\n const calendar = await this.fetchURLs(urls)\n .then(calendar => calendar.filter(entry => {\n if (CalendarPage.isMidnight(entry.start) && CalendarPage.isMidnight(entry.end)) {\n return CalendarPage.isSameDay(new Date(), entry.start) || (\n new Date().getHours() >= 20 &&\n CalendarPage.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), entry.start)\n );\n } else {\n return entry.start < new Date(new Date().getTime() + (1000 * 60 * 60 * 12));\n }\n }));\n\n if(!calendar.length) {\n const today = new TodayPage();\n const pageConfigCache = new Cache('page-config');\n const config = await pageConfigCache.get<Partial<TodayPagePayload>>('today');\n return today.render({}, config || {});\n }\n\n let validTill = new Date(new Date().getTime() + 1000 * 60 * 10);\n calendar.forEach(event => {\n message.write(\n CalendarPage.getTimeStr(event, calendar.length === 1),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write(event.summary, {line: MessageWriteOptionsLine.CURRENT, row: 6});\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n if(event.end < validTill) {\n validTill = event.end;\n }\n });\n\n message.center();\n return {\n message,\n validTill\n };\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\nexport interface TodayPagePayload {\n temp?: [number, number];\n precip?: number;\n locale: string;\n}\n\nexport default class TodayPage implements Page<Partial<TodayPagePayload>, TodayPagePayload> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): Partial<TodayPagePayload> {\n try {\n return JSON.parse(payload || '') as Partial<TodayPagePayload>;\n }\n catch(error) {\n return {};\n }\n }\n\n parseConfig(key: string, value: string | null, config: Partial<TodayPagePayload>): Partial<TodayPagePayload> {\n const result: Partial<TodayPagePayload> = {};\n console.log('parseConfig', config);\n\n if(key === 'min-temp' && Array.isArray(config.temp)) {\n result.temp = [\n parseInt(value || ''),\n Math.max(...config.temp)\n ];\n }\n else if(key === 'min-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp' && Array.isArray(config.temp)) {\n result.temp = [\n Math.min(...config.temp),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'precip') {\n result.precip = parseInt(value || '', 10);\n }\n else if(key === 'locale') {\n result.locale = String(value);\n }\n\n return result;\n }\n\n public async render (payload: Partial<TodayPagePayload>, config: Partial<TodayPagePayload>): Promise<PageRenderResponse> {\n const message = new Message();\n const today = new Date();\n\n const mergedPayload: TodayPagePayload = Object.assign({\n temp: undefined,\n precip: undefined,\n locale: 'en'\n }, config, payload);\n\n message.write(today.toLocaleString(mergedPayload.locale, {weekday: 'long'}));\n message.write(\n today.toLocaleString(mergedPayload.locale, {day: 'numeric', month: 'long'}),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n const varContent = [];\n if(Array.isArray(mergedPayload.temp)) {\n varContent.push(`${Math.min(...mergedPayload.temp)}-${Math.max(...mergedPayload.temp)}°C`);\n }\n if(typeof mergedPayload.precip === 'number' && mergedPayload.precip > 0) {\n varContent.push(`${mergedPayload.precip}%`);\n }\n if(varContent.length) {\n message.write(varContent.join(', '), {line: MessageWriteOptionsLine.NEXT});\n }\n\n message.center();\n\n const validTill = new Date();\n validTill.setHours(24,0,0,0);\n\n return { message, validTill };\n }\n}\n","import Page from '../page.js';\nimport MessagePage from './message.js';\nimport CalendarPage from './calendar.js';\nimport TodayPage from './today.js';\n\nconst pages: Record<string, Page<unknown, unknown>> = {\n message: new MessagePage(),\n calendar: new CalendarPage(),\n today: new TodayPage()\n};\n\nexport default pages;\n","#!/usr/bin/env node\n'use strict';\n\nimport Vestaboard2MQTT from '../lib/index.js';\n\nVestaboard2MQTT.run().catch(error => {\n console.log(error);\n process.exit(1);\n});\n"],"mappings":"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","names":["import_async_mqtt","import_path","import_fs","readFile","writeFile","_Cache","namespace","__async","fetch","content","error","obj","key","maxAge","item","value","Cache","Config","_a","__async","data","newValue","_b","Cache","import_values","SPECIAL_CHAR_MAP","Message","_Message","board","text","options","firstLine","status","pointer","word","i","charsLeftInLine","chars","c","result","char","fromVestaMap","key","fromMyMap","mapChar","line","space","content","padding","l","add","entry","name","code","MessagePage","payload","content","__async","message","Message","line","Cache","import_node_ical","TodayPage","payload","error","key","value","config","result","__async","message","Message","today","mergedPayload","varContent","validTill","Cache","_CalendarPage","payload","url","__async","preCached","calendar","ical","entry","a","b","date","urls","rawResult","event","oneLine","message","Message","id","Config","today","TodayPage","config","Cache","validTill","CalendarPage","pages","MessagePage","CalendarPage","TodayPage","pages_default","import_vestaboard_api","Vestaboard2MQTT","_Vestaboard2MQTT","Cache","Config","mqtt","error","topic","message","__async","msg","payload","value","pages_default","id","page","key","cache","config","newConfig","pageEntry","pageInstance","parsePayload","response","Message","charArray","subscriptions","i","Vestaboard2MQTT","error"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/index.ts","../../src/lib/cache.ts","../../src/lib/config.ts","../../src/lib/message.ts","../../src/lib/pages/message.ts","../../src/lib/pages/calendar.ts","../../src/lib/pages/today.ts","../../src/lib/pages/index.ts","../../src/bin/start.ts"],"sourcesContent":["import mqtt from 'async-mqtt';\nimport Config from './config.js';\nimport Cache from './cache.js';\nimport { MqttClient } from 'mqtt';\nimport Message from './message.js';\nimport pages from './pages/index.js';\nimport Page from './page.js';\nimport { Vesta } from 'vestaboard-api';\n\nexport default class Vestaboard2MQTT {\n private readonly mqtt: MqttClient;\n private readonly board: Vesta;\n private readonly pageConfig = new Cache('page-config');\n private subscriptionIds?: string[];\n private currentMessage?: {page: Page<unknown>, payload: string};\n private currentTimer?: NodeJS.Timeout;\n\n public static async run(): Promise<Vestaboard2MQTT> {\n await Config.load();\n return new Vestaboard2MQTT();\n }\n\n constructor() {\n this.board = new Vesta({\n apiKey: Config.board.key,\n apiSecret: Config.board.secret\n });\n\n this.mqtt = mqtt.connect(Config.mqtt.url, {\n will: {\n topic: Config.mqtt.prefix + '/status',\n payload: 'offline',\n qos: 0,\n retain: true\n }\n });\n this.mqtt.on('connect', () => {\n this.mqtt.publish(Config.mqtt.prefix + '/status', 'online', {\n retain: true\n });\n\n this.setupBrokerSubscriptions().catch(error => {\n console.error(new Error(`Unable to setup mqtt subscriptions: ${error.stack}`));\n process.exit(1);\n });\n });\n this.mqtt.on('message', (topic, message) => {\n this.handleMessage(topic, message.toString()).catch(error => {\n this.debug(`Unable to handle message: ${error.stack}`);\n });\n });\n }\n\n private async setupBrokerSubscriptions() {\n if(!this.mqtt) {\n throw new Error('Unable to setup subscriptions: client not set.');\n }\n\n await this.mqtt.subscribe(Config.mqtt.prefix + '/#');\n }\n\n private debug(message: Error | string) {\n const msg = String(message);\n this.mqtt.publish(Config.mqtt.prefix + '/debug', msg);\n console.log(msg);\n }\n\n private async handleMessage(topic: string, payload: string): Promise<void> {\n if([Config.mqtt.prefix + '/debug', Config.mqtt.prefix + '/status'].includes(topic)) {\n // just ignore my own events\n }\n else if(topic === Config.mqtt.prefix + '/disabled') {\n const value = ['1', 'true', 'TRUE'].includes(payload);\n await Config.updateDisabled(value);\n if(!value) {\n this.debug('Board not disabled anymore, update with latest message');\n await this.updateCurrentMessage();\n }\n }\n else if(Object.keys(pages).map(id => Config.mqtt.prefix + '/' + id).includes(topic)) {\n const page = pages[ topic.substr(Config.mqtt.prefix.length + 1) ];\n await this.renderMessage(page, payload);\n }\n else {\n const page = Object.entries(pages)\n .find(([id]) => topic.startsWith(Config.mqtt.prefix + '/' + id + '/'));\n\n if(!page) {\n this.debug(`Unknown topic ${topic}, I'm sorry`);\n return;\n }\n\n const key = topic.substr((Config.mqtt.prefix + '/' + page[0] + '/').length);\n const cache = await this.pageConfig.get<Partial<unknown>>(page[0]);\n\n const config = page[1].parseConfig(key, payload, cache || {});\n const newConfig = Object.assign(cache || {}, config);\n await this.pageConfig.set(page[0], newConfig);\n await this.updateCurrentMessage();\n this.debug(`Update config for page module ${page[0]}: ${JSON.stringify(newConfig, null, ' ')}`);\n }\n }\n\n private async updateCurrentMessage() {\n if(this.currentMessage) {\n await this.renderMessage(\n this.currentMessage.page,\n this.currentMessage.payload\n );\n }\n }\n\n private async renderMessage(page: Page<unknown>, payload: string) {\n this.currentMessage = {\n page,\n payload\n };\n\n const pageEntry = Object.entries(pages).find(([,pageInstance]) => pageInstance === page);\n if(Config.board.disabled || !pageEntry) {\n return;\n }\n\n const parsePayload = await page.parsePayload(payload);\n const config = await this.pageConfig.get<Partial<unknown>>(pageEntry[0]);\n\n const response = await page.render(parsePayload, config || {});\n if (response.message) {\n await this.sendMessage(response.message);\n }\n\n if(this.currentTimer) {\n clearTimeout(this.currentTimer);\n delete this.currentTimer;\n }\n if(response.validTill) {\n this.debug(`Set timer for ${response.validTill.toString()} to update message`);\n this.currentTimer = setTimeout(() => {\n this.debug('Here I am again, updating the message now…');\n this.renderMessage(page, payload).catch(error => {\n this.debug(`Unable to update page: ${error}`);\n });\n }, response.validTill.getTime() - new Date().getTime());\n }\n }\n\n private async sendMessage(message: Message = new Message()): Promise<void> {\n const charArray = message.export();\n\n if(!this.subscriptionIds) {\n const subscriptions = await this.board.getSubscriptions();\n this.subscriptionIds = subscriptions.map(i => i._id);\n }\n\n this.debug('Sending Message:\\n\\n' + message.toString());\n\n await Promise.all(this.subscriptionIds.map(id => this.board.postMessage(id, charArray)));\n }\n}\n","import {join} from 'path';\nimport {existsSync} from 'fs';\nimport {promises} from 'fs';\n\ntype CacheData = Record<string, CacheNamespace>;\ntype CacheNamespace = Record<string, CacheItem>;\ntype CacheItem = CacheItemObject | CacheValue;\ntype CacheItemObject = {updated: number, value: CacheValue};\ntype CacheValue = unknown;\n\n// as node@12 has no 'fs/promises'\nconst {readFile, writeFile} = promises;\n\nexport default class Cache {\n private static readonly file: string = join(process.env.HOME || '~', '.vestaboard2mqtt');\n private readonly namespace: string;\n private static currentFetch?: Promise<void>;\n private static currentData?: CacheData = {};\n\n constructor(namespace: string) {\n this.namespace = namespace;\n }\n\n static async fetch(): Promise<void> {\n if(this.currentFetch) {\n return this.currentFetch;\n }\n\n const fetch = this.fetchFromFile();\n this.currentFetch = fetch;\n return fetch;\n }\n\n static async fetchFromFile (): Promise<void> {\n if(!existsSync(this.file)) {\n return;\n }\n\n try {\n const content = await readFile(this.file, {encoding: 'utf8'});\n this.currentData = JSON.parse(content);\n }\n catch(error) {\n console.warn(`Unable to parse cache file: ${error}`);\n }\n }\n\n static isCacheItemObject(obj: CacheItem): obj is CacheItemObject {\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return typeof obj === 'object' && typeof obj.updated === 'number' && typeof obj.value !== 'undefined';\n }\n\n async get<T>(key: string, maxAge = 0): Promise<T | null> {\n await Cache.fetch();\n\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace] ||\n !Cache.currentData[this.namespace][key]\n ) {\n return null;\n }\n\n const item = Cache.currentData[this.namespace][key];\n if(!Cache.isCacheItemObject(item) && maxAge > 0) {\n return null;\n }\n if(!Cache.isCacheItemObject(item)) {\n return item as T;\n }\n\n if(maxAge > 0 && new Date().getTime() - item.updated > maxAge) {\n return null;\n }\n\n return item.value as T;\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await Cache.fetch();\n\n if(!Cache.currentData) {\n throw new Error('Unable to set value: currentData is empty!');\n }\n\n Cache.currentData[this.namespace] = Cache.currentData[this.namespace] || {};\n Cache.currentData[this.namespace][key] = {\n updated: new Date().getTime(),\n value\n };\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n async delete(key: string): Promise<void> {\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace]\n ) {\n return;\n }\n\n delete Cache.currentData[this.namespace][key];\n\n if(Object.keys(Cache.currentData[this.namespace]).length === 0) {\n delete Cache.currentData[this.namespace];\n }\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n static async save(): Promise<void> {\n await writeFile(this.file, JSON.stringify(this.currentData, null, ' '));\n }\n}\n","import Cache from './cache.js';\n\nexport interface ConfigBoardData {\n id: string;\n key: string;\n secret: string;\n disabled: boolean;\n}\n\nexport interface ConfigMQTTData {\n url: string;\n prefix: string;\n}\n\nexport interface ConfigCalendarData {\n urls: Record<string, string>;\n cacheTTL: number;\n}\n\nexport interface ConfigData {\n board: ConfigBoardData;\n mqtt: ConfigMQTTData;\n calendar: ConfigCalendarData;\n}\n\nexport default class Config {\n private static readonly cache = new Cache('config');\n private static data?: ConfigData;\n\n public static get loaded(): boolean {\n return !!this.data && !!this.cache;\n }\n\n public static get board(): ConfigBoardData {\n if(this.loaded && this.data?.board) {\n return this.data.board;\n }\n\n throw new Error('Unable to access board config, is config loaded?');\n }\n\n public static get mqtt(): ConfigMQTTData {\n if(this.loaded && this.data?.mqtt) {\n return this.data.mqtt;\n }\n\n throw new Error('Unable to access mqtt config, is config loaded?');\n }\n\n public static get calendar(): ConfigCalendarData {\n if(this.loaded && this.data?.calendar) {\n return this.data.calendar;\n }\n\n throw new Error('Unable to access calendar config, is config loaded?');\n }\n\n public static async load(): Promise<void> {\n const data = await this.cache.get<ConfigData>('default');\n if(!data) {\n throw new Error('Unable to load configuration, try to run the setup script…');\n }\n\n this.data = data;\n }\n\n public static async save(data: ConfigData): Promise<void> {\n this.data = data;\n await this.cache.set('default', data);\n }\n\n public static async updateDisabled(newValue: boolean): Promise<void> {\n if(newValue === this.data?.board.disabled || !this.loaded || !this.data?.mqtt) {\n return;\n }\n\n this.data.board.disabled = newValue;\n await this.save(this.data);\n }\n}\n","import {BoardCharArray, characterCode, emptyBoard, LINE_LENGTH} from 'vestaboard-api/lib/cjs/values.js';\n\nexport enum MessageWriteOptionsLine {\n CURRENT = 'CURRENT',\n NEXT = 'NEXT'\n}\n\nexport interface MessageWriteOptions {\n line?: number | MessageWriteOptionsLine;\n row?: number;\n}\n\nexport interface MessageDrawOptions {\n line?: number;\n}\n\n\nconst SPECIAL_CHAR_MAP = [\n ['ä', [1, 5]],\n ['Ä', [1, 5]],\n ['ö', [15, 5]],\n ['Ö', [15, 5]],\n ['ü', [21, 5]],\n ['Ü', [21, 5]],\n ['ß', [19, 19]],\n ['🟥', [63]],\n ['🟧', [64]],\n ['🟨', [65]],\n ['🟩', [66]],\n ['🟦', [67]],\n ['🟪', [68]],\n ['⬜️', [69]],\n ['⬜', [69]],\n ['⬛️', [0]],\n ['⬛', [0]],\n ['﹫', [38]],\n ['@', [38]],\n ['0️⃣', [36]],\n ['1️⃣', [27]],\n ['2️⃣', [28]],\n ['3️⃣', [29]],\n ['4️⃣', [30]],\n ['5️⃣', [31]],\n ['6️⃣', [32]],\n ['7️⃣', [33]],\n ['8️⃣', [34]],\n ['9️⃣', [35]],\n ['❕', [37]],\n ['‼️', [37, 37]],\n ['❗️', [37]],\n ['⁉️', [37, 60]],\n ['⚠️', [65]],\n ['#️⃣', [39]],\n ['💸', [40]],\n ['💲', [40]],\n ['💵', [40]],\n ['💰', [40]],\n ['$', [40]],\n ['﹩', [40]],\n ['$', [40]],\n ['←', [44]],\n ['→', [44]],\n ['➡', [44]],\n ['⬅', [44]],\n ['➔', [44]],\n ['↔', [44]],\n ['–', [44]],\n ['➕', [46]],\n ['+', [46]],\n ['﹪', [54]],\n ['%', [54]],\n ['❓', [60]],\n ['❔', [60]],\n ['℃', [62, 3]],\n ['℉', [62, 6]]\n];\n\n\nexport default class Message {\n private board: BoardCharArray;\n private currentLine: number | null = null;\n private isFilled = false;\n\n constructor(board: BoardCharArray = Message.newBoardCharArray()) {\n this.board = board;\n }\n\n static newBoardCharArray(): BoardCharArray {\n return JSON.parse(JSON.stringify(emptyBoard));\n }\n\n write(text: string, options: MessageWriteOptions = {}) : void{\n let firstLine = 0;\n if(this.isFilled) {\n return;\n }\n\n if(options.line === MessageWriteOptionsLine.CURRENT) {\n firstLine = this.currentLine || 0;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT && this.currentLine === null) {\n this.currentLine = 0;\n firstLine = this.currentLine;\n }\n else if(\n options.line === MessageWriteOptionsLine.NEXT &&\n typeof this.currentLine === 'number' &&\n this.currentLine < this.board.length - 1\n ) {\n // console.log('write() → current line + 1');\n this.currentLine++;\n firstLine = this.currentLine;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT) {\n this.isFilled = true;\n return;\n }\n else if(options.line !== undefined) {\n firstLine = options.line;\n }\n\n const status = {\n firstLine,\n lastLine: this.board.length - 1,\n firstRow: options.row || 0,\n lastRow: LINE_LENGTH - 1\n };\n const pointer = [status.firstRow, status.firstLine];\n\n const words = text.split(/\\s+/);\n words.forEach((word, i) => {\n\n // Space\n if(i !== 0 && pointer[0] !== status.firstRow) {\n this.board[pointer[1]][pointer[0]] = 0;\n pointer[0]++;\n }\n\n let charsLeftInLine = status.lastRow - pointer[0] + 1;\n const chars = Message.word2chars(word);\n // console.log(\n // 'write()',\n // 'chars =', chars,\n // 'left =', charsLeftInLine,\n // `(${status.lastRow} - ${pointer[0]})`,\n // 'pointer =', pointer,\n // 'charsLeftInLine =', charsLeftInLine\n // );\n\n // Unsupported Word / emoji?\n if(chars.filter(c => c === 60).length === chars.length && chars.length > 0) {\n return;\n }\n\n // New Line?\n if(\n chars.length > charsLeftInLine &&\n chars.length <= status.lastRow - status.firstRow + 1 &&\n pointer[1] < status.lastLine\n ) {\n pointer[0] = status.firstRow;\n pointer[1]++;\n charsLeftInLine = status.lastRow - pointer[0];\n // console.log('write() → new line', 'chars =', chars, 'left =', charsLeftInLine, `(${status.lastRow} - ${pointer[0]})`, 'pointer =', pointer);\n }\n\n // Add Word\n if(chars.length <= charsLeftInLine) {\n this.board[pointer[1]].splice(pointer[0], chars.length, ...chars);\n pointer[0] += chars.length;\n }\n else if(chars.length > charsLeftInLine && charsLeftInLine > 5) {\n this.board[pointer[1]].splice(pointer[0], charsLeftInLine, ...chars.slice(0, charsLeftInLine));\n pointer[0] += charsLeftInLine;\n }\n });\n this.currentLine = pointer[1];\n }\n\n static word2chars(word: string): number[] {\n const result: number[] = [];\n for(const char of word) {\n result.push(...this.char2char(char));\n }\n\n return result;\n }\n\n static char2char(char: string): number[] {\n const fromVestaMap = Object.entries(characterCode)\n .find(([key]) => char === key);\n if(fromVestaMap) {\n return [ fromVestaMap[1]] ;\n }\n\n const fromMyMap = SPECIAL_CHAR_MAP\n .find(([mapChar]) => char === mapChar);\n if(fromMyMap && Array.isArray(fromMyMap[1])) {\n return fromMyMap[1];\n }\n\n return [60];\n }\n\n repeat(char: string, options: MessageDrawOptions = {}): void {\n const line = this.board[options.line || 0];\n line.fill(Message.char2char(char)[0]);\n }\n\n centerLines(): void {\n this.board.forEach(line => {\n const space = [line.findIndex(c => c !== 0), line.slice().reverse().findIndex(c => c !== 0)];\n\n // Line is completely full, continue…\n if(space[0] === -1 || space[1] === -1) {\n return;\n }\n\n const content = line.slice(space[0], line.length - space[1]);\n const padding = Math.floor((space[0] + space[1]) / 2);\n\n line.fill(0, 0, padding);\n line.splice(padding, content.length, ...content);\n line.fill(0, padding + content.length);\n });\n }\n\n center(): void {\n const space = [\n this.board.findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.slice().reverse().findIndex(c => c !== 0) : l.length)),\n this.board.slice().reverse().findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.findIndex(c => c !== 0) : l.length)),\n ];\n\n const padding = [\n Math.floor((space[0] + space[2]) / 2),\n Math.floor((space[1] + space[3]) / 2)\n ];\n\n // Move up/down\n if(space[0] !== padding[0]) {\n const add = padding[0] - space[0];\n this.board.splice(space[0] < padding[0] ? 0 : this.board.length - add, 0,\n ...this.board.splice(this.board.length - add, add)\n );\n }\n\n // Move left/right\n if(space[3] !== padding[1]) {\n const add = padding[1] - space[3];\n\n this.board.forEach(line => {\n line.splice(space[3] < padding[1] ? 0 : line.length - add, 0,\n ...line.splice(line.length - add, add)\n );\n });\n }\n }\n\n isEmpty (): boolean {\n return !this.board.find(line =>\n line.find(char => char !== 0)\n );\n }\n\n toString(): string {\n return '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n' +\n this.board\n .map(line => '# ' + line.map(char => Message.charToString(char)).join('') + ' #\\n')\n .join('') +\n '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n';\n }\n\n static charToString(char: number): string {\n const entry = Object.entries(characterCode)\n .filter(([name]) => name.length <= 2)\n .find(([, code]) => code === char);\n\n if (entry) {\n return entry[0].toUpperCase() + ' ';\n }\n\n switch (char) {\n case 63:\n return '🟥';\n case 64:\n return '🟧';\n case 65:\n return '🟨';\n case 66:\n return '🟩';\n case 67:\n return '🟦';\n case 68:\n return '🟪';\n case 69:\n return '⬜️';\n default:\n return ' ';\n }\n }\n\n export(): BoardCharArray {\n return this.board;\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\n\nexport default class MessagePage implements Page<string> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): string {\n return payload || '';\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async render (content: string): Promise<PageRenderResponse> {\n const message = new Message();\n\n content.split('\\n').forEach(line => {\n message.write(line, {line: MessageWriteOptionsLine.NEXT});\n });\n\n message.centerLines();\n message.center();\n return {message};\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\nimport ical, { VEvent } from 'node-ical';\nimport Config from '../config.js';\nimport TodayPage, {TodayPagePayload} from './today.js';\n\n\nexport interface CalendarPagePayload {\n calendars: string[];\n}\n\nexport type CalendarPageItem = {start: Date, end: Date, summary: string};\n\nexport default class CalendarPage implements Page<CalendarPagePayload> {\n static readonly cache = new Cache('calendar');\n\n public parsePayload(payload: string | null): CalendarPagePayload {\n return {\n calendars: (payload || '').split(',')\n };\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async fetchURL(url: string): Promise<CalendarPageItem[]> {\n let preCached = await CalendarPage.cache.get<CalendarPageItem[]>(url, 1000 * 60 * 10);\n if(!preCached) {\n const calendar = await ical.async.fromURL(url);\n const events = Object.values(calendar)\n .filter(entry =>\n entry.type === 'VEVENT' &&\n entry.start &&\n entry.end &&\n entry.summary\n ) as VEvent[];\n preCached = events\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary).trim()\n }))\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime())\n .slice(0, 6);\n\n await CalendarPage.cache.set(url, preCached);\n }\n\n return preCached\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary)\n }))\n .filter(entry =>\n entry.end > new Date()\n );\n }\n\n public static isSameDay (a: Date, b: Date): boolean {\n const result = Boolean(\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n\n return result;\n }\n\n public static isMidnight (date: Date): boolean {\n const result = Boolean(\n !date.getHours() &&\n !date.getMinutes() &&\n !date.getSeconds() &&\n !date.getMilliseconds()\n );\n\n return result;\n }\n\n public async fetchURLs(urls: string[]): Promise<CalendarPageItem[]> {\n const rawResult: {start: Date, end: Date, summary: string}[][] = await Promise.all(urls.map(url => this.fetchURL(url)));\n return ([] as CalendarPageItem[])\n .concat(...rawResult)\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime());\n }\n\n public static getTimeStr (event: CalendarPageItem, oneLine = false): string {\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(), event.start)\n ) {\n return 'Heute';\n }\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), event.start)\n ) {\n return oneLine ? 'Morgen' : 'Morgn';\n }\n\n return event.start.getHours().toString().padStart(2, '⬛️') + ':' +\n event.start.getMinutes().toString().padStart(2, '0');\n }\n\n public async render (payload: CalendarPagePayload): Promise<PageRenderResponse> {\n const message = new Message();\n const urls = payload.calendars\n .map(id => Config.calendar.urls[id])\n .filter(Boolean);\n\n const calendar = await this.fetchURLs(urls)\n .then(calendar => calendar.filter(entry => {\n if (CalendarPage.isMidnight(entry.start) && CalendarPage.isMidnight(entry.end)) {\n return CalendarPage.isSameDay(new Date(), entry.start) || (\n new Date().getHours() >= 20 &&\n CalendarPage.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), entry.start)\n );\n } else {\n return entry.start < new Date(new Date().getTime() + (1000 * 60 * 60 * 12));\n }\n }));\n\n if(!calendar.length) {\n const today = new TodayPage();\n const pageConfigCache = new Cache('page-config');\n const config = await pageConfigCache.get<Partial<TodayPagePayload>>('today');\n return today.render({}, config || {});\n }\n\n let validTill = new Date(new Date().getTime() + 1000 * 60 * 10);\n calendar.forEach(event => {\n message.write(\n CalendarPage.getTimeStr(event, calendar.length === 1),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write(event.summary, {line: MessageWriteOptionsLine.CURRENT, row: 6});\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n if(event.end < validTill) {\n validTill = event.end;\n }\n });\n\n message.center();\n return {\n message,\n validTill\n };\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\nexport interface TodayPagePayload {\n temp?: [number, number];\n precip?: number;\n locale: string;\n}\n\nexport default class TodayPage implements Page<Partial<TodayPagePayload>, TodayPagePayload> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): Partial<TodayPagePayload> {\n try {\n return JSON.parse(payload || '') as Partial<TodayPagePayload>;\n }\n catch(error) {\n return {};\n }\n }\n\n parseConfig(key: string, value: string | null, config: Partial<TodayPagePayload>): Partial<TodayPagePayload> {\n const result: Partial<TodayPagePayload> = {};\n console.log('parseConfig', config);\n\n if(key === 'min-temp' && Array.isArray(config.temp)) {\n result.temp = [\n parseInt(value || ''),\n Math.max(...config.temp)\n ];\n }\n else if(key === 'min-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp' && Array.isArray(config.temp)) {\n result.temp = [\n Math.min(...config.temp),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'precip') {\n result.precip = parseInt(value || '', 10);\n }\n else if(key === 'locale') {\n result.locale = String(value);\n }\n\n return result;\n }\n\n public async render (payload: Partial<TodayPagePayload>, config: Partial<TodayPagePayload>): Promise<PageRenderResponse> {\n const message = new Message();\n const today = new Date();\n\n const mergedPayload: TodayPagePayload = Object.assign({\n temp: undefined,\n precip: undefined,\n locale: 'en'\n }, config, payload);\n\n message.write(today.toLocaleString(mergedPayload.locale, {weekday: 'long'}));\n message.write(\n today.toLocaleString(mergedPayload.locale, {day: 'numeric', month: 'long'}),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n const varContent = [];\n if(Array.isArray(mergedPayload.temp)) {\n varContent.push(`${Math.min(...mergedPayload.temp)}-${Math.max(...mergedPayload.temp)}°C`);\n }\n if(typeof mergedPayload.precip === 'number' && mergedPayload.precip > 0) {\n varContent.push(`${mergedPayload.precip}%`);\n }\n if(varContent.length) {\n message.write(varContent.join(', '), {line: MessageWriteOptionsLine.NEXT});\n }\n\n message.center();\n\n const validTill = new Date();\n validTill.setHours(24,0,0,0);\n\n return { message, validTill };\n }\n}\n","import Page from '../page.js';\nimport MessagePage from './message.js';\nimport CalendarPage from './calendar.js';\nimport TodayPage from './today.js';\n\nconst pages: Record<string, Page<unknown, unknown>> = {\n message: new MessagePage(),\n calendar: new CalendarPage(),\n today: new TodayPage()\n};\n\nexport default pages;\n","#!/usr/bin/env node\n'use strict';\n\nimport Vestaboard2MQTT from '../lib/index.js';\n\nVestaboard2MQTT.run().catch(error => {\n console.log(error);\n process.exit(1);\n});\n"],"mappings":"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","names":["import_async_mqtt","import_path","import_fs","readFile","writeFile","_Cache","namespace","__async","fetch","content","error","obj","key","maxAge","item","value","Cache","Config","_a","__async","data","newValue","_b","Cache","import_values","SPECIAL_CHAR_MAP","Message","_Message","board","text","options","firstLine","status","pointer","word","i","charsLeftInLine","chars","c","result","char","fromVestaMap","key","fromMyMap","mapChar","line","space","content","padding","l","add","entry","name","code","MessagePage","payload","content","__async","message","Message","line","Cache","import_node_ical","TodayPage","payload","error","key","value","config","result","__async","message","Message","today","mergedPayload","varContent","validTill","Cache","_CalendarPage","payload","url","__async","preCached","calendar","ical","entry","a","b","date","urls","rawResult","event","oneLine","message","Message","id","Config","today","TodayPage","config","Cache","validTill","CalendarPage","pages","MessagePage","CalendarPage","TodayPage","pages_default","import_vestaboard_api","Vestaboard2MQTT","_Vestaboard2MQTT","Cache","Config","mqtt","error","topic","message","__async","msg","payload","value","pages_default","id","page","key","cache","config","newConfig","pageEntry","pageInstance","parsePayload","response","Message","charArray","subscriptions","i","Vestaboard2MQTT","error"]}
|
package/dist/bin/start.d.cts
CHANGED
|
File without changes
|
package/dist/bin/start.d.ts
CHANGED
|
File without changes
|
package/dist/bin/start.js
CHANGED
|
@@ -5,4 +5,4 @@ var n=(u,e,a)=>new Promise((t,r)=>{var i=g=>{try{l(a.next(g))}catch(p){r(p)}},s=
|
|
|
5
5
|
`).forEach(t=>{a.write(t,{line:"NEXT"})}),a.centerLines(),a.center(),{message:a}})}};w.cache=new d("calendar");import O from"node-ical";var b=class{parsePayload(e){try{return JSON.parse(e||"")}catch(a){return{}}}parseConfig(e,a,t){let r={};return console.log("parseConfig",t),e==="min-temp"&&Array.isArray(t.temp)?r.temp=[parseInt(a||""),Math.max(...t.temp)]:e==="min-temp"?r.temp=[parseInt(a||""),parseInt(a||"")]:e==="max-temp"&&Array.isArray(t.temp)?r.temp=[Math.min(...t.temp),parseInt(a||"")]:e==="max-temp"?r.temp=[parseInt(a||""),parseInt(a||"")]:e==="precip"?r.precip=parseInt(a||"",10):e==="locale"&&(r.locale=String(a)),r}render(e,a){return n(this,null,function*(){let t=new m,r=new Date,i=Object.assign({temp:void 0,precip:void 0,locale:"en"},a,e);t.write(r.toLocaleString(i.locale,{weekday:"long"})),t.write(r.toLocaleString(i.locale,{day:"numeric",month:"long"}),{line:"NEXT"}),t.write("",{line:"NEXT"});let s=[];Array.isArray(i.temp)&&s.push(`${Math.min(...i.temp)}-${Math.max(...i.temp)}\xB0C`),typeof i.precip=="number"&&i.precip>0&&s.push(`${i.precip}%`),s.length&&t.write(s.join(", "),{line:"NEXT"}),t.center();let l=new Date;return l.setHours(24,0,0,0),{message:t,validTill:l}})}};b.cache=new d("calendar");var f=class f{parsePayload(e){return{calendars:(e||"").split(",")}}parseConfig(){return{}}fetchURL(e){return n(this,null,function*(){let a=yield f.cache.get(e,6e5);if(!a){let t=yield O.async.fromURL(e);a=Object.values(t).filter(i=>i.type==="VEVENT"&&i.start&&i.end&&i.summary).map(i=>({start:new Date(String(i.start)),end:new Date(String(i.end)),summary:String(i.summary).trim()})).filter(i=>i.end>new Date).sort((i,s)=>i.end.getTime()-s.end.getTime()).slice(0,6),yield f.cache.set(e,a)}return a.map(t=>({start:new Date(String(t.start)),end:new Date(String(t.end)),summary:String(t.summary)})).filter(t=>t.end>new Date)})}static isSameDay(e,a){return e.getFullYear()===a.getFullYear()&&e.getMonth()===a.getMonth()&&e.getDate()===a.getDate()}static isMidnight(e){return!e.getHours()&&!e.getMinutes()&&!e.getSeconds()&&!e.getMilliseconds()}fetchURLs(e){return n(this,null,function*(){let a=yield Promise.all(e.map(t=>this.fetchURL(t)));return[].concat(...a).filter(t=>t.end>new Date).sort((t,r)=>t.end.getTime()-r.end.getTime())})}static getTimeStr(e,a=!1){return this.isMidnight(e.start)&&this.isMidnight(e.end)&&this.isSameDay(new Date,e.start)?"Heute":this.isMidnight(e.start)&&this.isMidnight(e.end)&&this.isSameDay(new Date(new Date().getTime()+1e3*60*60*24),e.start)?a?"Morgen":"Morgn":e.start.getHours().toString().padStart(2,"\u2B1B\uFE0F")+":"+e.start.getMinutes().toString().padStart(2,"0")}render(e){return n(this,null,function*(){let a=new m,t=e.calendars.map(s=>o.calendar.urls[s]).filter(Boolean),r=yield this.fetchURLs(t).then(s=>s.filter(l=>f.isMidnight(l.start)&&f.isMidnight(l.end)?f.isSameDay(new Date,l.start)||new Date().getHours()>=20&&f.isSameDay(new Date(new Date().getTime()+1e3*60*60*24),l.start):l.start<new Date(new Date().getTime()+1e3*60*60*12)));if(!r.length){let s=new b,g=yield new d("page-config").get("today");return s.render({},g||{})}let i=new Date(new Date().getTime()+1e3*60*10);return r.forEach(s=>{a.write(f.getTimeStr(s,r.length===1),{line:"NEXT"}),a.write(s.summary,{line:"CURRENT",row:6}),a.write("",{line:"NEXT"}),s.end<i&&(i=s.end)}),a.center(),{message:a,validTill:i}})}};f.cache=new d("calendar");var y=f;var N={message:new w,calendar:new y,today:new b},P=N;import{Vesta as j}from"vestaboard-api";var D=class u{constructor(){this.pageConfig=new d("page-config");this.board=new j({apiKey:o.board.key,apiSecret:o.board.secret}),this.mqtt=U.connect(o.mqtt.url,{will:{topic:o.mqtt.prefix+"/status",payload:"offline",qos:0,retain:!0}}),this.mqtt.on("connect",()=>{this.mqtt.publish(o.mqtt.prefix+"/status","online",{retain:!0}),this.setupBrokerSubscriptions().catch(e=>{console.error(new Error(`Unable to setup mqtt subscriptions: ${e.stack}`)),process.exit(1)})}),this.mqtt.on("message",(e,a)=>{this.handleMessage(e,a.toString()).catch(t=>{this.debug(`Unable to handle message: ${t.stack}`)})})}static run(){return n(this,null,function*(){return yield o.load(),new u})}setupBrokerSubscriptions(){return n(this,null,function*(){if(!this.mqtt)throw new Error("Unable to setup subscriptions: client not set.");yield this.mqtt.subscribe(o.mqtt.prefix+"/#")})}debug(e){let a=String(e);this.mqtt.publish(o.mqtt.prefix+"/debug",a),console.log(a)}handleMessage(e,a){return n(this,null,function*(){if(![o.mqtt.prefix+"/debug",o.mqtt.prefix+"/status"].includes(e))if(e===o.mqtt.prefix+"/disabled"){let t=["1","true","TRUE"].includes(a);yield o.updateDisabled(t),t||(this.debug("Board not disabled anymore, update with latest message"),yield this.updateCurrentMessage())}else if(Object.keys(P).map(t=>o.mqtt.prefix+"/"+t).includes(e)){let t=P[e.substr(o.mqtt.prefix.length+1)];yield this.renderMessage(t,a)}else{let t=Object.entries(P).find(([g])=>e.startsWith(o.mqtt.prefix+"/"+g+"/"));if(!t){this.debug(`Unknown topic ${e}, I'm sorry`);return}let r=e.substr((o.mqtt.prefix+"/"+t[0]+"/").length),i=yield this.pageConfig.get(t[0]),s=t[1].parseConfig(r,a,i||{}),l=Object.assign(i||{},s);yield this.pageConfig.set(t[0],l),yield this.updateCurrentMessage(),this.debug(`Update config for page module ${t[0]}: ${JSON.stringify(l,null," ")}`)}})}updateCurrentMessage(){return n(this,null,function*(){this.currentMessage&&(yield this.renderMessage(this.currentMessage.page,this.currentMessage.payload))})}renderMessage(e,a){return n(this,null,function*(){this.currentMessage={page:e,payload:a};let t=Object.entries(P).find(([,l])=>l===e);if(o.board.disabled||!t)return;let r=yield e.parsePayload(a),i=yield this.pageConfig.get(t[0]),s=yield e.render(r,i||{});s.message&&(yield this.sendMessage(s.message)),this.currentTimer&&(clearTimeout(this.currentTimer),delete this.currentTimer),s.validTill&&(this.debug(`Set timer for ${s.validTill.toString()} to update message`),this.currentTimer=setTimeout(()=>{this.debug("Here I am again, updating the message now\u2026"),this.renderMessage(e,a).catch(l=>{this.debug(`Unable to update page: ${l}`)})},s.validTill.getTime()-new Date().getTime()))})}sendMessage(){return n(this,arguments,function*(e=new m){let a=e.export();if(!this.subscriptionIds){let t=yield this.board.getSubscriptions();this.subscriptionIds=t.map(r=>r._id)}this.debug(`Sending Message:
|
|
6
6
|
|
|
7
7
|
`+e.toString()),yield Promise.all(this.subscriptionIds.map(t=>this.board.postMessage(t,a)))})}};export{D as a};
|
|
8
|
-
//# sourceMappingURL=chunk-
|
|
8
|
+
//# sourceMappingURL=chunk-RW2UBCBQ.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/lib/index.ts","../src/lib/cache.ts","../src/lib/config.ts","../src/lib/message.ts","../src/lib/pages/message.ts","../src/lib/pages/calendar.ts","../src/lib/pages/today.ts","../src/lib/pages/index.ts"],"sourcesContent":["import mqtt from 'async-mqtt';\nimport Config from './config.js';\nimport Cache from './cache.js';\nimport { MqttClient } from 'mqtt';\nimport Message from './message.js';\nimport pages from './pages/index.js';\nimport Page from './page.js';\nimport { Vesta } from 'vestaboard-api';\n\nexport default class Vestaboard2MQTT {\n private readonly mqtt: MqttClient;\n private readonly board: Vesta;\n private readonly pageConfig = new Cache('page-config');\n private subscriptionIds?: string[];\n private currentMessage?: {page: Page<unknown>, payload: string};\n private currentTimer?: NodeJS.Timeout;\n\n public static async run(): Promise<Vestaboard2MQTT> {\n await Config.load();\n return new Vestaboard2MQTT();\n }\n\n constructor() {\n this.board = new Vesta({\n apiKey: Config.board.key,\n apiSecret: Config.board.secret\n });\n\n this.mqtt = mqtt.connect(Config.mqtt.url, {\n will: {\n topic: Config.mqtt.prefix + '/status',\n payload: 'offline',\n qos: 0,\n retain: true\n }\n });\n this.mqtt.on('connect', () => {\n this.mqtt.publish(Config.mqtt.prefix + '/status', 'online', {\n retain: true\n });\n\n this.setupBrokerSubscriptions().catch(error => {\n console.error(new Error(`Unable to setup mqtt subscriptions: ${error.stack}`));\n process.exit(1);\n });\n });\n this.mqtt.on('message', (topic, message) => {\n this.handleMessage(topic, message.toString()).catch(error => {\n this.debug(`Unable to handle message: ${error.stack}`);\n });\n });\n }\n\n private async setupBrokerSubscriptions() {\n if(!this.mqtt) {\n throw new Error('Unable to setup subscriptions: client not set.');\n }\n\n await this.mqtt.subscribe(Config.mqtt.prefix + '/#');\n }\n\n private debug(message: Error | string) {\n const msg = String(message);\n this.mqtt.publish(Config.mqtt.prefix + '/debug', msg);\n console.log(msg);\n }\n\n private async handleMessage(topic: string, payload: string): Promise<void> {\n if([Config.mqtt.prefix + '/debug', Config.mqtt.prefix + '/status'].includes(topic)) {\n // just ignore my own events\n }\n else if(topic === Config.mqtt.prefix + '/disabled') {\n const value = ['1', 'true', 'TRUE'].includes(payload);\n await Config.updateDisabled(value);\n if(!value) {\n this.debug('Board not disabled anymore, update with latest message');\n await this.updateCurrentMessage();\n }\n }\n else if(Object.keys(pages).map(id => Config.mqtt.prefix + '/' + id).includes(topic)) {\n const page = pages[ topic.substr(Config.mqtt.prefix.length + 1) ];\n await this.renderMessage(page, payload);\n }\n else {\n const page = Object.entries(pages)\n .find(([id]) => topic.startsWith(Config.mqtt.prefix + '/' + id + '/'));\n\n if(!page) {\n this.debug(`Unknown topic ${topic}, I'm sorry`);\n return;\n }\n\n const key = topic.substr((Config.mqtt.prefix + '/' + page[0] + '/').length);\n const cache = await this.pageConfig.get<Partial<unknown>>(page[0]);\n\n const config = page[1].parseConfig(key, payload, cache || {});\n const newConfig = Object.assign(cache || {}, config);\n await this.pageConfig.set(page[0], newConfig);\n await this.updateCurrentMessage();\n this.debug(`Update config for page module ${page[0]}: ${JSON.stringify(newConfig, null, ' ')}`);\n }\n }\n\n private async updateCurrentMessage() {\n if(this.currentMessage) {\n await this.renderMessage(\n this.currentMessage.page,\n this.currentMessage.payload\n );\n }\n }\n\n private async renderMessage(page: Page<unknown>, payload: string) {\n this.currentMessage = {\n page,\n payload\n };\n\n const pageEntry = Object.entries(pages).find(([,pageInstance]) => pageInstance === page);\n if(Config.board.disabled || !pageEntry) {\n return;\n }\n\n const parsePayload = await page.parsePayload(payload);\n const config = await this.pageConfig.get<Partial<unknown>>(pageEntry[0]);\n\n const response = await page.render(parsePayload, config || {});\n if (response.message) {\n await this.sendMessage(response.message);\n }\n\n if(this.currentTimer) {\n clearTimeout(this.currentTimer);\n delete this.currentTimer;\n }\n if(response.validTill) {\n this.debug(`Set timer for ${response.validTill.toString()} to update message`);\n this.currentTimer = setTimeout(() => {\n this.debug('Here I am again, updating the message now…');\n this.renderMessage(page, payload).catch(error => {\n this.debug(`Unable to update page: ${error}`);\n });\n }, response.validTill.getTime() - new Date().getTime());\n }\n }\n\n private async sendMessage(message: Message = new Message()): Promise<void> {\n const charArray = message.export();\n\n if(!this.subscriptionIds) {\n const subscriptions = await this.board.getSubscriptions();\n this.subscriptionIds = subscriptions.map(i => i._id);\n }\n\n this.debug('Sending Message:\\n\\n' + message.toString());\n\n await Promise.all(this.subscriptionIds.map(id => this.board.postMessage(id, charArray)));\n }\n}\n","import {join} from 'path';\nimport {existsSync} from 'fs';\nimport {promises} from 'fs';\n\ntype CacheData = Record<string, CacheNamespace>;\ntype CacheNamespace = Record<string, CacheItem>;\ntype CacheItem = CacheItemObject | CacheValue;\ntype CacheItemObject = {updated: number, value: CacheValue};\ntype CacheValue = unknown;\n\n// as node@12 has no 'fs/promises'\nconst {readFile, writeFile} = promises;\n\nexport default class Cache {\n private static readonly file: string = join(process.env.HOME || '~', '.vestaboard2mqtt');\n private readonly namespace: string;\n private static currentFetch?: Promise<void>;\n private static currentData?: CacheData = {};\n\n constructor(namespace: string) {\n this.namespace = namespace;\n }\n\n static async fetch(): Promise<void> {\n if(this.currentFetch) {\n return this.currentFetch;\n }\n\n const fetch = this.fetchFromFile();\n this.currentFetch = fetch;\n return fetch;\n }\n\n static async fetchFromFile (): Promise<void> {\n if(!existsSync(this.file)) {\n return;\n }\n\n try {\n const content = await readFile(this.file, {encoding: 'utf8'});\n this.currentData = JSON.parse(content);\n }\n catch(error) {\n console.warn(`Unable to parse cache file: ${error}`);\n }\n }\n\n static isCacheItemObject(obj: CacheItem): obj is CacheItemObject {\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return typeof obj === 'object' && typeof obj.updated === 'number' && typeof obj.value !== 'undefined';\n }\n\n async get<T>(key: string, maxAge = 0): Promise<T | null> {\n await Cache.fetch();\n\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace] ||\n !Cache.currentData[this.namespace][key]\n ) {\n return null;\n }\n\n const item = Cache.currentData[this.namespace][key];\n if(!Cache.isCacheItemObject(item) && maxAge > 0) {\n return null;\n }\n if(!Cache.isCacheItemObject(item)) {\n return item as T;\n }\n\n if(maxAge > 0 && new Date().getTime() - item.updated > maxAge) {\n return null;\n }\n\n return item.value as T;\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await Cache.fetch();\n\n if(!Cache.currentData) {\n throw new Error('Unable to set value: currentData is empty!');\n }\n\n Cache.currentData[this.namespace] = Cache.currentData[this.namespace] || {};\n Cache.currentData[this.namespace][key] = {\n updated: new Date().getTime(),\n value\n };\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n async delete(key: string): Promise<void> {\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace]\n ) {\n return;\n }\n\n delete Cache.currentData[this.namespace][key];\n\n if(Object.keys(Cache.currentData[this.namespace]).length === 0) {\n delete Cache.currentData[this.namespace];\n }\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n static async save(): Promise<void> {\n await writeFile(this.file, JSON.stringify(this.currentData, null, ' '));\n }\n}\n","import Cache from './cache.js';\n\nexport interface ConfigBoardData {\n id: string;\n key: string;\n secret: string;\n disabled: boolean;\n}\n\nexport interface ConfigMQTTData {\n url: string;\n prefix: string;\n}\n\nexport interface ConfigCalendarData {\n urls: Record<string, string>;\n cacheTTL: number;\n}\n\nexport interface ConfigData {\n board: ConfigBoardData;\n mqtt: ConfigMQTTData;\n calendar: ConfigCalendarData;\n}\n\nexport default class Config {\n private static readonly cache = new Cache('config');\n private static data?: ConfigData;\n\n public static get loaded(): boolean {\n return !!this.data && !!this.cache;\n }\n\n public static get board(): ConfigBoardData {\n if(this.loaded && this.data?.board) {\n return this.data.board;\n }\n\n throw new Error('Unable to access board config, is config loaded?');\n }\n\n public static get mqtt(): ConfigMQTTData {\n if(this.loaded && this.data?.mqtt) {\n return this.data.mqtt;\n }\n\n throw new Error('Unable to access mqtt config, is config loaded?');\n }\n\n public static get calendar(): ConfigCalendarData {\n if(this.loaded && this.data?.calendar) {\n return this.data.calendar;\n }\n\n throw new Error('Unable to access calendar config, is config loaded?');\n }\n\n public static async load(): Promise<void> {\n const data = await this.cache.get<ConfigData>('default');\n if(!data) {\n throw new Error('Unable to load configuration, try to run the setup script…');\n }\n\n this.data = data;\n }\n\n public static async save(data: ConfigData): Promise<void> {\n this.data = data;\n await this.cache.set('default', data);\n }\n\n public static async updateDisabled(newValue: boolean): Promise<void> {\n if(newValue === this.data?.board.disabled || !this.loaded || !this.data?.mqtt) {\n return;\n }\n\n this.data.board.disabled = newValue;\n await this.save(this.data);\n }\n}\n","import {BoardCharArray, characterCode, emptyBoard, LINE_LENGTH} from 'vestaboard-api/lib/cjs/values.js';\n\nexport enum MessageWriteOptionsLine {\n CURRENT = 'CURRENT',\n NEXT = 'NEXT'\n}\n\nexport interface MessageWriteOptions {\n line?: number | MessageWriteOptionsLine;\n row?: number;\n}\n\nexport interface MessageDrawOptions {\n line?: number;\n}\n\n\nconst SPECIAL_CHAR_MAP = [\n ['ä', [1, 5]],\n ['Ä', [1, 5]],\n ['ö', [15, 5]],\n ['Ö', [15, 5]],\n ['ü', [21, 5]],\n ['Ü', [21, 5]],\n ['ß', [19, 19]],\n ['🟥', [63]],\n ['🟧', [64]],\n ['🟨', [65]],\n ['🟩', [66]],\n ['🟦', [67]],\n ['🟪', [68]],\n ['⬜️', [69]],\n ['⬜', [69]],\n ['⬛️', [0]],\n ['⬛', [0]],\n ['﹫', [38]],\n ['@', [38]],\n ['0️⃣', [36]],\n ['1️⃣', [27]],\n ['2️⃣', [28]],\n ['3️⃣', [29]],\n ['4️⃣', [30]],\n ['5️⃣', [31]],\n ['6️⃣', [32]],\n ['7️⃣', [33]],\n ['8️⃣', [34]],\n ['9️⃣', [35]],\n ['❕', [37]],\n ['‼️', [37, 37]],\n ['❗️', [37]],\n ['⁉️', [37, 60]],\n ['⚠️', [65]],\n ['#️⃣', [39]],\n ['💸', [40]],\n ['💲', [40]],\n ['💵', [40]],\n ['💰', [40]],\n ['$', [40]],\n ['﹩', [40]],\n ['$', [40]],\n ['←', [44]],\n ['→', [44]],\n ['➡', [44]],\n ['⬅', [44]],\n ['➔', [44]],\n ['↔', [44]],\n ['–', [44]],\n ['➕', [46]],\n ['+', [46]],\n ['﹪', [54]],\n ['%', [54]],\n ['❓', [60]],\n ['❔', [60]],\n ['℃', [62, 3]],\n ['℉', [62, 6]]\n];\n\n\nexport default class Message {\n private board: BoardCharArray;\n private currentLine: number | null = null;\n private isFilled = false;\n\n constructor(board: BoardCharArray = Message.newBoardCharArray()) {\n this.board = board;\n }\n\n static newBoardCharArray(): BoardCharArray {\n return JSON.parse(JSON.stringify(emptyBoard));\n }\n\n write(text: string, options: MessageWriteOptions = {}) : void{\n let firstLine = 0;\n if(this.isFilled) {\n return;\n }\n\n if(options.line === MessageWriteOptionsLine.CURRENT) {\n firstLine = this.currentLine || 0;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT && this.currentLine === null) {\n this.currentLine = 0;\n firstLine = this.currentLine;\n }\n else if(\n options.line === MessageWriteOptionsLine.NEXT &&\n typeof this.currentLine === 'number' &&\n this.currentLine < this.board.length - 1\n ) {\n // console.log('write() → current line + 1');\n this.currentLine++;\n firstLine = this.currentLine;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT) {\n this.isFilled = true;\n return;\n }\n else if(options.line !== undefined) {\n firstLine = options.line;\n }\n\n const status = {\n firstLine,\n lastLine: this.board.length - 1,\n firstRow: options.row || 0,\n lastRow: LINE_LENGTH - 1\n };\n const pointer = [status.firstRow, status.firstLine];\n\n const words = text.split(/\\s+/);\n words.forEach((word, i) => {\n\n // Space\n if(i !== 0 && pointer[0] !== status.firstRow) {\n this.board[pointer[1]][pointer[0]] = 0;\n pointer[0]++;\n }\n\n let charsLeftInLine = status.lastRow - pointer[0] + 1;\n const chars = Message.word2chars(word);\n // console.log(\n // 'write()',\n // 'chars =', chars,\n // 'left =', charsLeftInLine,\n // `(${status.lastRow} - ${pointer[0]})`,\n // 'pointer =', pointer,\n // 'charsLeftInLine =', charsLeftInLine\n // );\n\n // Unsupported Word / emoji?\n if(chars.filter(c => c === 60).length === chars.length && chars.length > 0) {\n return;\n }\n\n // New Line?\n if(\n chars.length > charsLeftInLine &&\n chars.length <= status.lastRow - status.firstRow + 1 &&\n pointer[1] < status.lastLine\n ) {\n pointer[0] = status.firstRow;\n pointer[1]++;\n charsLeftInLine = status.lastRow - pointer[0];\n // console.log('write() → new line', 'chars =', chars, 'left =', charsLeftInLine, `(${status.lastRow} - ${pointer[0]})`, 'pointer =', pointer);\n }\n\n // Add Word\n if(chars.length <= charsLeftInLine) {\n this.board[pointer[1]].splice(pointer[0], chars.length, ...chars);\n pointer[0] += chars.length;\n }\n else if(chars.length > charsLeftInLine && charsLeftInLine > 5) {\n this.board[pointer[1]].splice(pointer[0], charsLeftInLine, ...chars.slice(0, charsLeftInLine));\n pointer[0] += charsLeftInLine;\n }\n });\n this.currentLine = pointer[1];\n }\n\n static word2chars(word: string): number[] {\n const result: number[] = [];\n for(const char of word) {\n result.push(...this.char2char(char));\n }\n\n return result;\n }\n\n static char2char(char: string): number[] {\n const fromVestaMap = Object.entries(characterCode)\n .find(([key]) => char === key);\n if(fromVestaMap) {\n return [ fromVestaMap[1]] ;\n }\n\n const fromMyMap = SPECIAL_CHAR_MAP\n .find(([mapChar]) => char === mapChar);\n if(fromMyMap && Array.isArray(fromMyMap[1])) {\n return fromMyMap[1];\n }\n\n return [60];\n }\n\n repeat(char: string, options: MessageDrawOptions = {}): void {\n const line = this.board[options.line || 0];\n line.fill(Message.char2char(char)[0]);\n }\n\n centerLines(): void {\n this.board.forEach(line => {\n const space = [line.findIndex(c => c !== 0), line.slice().reverse().findIndex(c => c !== 0)];\n\n // Line is completely full, continue…\n if(space[0] === -1 || space[1] === -1) {\n return;\n }\n\n const content = line.slice(space[0], line.length - space[1]);\n const padding = Math.floor((space[0] + space[1]) / 2);\n\n line.fill(0, 0, padding);\n line.splice(padding, content.length, ...content);\n line.fill(0, padding + content.length);\n });\n }\n\n center(): void {\n const space = [\n this.board.findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.slice().reverse().findIndex(c => c !== 0) : l.length)),\n this.board.slice().reverse().findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.findIndex(c => c !== 0) : l.length)),\n ];\n\n const padding = [\n Math.floor((space[0] + space[2]) / 2),\n Math.floor((space[1] + space[3]) / 2)\n ];\n\n // Move up/down\n if(space[0] !== padding[0]) {\n const add = padding[0] - space[0];\n this.board.splice(space[0] < padding[0] ? 0 : this.board.length - add, 0,\n ...this.board.splice(this.board.length - add, add)\n );\n }\n\n // Move left/right\n if(space[3] !== padding[1]) {\n const add = padding[1] - space[3];\n\n this.board.forEach(line => {\n line.splice(space[3] < padding[1] ? 0 : line.length - add, 0,\n ...line.splice(line.length - add, add)\n );\n });\n }\n }\n\n isEmpty (): boolean {\n return !this.board.find(line =>\n line.find(char => char !== 0)\n );\n }\n\n toString(): string {\n return '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n' +\n this.board\n .map(line => '# ' + line.map(char => Message.charToString(char)).join('') + ' #\\n')\n .join('') +\n '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n';\n }\n\n static charToString(char: number): string {\n const entry = Object.entries(characterCode)\n .filter(([name]) => name.length <= 2)\n .find(([, code]) => code === char);\n\n if (entry) {\n return entry[0].toUpperCase() + ' ';\n }\n\n switch (char) {\n case 63:\n return '🟥';\n case 64:\n return '🟧';\n case 65:\n return '🟨';\n case 66:\n return '🟩';\n case 67:\n return '🟦';\n case 68:\n return '🟪';\n case 69:\n return '⬜️';\n default:\n return ' ';\n }\n }\n\n export(): BoardCharArray {\n return this.board;\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\n\nexport default class MessagePage implements Page<string> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): string {\n return payload || '';\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async render (content: string): Promise<PageRenderResponse> {\n const message = new Message();\n\n content.split('\\n').forEach(line => {\n message.write(line, {line: MessageWriteOptionsLine.NEXT});\n });\n\n message.centerLines();\n message.center();\n return {message};\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\nimport ical, { VEvent } from 'node-ical';\nimport Config from '../config.js';\nimport TodayPage, {TodayPagePayload} from './today.js';\n\n\nexport interface CalendarPagePayload {\n calendars: string[];\n}\n\nexport type CalendarPageItem = {start: Date, end: Date, summary: string};\n\nexport default class CalendarPage implements Page<CalendarPagePayload> {\n static readonly cache = new Cache('calendar');\n\n public parsePayload(payload: string | null): CalendarPagePayload {\n return {\n calendars: (payload || '').split(',')\n };\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async fetchURL(url: string): Promise<CalendarPageItem[]> {\n let preCached = await CalendarPage.cache.get<CalendarPageItem[]>(url, 1000 * 60 * 10);\n if(!preCached) {\n const calendar = await ical.async.fromURL(url);\n const events = Object.values(calendar)\n .filter(entry =>\n entry.type === 'VEVENT' &&\n entry.start &&\n entry.end &&\n entry.summary\n ) as VEvent[];\n preCached = events\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary).trim()\n }))\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime())\n .slice(0, 6);\n\n await CalendarPage.cache.set(url, preCached);\n }\n\n return preCached\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary)\n }))\n .filter(entry =>\n entry.end > new Date()\n );\n }\n\n public static isSameDay (a: Date, b: Date): boolean {\n const result = Boolean(\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n\n return result;\n }\n\n public static isMidnight (date: Date): boolean {\n const result = Boolean(\n !date.getHours() &&\n !date.getMinutes() &&\n !date.getSeconds() &&\n !date.getMilliseconds()\n );\n\n return result;\n }\n\n public async fetchURLs(urls: string[]): Promise<CalendarPageItem[]> {\n const rawResult: {start: Date, end: Date, summary: string}[][] = await Promise.all(urls.map(url => this.fetchURL(url)));\n return ([] as CalendarPageItem[])\n .concat(...rawResult)\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime());\n }\n\n public static getTimeStr (event: CalendarPageItem, oneLine = false): string {\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(), event.start)\n ) {\n return 'Heute';\n }\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), event.start)\n ) {\n return oneLine ? 'Morgen' : 'Morgn';\n }\n\n return event.start.getHours().toString().padStart(2, '⬛️') + ':' +\n event.start.getMinutes().toString().padStart(2, '0');\n }\n\n public async render (payload: CalendarPagePayload): Promise<PageRenderResponse> {\n const message = new Message();\n const urls = payload.calendars\n .map(id => Config.calendar.urls[id])\n .filter(Boolean);\n\n const calendar = await this.fetchURLs(urls)\n .then(calendar => calendar.filter(entry => {\n if (CalendarPage.isMidnight(entry.start) && CalendarPage.isMidnight(entry.end)) {\n return CalendarPage.isSameDay(new Date(), entry.start) || (\n new Date().getHours() >= 20 &&\n CalendarPage.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), entry.start)\n );\n } else {\n return entry.start < new Date(new Date().getTime() + (1000 * 60 * 60 * 12));\n }\n }));\n\n if(!calendar.length) {\n const today = new TodayPage();\n const pageConfigCache = new Cache('page-config');\n const config = await pageConfigCache.get<Partial<TodayPagePayload>>('today');\n return today.render({}, config || {});\n }\n\n let validTill = new Date(new Date().getTime() + 1000 * 60 * 10);\n calendar.forEach(event => {\n message.write(\n CalendarPage.getTimeStr(event, calendar.length === 1),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write(event.summary, {line: MessageWriteOptionsLine.CURRENT, row: 6});\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n if(event.end < validTill) {\n validTill = event.end;\n }\n });\n\n message.center();\n return {\n message,\n validTill\n };\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\nexport interface TodayPagePayload {\n temp?: [number, number];\n precip?: number;\n locale: string;\n}\n\nexport default class TodayPage implements Page<Partial<TodayPagePayload>, TodayPagePayload> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): Partial<TodayPagePayload> {\n try {\n return JSON.parse(payload || '') as Partial<TodayPagePayload>;\n }\n catch(error) {\n return {};\n }\n }\n\n parseConfig(key: string, value: string | null, config: Partial<TodayPagePayload>): Partial<TodayPagePayload> {\n const result: Partial<TodayPagePayload> = {};\n console.log('parseConfig', config);\n\n if(key === 'min-temp' && Array.isArray(config.temp)) {\n result.temp = [\n parseInt(value || ''),\n Math.max(...config.temp)\n ];\n }\n else if(key === 'min-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp' && Array.isArray(config.temp)) {\n result.temp = [\n Math.min(...config.temp),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'precip') {\n result.precip = parseInt(value || '', 10);\n }\n else if(key === 'locale') {\n result.locale = String(value);\n }\n\n return result;\n }\n\n public async render (payload: Partial<TodayPagePayload>, config: Partial<TodayPagePayload>): Promise<PageRenderResponse> {\n const message = new Message();\n const today = new Date();\n\n const mergedPayload: TodayPagePayload = Object.assign({\n temp: undefined,\n precip: undefined,\n locale: 'en'\n }, config, payload);\n\n message.write(today.toLocaleString(mergedPayload.locale, {weekday: 'long'}));\n message.write(\n today.toLocaleString(mergedPayload.locale, {day: 'numeric', month: 'long'}),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n const varContent = [];\n if(Array.isArray(mergedPayload.temp)) {\n varContent.push(`${Math.min(...mergedPayload.temp)}-${Math.max(...mergedPayload.temp)}°C`);\n }\n if(typeof mergedPayload.precip === 'number' && mergedPayload.precip > 0) {\n varContent.push(`${mergedPayload.precip}%`);\n }\n if(varContent.length) {\n message.write(varContent.join(', '), {line: MessageWriteOptionsLine.NEXT});\n }\n\n message.center();\n\n const validTill = new Date();\n validTill.setHours(24,0,0,0);\n\n return { message, validTill };\n }\n}\n","import Page from '../page.js';\nimport MessagePage from './message.js';\nimport CalendarPage from './calendar.js';\nimport TodayPage from './today.js';\n\nconst pages: Record<string, Page<unknown, unknown>> = {\n message: new MessagePage(),\n calendar: new CalendarPage(),\n today: new TodayPage()\n};\n\nexport default pages;\n"],"mappings":"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","names":["mqtt","join","existsSync","promises","readFile","writeFile","promises","_Cache","namespace","__async","fetch","existsSync","content","error","obj","key","maxAge","item","value","join","Cache","Config","_a","__async","data","newValue","_b","Cache","characterCode","emptyBoard","LINE_LENGTH","SPECIAL_CHAR_MAP","Message","_Message","board","emptyBoard","text","options","firstLine","status","LINE_LENGTH","pointer","word","i","charsLeftInLine","chars","c","result","char","fromVestaMap","characterCode","key","fromMyMap","mapChar","line","space","content","padding","l","add","entry","name","code","MessagePage","payload","content","__async","message","Message","line","Cache","ical","TodayPage","payload","error","key","value","config","result","__async","message","Message","today","mergedPayload","varContent","validTill","Cache","_CalendarPage","payload","url","__async","preCached","calendar","ical","entry","a","b","date","urls","rawResult","event","oneLine","message","Message","id","Config","today","TodayPage","config","Cache","validTill","CalendarPage","pages","MessagePage","CalendarPage","TodayPage","pages_default","Vesta","Vestaboard2MQTT","_Vestaboard2MQTT","Cache","Vesta","Config","mqtt","error","topic","message","__async","msg","payload","value","pages_default","id","page","key","cache","config","newConfig","pageEntry","pageInstance","parsePayload","response","Message","charArray","subscriptions","i"]}
|
|
1
|
+
{"version":3,"sources":["../src/lib/index.ts","../src/lib/cache.ts","../src/lib/config.ts","../src/lib/message.ts","../src/lib/pages/message.ts","../src/lib/pages/calendar.ts","../src/lib/pages/today.ts","../src/lib/pages/index.ts"],"sourcesContent":["import mqtt from 'async-mqtt';\nimport Config from './config.js';\nimport Cache from './cache.js';\nimport { MqttClient } from 'mqtt';\nimport Message from './message.js';\nimport pages from './pages/index.js';\nimport Page from './page.js';\nimport { Vesta } from 'vestaboard-api';\n\nexport default class Vestaboard2MQTT {\n private readonly mqtt: MqttClient;\n private readonly board: Vesta;\n private readonly pageConfig = new Cache('page-config');\n private subscriptionIds?: string[];\n private currentMessage?: {page: Page<unknown>, payload: string};\n private currentTimer?: NodeJS.Timeout;\n\n public static async run(): Promise<Vestaboard2MQTT> {\n await Config.load();\n return new Vestaboard2MQTT();\n }\n\n constructor() {\n this.board = new Vesta({\n apiKey: Config.board.key,\n apiSecret: Config.board.secret\n });\n\n this.mqtt = mqtt.connect(Config.mqtt.url, {\n will: {\n topic: Config.mqtt.prefix + '/status',\n payload: 'offline',\n qos: 0,\n retain: true\n }\n });\n this.mqtt.on('connect', () => {\n this.mqtt.publish(Config.mqtt.prefix + '/status', 'online', {\n retain: true\n });\n\n this.setupBrokerSubscriptions().catch(error => {\n console.error(new Error(`Unable to setup mqtt subscriptions: ${error.stack}`));\n process.exit(1);\n });\n });\n this.mqtt.on('message', (topic, message) => {\n this.handleMessage(topic, message.toString()).catch(error => {\n this.debug(`Unable to handle message: ${error.stack}`);\n });\n });\n }\n\n private async setupBrokerSubscriptions() {\n if(!this.mqtt) {\n throw new Error('Unable to setup subscriptions: client not set.');\n }\n\n await this.mqtt.subscribe(Config.mqtt.prefix + '/#');\n }\n\n private debug(message: Error | string) {\n const msg = String(message);\n this.mqtt.publish(Config.mqtt.prefix + '/debug', msg);\n console.log(msg);\n }\n\n private async handleMessage(topic: string, payload: string): Promise<void> {\n if([Config.mqtt.prefix + '/debug', Config.mqtt.prefix + '/status'].includes(topic)) {\n // just ignore my own events\n }\n else if(topic === Config.mqtt.prefix + '/disabled') {\n const value = ['1', 'true', 'TRUE'].includes(payload);\n await Config.updateDisabled(value);\n if(!value) {\n this.debug('Board not disabled anymore, update with latest message');\n await this.updateCurrentMessage();\n }\n }\n else if(Object.keys(pages).map(id => Config.mqtt.prefix + '/' + id).includes(topic)) {\n const page = pages[ topic.substr(Config.mqtt.prefix.length + 1) ];\n await this.renderMessage(page, payload);\n }\n else {\n const page = Object.entries(pages)\n .find(([id]) => topic.startsWith(Config.mqtt.prefix + '/' + id + '/'));\n\n if(!page) {\n this.debug(`Unknown topic ${topic}, I'm sorry`);\n return;\n }\n\n const key = topic.substr((Config.mqtt.prefix + '/' + page[0] + '/').length);\n const cache = await this.pageConfig.get<Partial<unknown>>(page[0]);\n\n const config = page[1].parseConfig(key, payload, cache || {});\n const newConfig = Object.assign(cache || {}, config);\n await this.pageConfig.set(page[0], newConfig);\n await this.updateCurrentMessage();\n this.debug(`Update config for page module ${page[0]}: ${JSON.stringify(newConfig, null, ' ')}`);\n }\n }\n\n private async updateCurrentMessage() {\n if(this.currentMessage) {\n await this.renderMessage(\n this.currentMessage.page,\n this.currentMessage.payload\n );\n }\n }\n\n private async renderMessage(page: Page<unknown>, payload: string) {\n this.currentMessage = {\n page,\n payload\n };\n\n const pageEntry = Object.entries(pages).find(([,pageInstance]) => pageInstance === page);\n if(Config.board.disabled || !pageEntry) {\n return;\n }\n\n const parsePayload = await page.parsePayload(payload);\n const config = await this.pageConfig.get<Partial<unknown>>(pageEntry[0]);\n\n const response = await page.render(parsePayload, config || {});\n if (response.message) {\n await this.sendMessage(response.message);\n }\n\n if(this.currentTimer) {\n clearTimeout(this.currentTimer);\n delete this.currentTimer;\n }\n if(response.validTill) {\n this.debug(`Set timer for ${response.validTill.toString()} to update message`);\n this.currentTimer = setTimeout(() => {\n this.debug('Here I am again, updating the message now…');\n this.renderMessage(page, payload).catch(error => {\n this.debug(`Unable to update page: ${error}`);\n });\n }, response.validTill.getTime() - new Date().getTime());\n }\n }\n\n private async sendMessage(message: Message = new Message()): Promise<void> {\n const charArray = message.export();\n\n if(!this.subscriptionIds) {\n const subscriptions = await this.board.getSubscriptions();\n this.subscriptionIds = subscriptions.map(i => i._id);\n }\n\n this.debug('Sending Message:\\n\\n' + message.toString());\n\n await Promise.all(this.subscriptionIds.map(id => this.board.postMessage(id, charArray)));\n }\n}\n","import {join} from 'path';\nimport {existsSync} from 'fs';\nimport {promises} from 'fs';\n\ntype CacheData = Record<string, CacheNamespace>;\ntype CacheNamespace = Record<string, CacheItem>;\ntype CacheItem = CacheItemObject | CacheValue;\ntype CacheItemObject = {updated: number, value: CacheValue};\ntype CacheValue = unknown;\n\n// as node@12 has no 'fs/promises'\nconst {readFile, writeFile} = promises;\n\nexport default class Cache {\n private static readonly file: string = join(process.env.HOME || '~', '.vestaboard2mqtt');\n private readonly namespace: string;\n private static currentFetch?: Promise<void>;\n private static currentData?: CacheData = {};\n\n constructor(namespace: string) {\n this.namespace = namespace;\n }\n\n static async fetch(): Promise<void> {\n if(this.currentFetch) {\n return this.currentFetch;\n }\n\n const fetch = this.fetchFromFile();\n this.currentFetch = fetch;\n return fetch;\n }\n\n static async fetchFromFile (): Promise<void> {\n if(!existsSync(this.file)) {\n return;\n }\n\n try {\n const content = await readFile(this.file, {encoding: 'utf8'});\n this.currentData = JSON.parse(content);\n }\n catch(error) {\n console.warn(`Unable to parse cache file: ${error}`);\n }\n }\n\n static isCacheItemObject(obj: CacheItem): obj is CacheItemObject {\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return typeof obj === 'object' && typeof obj.updated === 'number' && typeof obj.value !== 'undefined';\n }\n\n async get<T>(key: string, maxAge = 0): Promise<T | null> {\n await Cache.fetch();\n\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace] ||\n !Cache.currentData[this.namespace][key]\n ) {\n return null;\n }\n\n const item = Cache.currentData[this.namespace][key];\n if(!Cache.isCacheItemObject(item) && maxAge > 0) {\n return null;\n }\n if(!Cache.isCacheItemObject(item)) {\n return item as T;\n }\n\n if(maxAge > 0 && new Date().getTime() - item.updated > maxAge) {\n return null;\n }\n\n return item.value as T;\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await Cache.fetch();\n\n if(!Cache.currentData) {\n throw new Error('Unable to set value: currentData is empty!');\n }\n\n Cache.currentData[this.namespace] = Cache.currentData[this.namespace] || {};\n Cache.currentData[this.namespace][key] = {\n updated: new Date().getTime(),\n value\n };\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n async delete(key: string): Promise<void> {\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace]\n ) {\n return;\n }\n\n delete Cache.currentData[this.namespace][key];\n\n if(Object.keys(Cache.currentData[this.namespace]).length === 0) {\n delete Cache.currentData[this.namespace];\n }\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n static async save(): Promise<void> {\n await writeFile(this.file, JSON.stringify(this.currentData, null, ' '));\n }\n}\n","import Cache from './cache.js';\n\nexport interface ConfigBoardData {\n id: string;\n key: string;\n secret: string;\n disabled: boolean;\n}\n\nexport interface ConfigMQTTData {\n url: string;\n prefix: string;\n}\n\nexport interface ConfigCalendarData {\n urls: Record<string, string>;\n cacheTTL: number;\n}\n\nexport interface ConfigData {\n board: ConfigBoardData;\n mqtt: ConfigMQTTData;\n calendar: ConfigCalendarData;\n}\n\nexport default class Config {\n private static readonly cache = new Cache('config');\n private static data?: ConfigData;\n\n public static get loaded(): boolean {\n return !!this.data && !!this.cache;\n }\n\n public static get board(): ConfigBoardData {\n if(this.loaded && this.data?.board) {\n return this.data.board;\n }\n\n throw new Error('Unable to access board config, is config loaded?');\n }\n\n public static get mqtt(): ConfigMQTTData {\n if(this.loaded && this.data?.mqtt) {\n return this.data.mqtt;\n }\n\n throw new Error('Unable to access mqtt config, is config loaded?');\n }\n\n public static get calendar(): ConfigCalendarData {\n if(this.loaded && this.data?.calendar) {\n return this.data.calendar;\n }\n\n throw new Error('Unable to access calendar config, is config loaded?');\n }\n\n public static async load(): Promise<void> {\n const data = await this.cache.get<ConfigData>('default');\n if(!data) {\n throw new Error('Unable to load configuration, try to run the setup script…');\n }\n\n this.data = data;\n }\n\n public static async save(data: ConfigData): Promise<void> {\n this.data = data;\n await this.cache.set('default', data);\n }\n\n public static async updateDisabled(newValue: boolean): Promise<void> {\n if(newValue === this.data?.board.disabled || !this.loaded || !this.data?.mqtt) {\n return;\n }\n\n this.data.board.disabled = newValue;\n await this.save(this.data);\n }\n}\n","import {BoardCharArray, characterCode, emptyBoard, LINE_LENGTH} from 'vestaboard-api/lib/cjs/values.js';\n\nexport enum MessageWriteOptionsLine {\n CURRENT = 'CURRENT',\n NEXT = 'NEXT'\n}\n\nexport interface MessageWriteOptions {\n line?: number | MessageWriteOptionsLine;\n row?: number;\n}\n\nexport interface MessageDrawOptions {\n line?: number;\n}\n\n\nconst SPECIAL_CHAR_MAP = [\n ['ä', [1, 5]],\n ['Ä', [1, 5]],\n ['ö', [15, 5]],\n ['Ö', [15, 5]],\n ['ü', [21, 5]],\n ['Ü', [21, 5]],\n ['ß', [19, 19]],\n ['🟥', [63]],\n ['🟧', [64]],\n ['🟨', [65]],\n ['🟩', [66]],\n ['🟦', [67]],\n ['🟪', [68]],\n ['⬜️', [69]],\n ['⬜', [69]],\n ['⬛️', [0]],\n ['⬛', [0]],\n ['﹫', [38]],\n ['@', [38]],\n ['0️⃣', [36]],\n ['1️⃣', [27]],\n ['2️⃣', [28]],\n ['3️⃣', [29]],\n ['4️⃣', [30]],\n ['5️⃣', [31]],\n ['6️⃣', [32]],\n ['7️⃣', [33]],\n ['8️⃣', [34]],\n ['9️⃣', [35]],\n ['❕', [37]],\n ['‼️', [37, 37]],\n ['❗️', [37]],\n ['⁉️', [37, 60]],\n ['⚠️', [65]],\n ['#️⃣', [39]],\n ['💸', [40]],\n ['💲', [40]],\n ['💵', [40]],\n ['💰', [40]],\n ['$', [40]],\n ['﹩', [40]],\n ['$', [40]],\n ['←', [44]],\n ['→', [44]],\n ['➡', [44]],\n ['⬅', [44]],\n ['➔', [44]],\n ['↔', [44]],\n ['–', [44]],\n ['➕', [46]],\n ['+', [46]],\n ['﹪', [54]],\n ['%', [54]],\n ['❓', [60]],\n ['❔', [60]],\n ['℃', [62, 3]],\n ['℉', [62, 6]]\n];\n\n\nexport default class Message {\n private board: BoardCharArray;\n private currentLine: number | null = null;\n private isFilled = false;\n\n constructor(board: BoardCharArray = Message.newBoardCharArray()) {\n this.board = board;\n }\n\n static newBoardCharArray(): BoardCharArray {\n return JSON.parse(JSON.stringify(emptyBoard));\n }\n\n write(text: string, options: MessageWriteOptions = {}) : void{\n let firstLine = 0;\n if(this.isFilled) {\n return;\n }\n\n if(options.line === MessageWriteOptionsLine.CURRENT) {\n firstLine = this.currentLine || 0;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT && this.currentLine === null) {\n this.currentLine = 0;\n firstLine = this.currentLine;\n }\n else if(\n options.line === MessageWriteOptionsLine.NEXT &&\n typeof this.currentLine === 'number' &&\n this.currentLine < this.board.length - 1\n ) {\n // console.log('write() → current line + 1');\n this.currentLine++;\n firstLine = this.currentLine;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT) {\n this.isFilled = true;\n return;\n }\n else if(options.line !== undefined) {\n firstLine = options.line;\n }\n\n const status = {\n firstLine,\n lastLine: this.board.length - 1,\n firstRow: options.row || 0,\n lastRow: LINE_LENGTH - 1\n };\n const pointer = [status.firstRow, status.firstLine];\n\n const words = text.split(/\\s+/);\n words.forEach((word, i) => {\n\n // Space\n if(i !== 0 && pointer[0] !== status.firstRow) {\n this.board[pointer[1]][pointer[0]] = 0;\n pointer[0]++;\n }\n\n let charsLeftInLine = status.lastRow - pointer[0] + 1;\n const chars = Message.word2chars(word);\n // console.log(\n // 'write()',\n // 'chars =', chars,\n // 'left =', charsLeftInLine,\n // `(${status.lastRow} - ${pointer[0]})`,\n // 'pointer =', pointer,\n // 'charsLeftInLine =', charsLeftInLine\n // );\n\n // Unsupported Word / emoji?\n if(chars.filter(c => c === 60).length === chars.length && chars.length > 0) {\n return;\n }\n\n // New Line?\n if(\n chars.length > charsLeftInLine &&\n chars.length <= status.lastRow - status.firstRow + 1 &&\n pointer[1] < status.lastLine\n ) {\n pointer[0] = status.firstRow;\n pointer[1]++;\n charsLeftInLine = status.lastRow - pointer[0];\n // console.log('write() → new line', 'chars =', chars, 'left =', charsLeftInLine, `(${status.lastRow} - ${pointer[0]})`, 'pointer =', pointer);\n }\n\n // Add Word\n if(chars.length <= charsLeftInLine) {\n this.board[pointer[1]].splice(pointer[0], chars.length, ...chars);\n pointer[0] += chars.length;\n }\n else if(chars.length > charsLeftInLine && charsLeftInLine > 5) {\n this.board[pointer[1]].splice(pointer[0], charsLeftInLine, ...chars.slice(0, charsLeftInLine));\n pointer[0] += charsLeftInLine;\n }\n });\n this.currentLine = pointer[1];\n }\n\n static word2chars(word: string): number[] {\n const result: number[] = [];\n for(const char of word) {\n result.push(...this.char2char(char));\n }\n\n return result;\n }\n\n static char2char(char: string): number[] {\n const fromVestaMap = Object.entries(characterCode)\n .find(([key]) => char === key);\n if(fromVestaMap) {\n return [ fromVestaMap[1]] ;\n }\n\n const fromMyMap = SPECIAL_CHAR_MAP\n .find(([mapChar]) => char === mapChar);\n if(fromMyMap && Array.isArray(fromMyMap[1])) {\n return fromMyMap[1];\n }\n\n return [60];\n }\n\n repeat(char: string, options: MessageDrawOptions = {}): void {\n const line = this.board[options.line || 0];\n line.fill(Message.char2char(char)[0]);\n }\n\n centerLines(): void {\n this.board.forEach(line => {\n const space = [line.findIndex(c => c !== 0), line.slice().reverse().findIndex(c => c !== 0)];\n\n // Line is completely full, continue…\n if(space[0] === -1 || space[1] === -1) {\n return;\n }\n\n const content = line.slice(space[0], line.length - space[1]);\n const padding = Math.floor((space[0] + space[1]) / 2);\n\n line.fill(0, 0, padding);\n line.splice(padding, content.length, ...content);\n line.fill(0, padding + content.length);\n });\n }\n\n center(): void {\n const space = [\n this.board.findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.slice().reverse().findIndex(c => c !== 0) : l.length)),\n this.board.slice().reverse().findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.findIndex(c => c !== 0) : l.length)),\n ];\n\n const padding = [\n Math.floor((space[0] + space[2]) / 2),\n Math.floor((space[1] + space[3]) / 2)\n ];\n\n // Move up/down\n if(space[0] !== padding[0]) {\n const add = padding[0] - space[0];\n this.board.splice(space[0] < padding[0] ? 0 : this.board.length - add, 0,\n ...this.board.splice(this.board.length - add, add)\n );\n }\n\n // Move left/right\n if(space[3] !== padding[1]) {\n const add = padding[1] - space[3];\n\n this.board.forEach(line => {\n line.splice(space[3] < padding[1] ? 0 : line.length - add, 0,\n ...line.splice(line.length - add, add)\n );\n });\n }\n }\n\n isEmpty (): boolean {\n return !this.board.find(line =>\n line.find(char => char !== 0)\n );\n }\n\n toString(): string {\n return '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n' +\n this.board\n .map(line => '# ' + line.map(char => Message.charToString(char)).join('') + ' #\\n')\n .join('') +\n '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n';\n }\n\n static charToString(char: number): string {\n const entry = Object.entries(characterCode)\n .filter(([name]) => name.length <= 2)\n .find(([, code]) => code === char);\n\n if (entry) {\n return entry[0].toUpperCase() + ' ';\n }\n\n switch (char) {\n case 63:\n return '🟥';\n case 64:\n return '🟧';\n case 65:\n return '🟨';\n case 66:\n return '🟩';\n case 67:\n return '🟦';\n case 68:\n return '🟪';\n case 69:\n return '⬜️';\n default:\n return ' ';\n }\n }\n\n export(): BoardCharArray {\n return this.board;\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\n\nexport default class MessagePage implements Page<string> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): string {\n return payload || '';\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async render (content: string): Promise<PageRenderResponse> {\n const message = new Message();\n\n content.split('\\n').forEach(line => {\n message.write(line, {line: MessageWriteOptionsLine.NEXT});\n });\n\n message.centerLines();\n message.center();\n return {message};\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\nimport ical, { VEvent } from 'node-ical';\nimport Config from '../config.js';\nimport TodayPage, {TodayPagePayload} from './today.js';\n\n\nexport interface CalendarPagePayload {\n calendars: string[];\n}\n\nexport type CalendarPageItem = {start: Date, end: Date, summary: string};\n\nexport default class CalendarPage implements Page<CalendarPagePayload> {\n static readonly cache = new Cache('calendar');\n\n public parsePayload(payload: string | null): CalendarPagePayload {\n return {\n calendars: (payload || '').split(',')\n };\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async fetchURL(url: string): Promise<CalendarPageItem[]> {\n let preCached = await CalendarPage.cache.get<CalendarPageItem[]>(url, 1000 * 60 * 10);\n if(!preCached) {\n const calendar = await ical.async.fromURL(url);\n const events = Object.values(calendar)\n .filter(entry =>\n entry.type === 'VEVENT' &&\n entry.start &&\n entry.end &&\n entry.summary\n ) as VEvent[];\n preCached = events\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary).trim()\n }))\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime())\n .slice(0, 6);\n\n await CalendarPage.cache.set(url, preCached);\n }\n\n return preCached\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary)\n }))\n .filter(entry =>\n entry.end > new Date()\n );\n }\n\n public static isSameDay (a: Date, b: Date): boolean {\n const result = Boolean(\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n\n return result;\n }\n\n public static isMidnight (date: Date): boolean {\n const result = Boolean(\n !date.getHours() &&\n !date.getMinutes() &&\n !date.getSeconds() &&\n !date.getMilliseconds()\n );\n\n return result;\n }\n\n public async fetchURLs(urls: string[]): Promise<CalendarPageItem[]> {\n const rawResult: {start: Date, end: Date, summary: string}[][] = await Promise.all(urls.map(url => this.fetchURL(url)));\n return ([] as CalendarPageItem[])\n .concat(...rawResult)\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime());\n }\n\n public static getTimeStr (event: CalendarPageItem, oneLine = false): string {\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(), event.start)\n ) {\n return 'Heute';\n }\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), event.start)\n ) {\n return oneLine ? 'Morgen' : 'Morgn';\n }\n\n return event.start.getHours().toString().padStart(2, '⬛️') + ':' +\n event.start.getMinutes().toString().padStart(2, '0');\n }\n\n public async render (payload: CalendarPagePayload): Promise<PageRenderResponse> {\n const message = new Message();\n const urls = payload.calendars\n .map(id => Config.calendar.urls[id])\n .filter(Boolean);\n\n const calendar = await this.fetchURLs(urls)\n .then(calendar => calendar.filter(entry => {\n if (CalendarPage.isMidnight(entry.start) && CalendarPage.isMidnight(entry.end)) {\n return CalendarPage.isSameDay(new Date(), entry.start) || (\n new Date().getHours() >= 20 &&\n CalendarPage.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), entry.start)\n );\n } else {\n return entry.start < new Date(new Date().getTime() + (1000 * 60 * 60 * 12));\n }\n }));\n\n if(!calendar.length) {\n const today = new TodayPage();\n const pageConfigCache = new Cache('page-config');\n const config = await pageConfigCache.get<Partial<TodayPagePayload>>('today');\n return today.render({}, config || {});\n }\n\n let validTill = new Date(new Date().getTime() + 1000 * 60 * 10);\n calendar.forEach(event => {\n message.write(\n CalendarPage.getTimeStr(event, calendar.length === 1),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write(event.summary, {line: MessageWriteOptionsLine.CURRENT, row: 6});\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n if(event.end < validTill) {\n validTill = event.end;\n }\n });\n\n message.center();\n return {\n message,\n validTill\n };\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\nexport interface TodayPagePayload {\n temp?: [number, number];\n precip?: number;\n locale: string;\n}\n\nexport default class TodayPage implements Page<Partial<TodayPagePayload>, TodayPagePayload> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): Partial<TodayPagePayload> {\n try {\n return JSON.parse(payload || '') as Partial<TodayPagePayload>;\n }\n catch(error) {\n return {};\n }\n }\n\n parseConfig(key: string, value: string | null, config: Partial<TodayPagePayload>): Partial<TodayPagePayload> {\n const result: Partial<TodayPagePayload> = {};\n console.log('parseConfig', config);\n\n if(key === 'min-temp' && Array.isArray(config.temp)) {\n result.temp = [\n parseInt(value || ''),\n Math.max(...config.temp)\n ];\n }\n else if(key === 'min-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp' && Array.isArray(config.temp)) {\n result.temp = [\n Math.min(...config.temp),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'precip') {\n result.precip = parseInt(value || '', 10);\n }\n else if(key === 'locale') {\n result.locale = String(value);\n }\n\n return result;\n }\n\n public async render (payload: Partial<TodayPagePayload>, config: Partial<TodayPagePayload>): Promise<PageRenderResponse> {\n const message = new Message();\n const today = new Date();\n\n const mergedPayload: TodayPagePayload = Object.assign({\n temp: undefined,\n precip: undefined,\n locale: 'en'\n }, config, payload);\n\n message.write(today.toLocaleString(mergedPayload.locale, {weekday: 'long'}));\n message.write(\n today.toLocaleString(mergedPayload.locale, {day: 'numeric', month: 'long'}),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n const varContent = [];\n if(Array.isArray(mergedPayload.temp)) {\n varContent.push(`${Math.min(...mergedPayload.temp)}-${Math.max(...mergedPayload.temp)}°C`);\n }\n if(typeof mergedPayload.precip === 'number' && mergedPayload.precip > 0) {\n varContent.push(`${mergedPayload.precip}%`);\n }\n if(varContent.length) {\n message.write(varContent.join(', '), {line: MessageWriteOptionsLine.NEXT});\n }\n\n message.center();\n\n const validTill = new Date();\n validTill.setHours(24,0,0,0);\n\n return { message, validTill };\n }\n}\n","import Page from '../page.js';\nimport MessagePage from './message.js';\nimport CalendarPage from './calendar.js';\nimport TodayPage from './today.js';\n\nconst pages: Record<string, Page<unknown, unknown>> = {\n message: new MessagePage(),\n calendar: new CalendarPage(),\n today: new TodayPage()\n};\n\nexport default pages;\n"],"mappings":"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","names":["mqtt","join","existsSync","promises","readFile","writeFile","promises","_Cache","namespace","__async","fetch","existsSync","content","error","obj","key","maxAge","item","value","join","Cache","Config","_a","__async","data","newValue","_b","Cache","characterCode","emptyBoard","LINE_LENGTH","SPECIAL_CHAR_MAP","Message","_Message","board","emptyBoard","text","options","firstLine","status","LINE_LENGTH","pointer","word","i","charsLeftInLine","chars","c","result","char","fromVestaMap","characterCode","key","fromMyMap","mapChar","line","space","content","padding","l","add","entry","name","code","MessagePage","payload","content","__async","message","Message","line","Cache","ical","TodayPage","payload","error","key","value","config","result","__async","message","Message","today","mergedPayload","varContent","validTill","Cache","_CalendarPage","payload","url","__async","preCached","calendar","ical","entry","a","b","date","urls","rawResult","event","oneLine","message","Message","id","Config","today","TodayPage","config","Cache","validTill","CalendarPage","pages","MessagePage","CalendarPage","TodayPage","pages_default","Vesta","Vestaboard2MQTT","_Vestaboard2MQTT","Cache","Vesta","Config","mqtt","error","topic","message","__async","msg","payload","value","pages_default","id","page","key","cache","config","newConfig","pageEntry","pageInstance","parsePayload","response","Message","charArray","subscriptions","i"]}
|
package/dist/lib/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/lib/index.ts","../../src/lib/cache.ts","../../src/lib/config.ts","../../src/lib/message.ts","../../src/lib/pages/message.ts","../../src/lib/pages/calendar.ts","../../src/lib/pages/today.ts","../../src/lib/pages/index.ts"],"sourcesContent":["import mqtt from 'async-mqtt';\nimport Config from './config.js';\nimport Cache from './cache.js';\nimport { MqttClient } from 'mqtt';\nimport Message from './message.js';\nimport pages from './pages/index.js';\nimport Page from './page.js';\nimport { Vesta } from 'vestaboard-api';\n\nexport default class Vestaboard2MQTT {\n private readonly mqtt: MqttClient;\n private readonly board: Vesta;\n private readonly pageConfig = new Cache('page-config');\n private subscriptionIds?: string[];\n private currentMessage?: {page: Page<unknown>, payload: string};\n private currentTimer?: NodeJS.Timeout;\n\n public static async run(): Promise<Vestaboard2MQTT> {\n await Config.load();\n return new Vestaboard2MQTT();\n }\n\n constructor() {\n this.board = new Vesta({\n apiKey: Config.board.key,\n apiSecret: Config.board.secret\n });\n\n this.mqtt = mqtt.connect(Config.mqtt.url, {\n will: {\n topic: Config.mqtt.prefix + '/status',\n payload: 'offline',\n qos: 0,\n retain: true\n }\n });\n this.mqtt.on('connect', () => {\n this.mqtt.publish(Config.mqtt.prefix + '/status', 'online', {\n retain: true\n });\n\n this.setupBrokerSubscriptions().catch(error => {\n console.error(new Error(`Unable to setup mqtt subscriptions: ${error.stack}`));\n process.exit(1);\n });\n });\n this.mqtt.on('message', (topic, message) => {\n this.handleMessage(topic, message.toString()).catch(error => {\n this.debug(`Unable to handle message: ${error.stack}`);\n });\n });\n }\n\n private async setupBrokerSubscriptions() {\n if(!this.mqtt) {\n throw new Error('Unable to setup subscriptions: client not set.');\n }\n\n await this.mqtt.subscribe(Config.mqtt.prefix + '/#');\n }\n\n private debug(message: Error | string) {\n const msg = String(message);\n this.mqtt.publish(Config.mqtt.prefix + '/debug', msg);\n console.log(msg);\n }\n\n private async handleMessage(topic: string, payload: string): Promise<void> {\n if([Config.mqtt.prefix + '/debug', Config.mqtt.prefix + '/status'].includes(topic)) {\n // just ignore my own events\n }\n else if(topic === Config.mqtt.prefix + '/disabled') {\n const value = ['1', 'true', 'TRUE'].includes(payload);\n await Config.updateDisabled(value);\n if(!value) {\n this.debug('Board not disabled anymore, update with latest message');\n await this.updateCurrentMessage();\n }\n }\n else if(Object.keys(pages).map(id => Config.mqtt.prefix + '/' + id).includes(topic)) {\n const page = pages[ topic.substr(Config.mqtt.prefix.length + 1) ];\n await this.renderMessage(page, payload);\n }\n else {\n const page = Object.entries(pages)\n .find(([id]) => topic.startsWith(Config.mqtt.prefix + '/' + id + '/'));\n\n if(!page) {\n this.debug(`Unknown topic ${topic}, I'm sorry`);\n return;\n }\n\n const key = topic.substr((Config.mqtt.prefix + '/' + page[0] + '/').length);\n const cache = await this.pageConfig.get<Partial<unknown>>(page[0]);\n\n const config = page[1].parseConfig(key, payload, cache || {});\n const newConfig = Object.assign(cache || {}, config);\n await this.pageConfig.set(page[0], newConfig);\n await this.updateCurrentMessage();\n this.debug(`Update config for page module ${page[0]}: ${JSON.stringify(newConfig, null, ' ')}`);\n }\n }\n\n private async updateCurrentMessage() {\n if(this.currentMessage) {\n await this.renderMessage(\n this.currentMessage.page,\n this.currentMessage.payload\n );\n }\n }\n\n private async renderMessage(page: Page<unknown>, payload: string) {\n this.currentMessage = {\n page,\n payload\n };\n\n const pageEntry = Object.entries(pages).find(([,pageInstance]) => pageInstance === page);\n if(Config.board.disabled || !pageEntry) {\n return;\n }\n\n const parsePayload = await page.parsePayload(payload);\n const config = await this.pageConfig.get<Partial<unknown>>(pageEntry[0]);\n\n const response = await page.render(parsePayload, config || {});\n if (response.message) {\n await this.sendMessage(response.message);\n }\n\n if(this.currentTimer) {\n clearTimeout(this.currentTimer);\n delete this.currentTimer;\n }\n if(response.validTill) {\n this.debug(`Set timer for ${response.validTill.toString()} to update message`);\n this.currentTimer = setTimeout(() => {\n this.debug('Here I am again, updating the message now…');\n this.renderMessage(page, payload).catch(error => {\n this.debug(`Unable to update page: ${error}`);\n });\n }, response.validTill.getTime() - new Date().getTime());\n }\n }\n\n private async sendMessage(message: Message = new Message()): Promise<void> {\n const charArray = message.export();\n\n if(!this.subscriptionIds) {\n const subscriptions = await this.board.getSubscriptions();\n this.subscriptionIds = subscriptions.map(i => i._id);\n }\n\n this.debug('Sending Message:\\n\\n' + message.toString());\n\n await Promise.all(this.subscriptionIds.map(id => this.board.postMessage(id, charArray)));\n }\n}\n","import {join} from 'path';\nimport {existsSync} from 'fs';\nimport {promises} from 'fs';\n\ntype CacheData = Record<string, CacheNamespace>;\ntype CacheNamespace = Record<string, CacheItem>;\ntype CacheItem = CacheItemObject | CacheValue;\ntype CacheItemObject = {updated: number, value: CacheValue};\ntype CacheValue = unknown;\n\n// as node@12 has no 'fs/promises'\nconst {readFile, writeFile} = promises;\n\nexport default class Cache {\n private static readonly file: string = join(process.env.HOME || '~', '.vestaboard2mqtt');\n private readonly namespace: string;\n private static currentFetch?: Promise<void>;\n private static currentData?: CacheData = {};\n\n constructor(namespace: string) {\n this.namespace = namespace;\n }\n\n static async fetch(): Promise<void> {\n if(this.currentFetch) {\n return this.currentFetch;\n }\n\n const fetch = this.fetchFromFile();\n this.currentFetch = fetch;\n return fetch;\n }\n\n static async fetchFromFile (): Promise<void> {\n if(!existsSync(this.file)) {\n return;\n }\n\n try {\n const content = await readFile(this.file, {encoding: 'utf8'});\n this.currentData = JSON.parse(content);\n }\n catch(error) {\n console.warn(`Unable to parse cache file: ${error}`);\n }\n }\n\n static isCacheItemObject(obj: CacheItem): obj is CacheItemObject {\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return typeof obj === 'object' && typeof obj.updated === 'number' && typeof obj.value !== 'undefined';\n }\n\n async get<T>(key: string, maxAge = 0): Promise<T | null> {\n await Cache.fetch();\n\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace] ||\n !Cache.currentData[this.namespace][key]\n ) {\n return null;\n }\n\n const item = Cache.currentData[this.namespace][key];\n if(!Cache.isCacheItemObject(item) && maxAge > 0) {\n return null;\n }\n if(!Cache.isCacheItemObject(item)) {\n return item as T;\n }\n\n if(maxAge > 0 && new Date().getTime() - item.updated > maxAge) {\n return null;\n }\n\n return item.value as T;\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await Cache.fetch();\n\n if(!Cache.currentData) {\n throw new Error('Unable to set value: currentData is empty!');\n }\n\n Cache.currentData[this.namespace] = Cache.currentData[this.namespace] || {};\n Cache.currentData[this.namespace][key] = {\n updated: new Date().getTime(),\n value\n };\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n async delete(key: string): Promise<void> {\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace]\n ) {\n return;\n }\n\n delete Cache.currentData[this.namespace][key];\n\n if(Object.keys(Cache.currentData[this.namespace]).length === 0) {\n delete Cache.currentData[this.namespace];\n }\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n static async save(): Promise<void> {\n await writeFile(this.file, JSON.stringify(this.currentData, null, ' '));\n }\n}\n","import Cache from './cache.js';\n\nexport interface ConfigBoardData {\n id: string;\n key: string;\n secret: string;\n disabled: boolean;\n}\n\nexport interface ConfigMQTTData {\n url: string;\n prefix: string;\n}\n\nexport interface ConfigCalendarData {\n urls: Record<string, string>;\n cacheTTL: number;\n}\n\nexport interface ConfigData {\n board: ConfigBoardData;\n mqtt: ConfigMQTTData;\n calendar: ConfigCalendarData;\n}\n\nexport default class Config {\n private static readonly cache = new Cache('config');\n private static data?: ConfigData;\n\n public static get loaded(): boolean {\n return !!this.data && !!this.cache;\n }\n\n public static get board(): ConfigBoardData {\n if(this.loaded && this.data?.board) {\n return this.data.board;\n }\n\n throw new Error('Unable to access board config, is config loaded?');\n }\n\n public static get mqtt(): ConfigMQTTData {\n if(this.loaded && this.data?.mqtt) {\n return this.data.mqtt;\n }\n\n throw new Error('Unable to access mqtt config, is config loaded?');\n }\n\n public static get calendar(): ConfigCalendarData {\n if(this.loaded && this.data?.calendar) {\n return this.data.calendar;\n }\n\n throw new Error('Unable to access calendar config, is config loaded?');\n }\n\n public static async load(): Promise<void> {\n const data = await this.cache.get<ConfigData>('default');\n if(!data) {\n throw new Error('Unable to load configuration, try to run the setup script…');\n }\n\n this.data = data;\n }\n\n public static async save(data: ConfigData): Promise<void> {\n this.data = data;\n await this.cache.set('default', data);\n }\n\n public static async updateDisabled(newValue: boolean): Promise<void> {\n if(newValue === this.data?.board.disabled || !this.loaded || !this.data?.mqtt) {\n return;\n }\n\n this.data.board.disabled = newValue;\n await this.save(this.data);\n }\n}\n","import {BoardCharArray, characterCode, emptyBoard, LINE_LENGTH} from 'vestaboard-api/lib/cjs/values.js';\n\nexport enum MessageWriteOptionsLine {\n CURRENT = 'CURRENT',\n NEXT = 'NEXT'\n}\n\nexport interface MessageWriteOptions {\n line?: number | MessageWriteOptionsLine;\n row?: number;\n}\n\nexport interface MessageDrawOptions {\n line?: number;\n}\n\n\nconst SPECIAL_CHAR_MAP = [\n ['ä', [1, 5]],\n ['Ä', [1, 5]],\n ['ö', [15, 5]],\n ['Ö', [15, 5]],\n ['ü', [21, 5]],\n ['Ü', [21, 5]],\n ['ß', [19, 19]],\n ['🟥', [63]],\n ['🟧', [64]],\n ['🟨', [65]],\n ['🟩', [66]],\n ['🟦', [67]],\n ['🟪', [68]],\n ['⬜️', [69]],\n ['⬜', [69]],\n ['⬛️', [0]],\n ['⬛', [0]],\n ['﹫', [38]],\n ['@', [38]],\n ['0️⃣', [36]],\n ['1️⃣', [27]],\n ['2️⃣', [28]],\n ['3️⃣', [29]],\n ['4️⃣', [30]],\n ['5️⃣', [31]],\n ['6️⃣', [32]],\n ['7️⃣', [33]],\n ['8️⃣', [34]],\n ['9️⃣', [35]],\n ['❕', [37]],\n ['‼️', [37, 37]],\n ['❗️', [37]],\n ['⁉️', [37, 60]],\n ['⚠️', [65]],\n ['#️⃣', [39]],\n ['💸', [40]],\n ['💲', [40]],\n ['💵', [40]],\n ['💰', [40]],\n ['$', [40]],\n ['﹩', [40]],\n ['$', [40]],\n ['←', [44]],\n ['→', [44]],\n ['➡', [44]],\n ['⬅', [44]],\n ['➔', [44]],\n ['↔', [44]],\n ['–', [44]],\n ['➕', [46]],\n ['+', [46]],\n ['﹪', [54]],\n ['%', [54]],\n ['❓', [60]],\n ['❔', [60]],\n ['℃', [62, 3]],\n ['℉', [62, 6]]\n];\n\n\nexport default class Message {\n private board: BoardCharArray;\n private currentLine: number | null = null;\n private isFilled = false;\n\n constructor(board: BoardCharArray = Message.newBoardCharArray()) {\n this.board = board;\n }\n\n static newBoardCharArray(): BoardCharArray {\n return JSON.parse(JSON.stringify(emptyBoard));\n }\n\n write(text: string, options: MessageWriteOptions = {}) : void{\n let firstLine = 0;\n if(this.isFilled) {\n return;\n }\n\n if(options.line === MessageWriteOptionsLine.CURRENT) {\n firstLine = this.currentLine || 0;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT && this.currentLine === null) {\n this.currentLine = 0;\n firstLine = this.currentLine;\n }\n else if(\n options.line === MessageWriteOptionsLine.NEXT &&\n typeof this.currentLine === 'number' &&\n this.currentLine < this.board.length - 1\n ) {\n // console.log('write() → current line + 1');\n this.currentLine++;\n firstLine = this.currentLine;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT) {\n this.isFilled = true;\n return;\n }\n else if(options.line !== undefined) {\n firstLine = options.line;\n }\n\n const status = {\n firstLine,\n lastLine: this.board.length - 1,\n firstRow: options.row || 0,\n lastRow: LINE_LENGTH - 1\n };\n const pointer = [status.firstRow, status.firstLine];\n\n const words = text.split(/\\s+/);\n words.forEach((word, i) => {\n\n // Space\n if(i !== 0 && pointer[0] !== status.firstRow) {\n this.board[pointer[1]][pointer[0]] = 0;\n pointer[0]++;\n }\n\n let charsLeftInLine = status.lastRow - pointer[0] + 1;\n const chars = Message.word2chars(word);\n // console.log(\n // 'write()',\n // 'chars =', chars,\n // 'left =', charsLeftInLine,\n // `(${status.lastRow} - ${pointer[0]})`,\n // 'pointer =', pointer,\n // 'charsLeftInLine =', charsLeftInLine\n // );\n\n // Unsupported Word / emoji?\n if(chars.filter(c => c === 60).length === chars.length && chars.length > 0) {\n return;\n }\n\n // New Line?\n if(\n chars.length > charsLeftInLine &&\n chars.length <= status.lastRow - status.firstRow + 1 &&\n pointer[1] < status.lastLine\n ) {\n pointer[0] = status.firstRow;\n pointer[1]++;\n charsLeftInLine = status.lastRow - pointer[0];\n // console.log('write() → new line', 'chars =', chars, 'left =', charsLeftInLine, `(${status.lastRow} - ${pointer[0]})`, 'pointer =', pointer);\n }\n\n // Add Word\n if(chars.length <= charsLeftInLine) {\n this.board[pointer[1]].splice(pointer[0], chars.length, ...chars);\n pointer[0] += chars.length;\n }\n else if(chars.length > charsLeftInLine && charsLeftInLine > 5) {\n this.board[pointer[1]].splice(pointer[0], charsLeftInLine, ...chars.slice(0, charsLeftInLine));\n pointer[0] += charsLeftInLine;\n }\n });\n this.currentLine = pointer[1];\n }\n\n static word2chars(word: string): number[] {\n const result: number[] = [];\n for(const char of word) {\n result.push(...this.char2char(char));\n }\n\n return result;\n }\n\n static char2char(char: string): number[] {\n const fromVestaMap = Object.entries(characterCode)\n .find(([key]) => char === key);\n if(fromVestaMap) {\n return [ fromVestaMap[1]] ;\n }\n\n const fromMyMap = SPECIAL_CHAR_MAP\n .find(([mapChar]) => char === mapChar);\n if(fromMyMap && Array.isArray(fromMyMap[1])) {\n return fromMyMap[1];\n }\n\n return [60];\n }\n\n repeat(char: string, options: MessageDrawOptions = {}): void {\n const line = this.board[options.line || 0];\n line.fill(Message.char2char(char)[0]);\n }\n\n centerLines(): void {\n this.board.forEach(line => {\n const space = [line.findIndex(c => c !== 0), line.slice().reverse().findIndex(c => c !== 0)];\n\n // Line is completely full, continue…\n if(space[0] === -1 || space[1] === -1) {\n return;\n }\n\n const content = line.slice(space[0], line.length - space[1]);\n const padding = Math.floor((space[0] + space[1]) / 2);\n\n line.fill(0, 0, padding);\n line.splice(padding, content.length, ...content);\n line.fill(0, padding + content.length);\n });\n }\n\n center(): void {\n const space = [\n this.board.findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.slice().reverse().findIndex(c => c !== 0) : l.length)),\n this.board.slice().reverse().findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.findIndex(c => c !== 0) : l.length)),\n ];\n\n const padding = [\n Math.floor((space[0] + space[2]) / 2),\n Math.floor((space[1] + space[3]) / 2)\n ];\n\n // Move up/down\n if(space[0] !== padding[0]) {\n const add = padding[0] - space[0];\n this.board.splice(space[0] < padding[0] ? 0 : this.board.length - add, 0,\n ...this.board.splice(this.board.length - add, add)\n );\n }\n\n // Move left/right\n if(space[3] !== padding[1]) {\n const add = padding[1] - space[3];\n\n this.board.forEach(line => {\n line.splice(space[3] < padding[1] ? 0 : line.length - add, 0,\n ...line.splice(line.length - add, add)\n );\n });\n }\n }\n\n isEmpty (): boolean {\n return !this.board.find(line =>\n line.find(char => char !== 0)\n );\n }\n\n toString(): string {\n return '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n' +\n this.board\n .map(line => '# ' + line.map(char => Message.charToString(char)).join('') + ' #\\n')\n .join('') +\n '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n';\n }\n\n static charToString(char: number): string {\n const entry = Object.entries(characterCode)\n .filter(([name]) => name.length <= 2)\n .find(([, code]) => code === char);\n\n if (entry) {\n return entry[0].toUpperCase() + ' ';\n }\n\n switch (char) {\n case 63:\n return '🟥';\n case 64:\n return '🟧';\n case 65:\n return '🟨';\n case 66:\n return '🟩';\n case 67:\n return '🟦';\n case 68:\n return '🟪';\n case 69:\n return '⬜️';\n default:\n return ' ';\n }\n }\n\n export(): BoardCharArray {\n return this.board;\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\n\nexport default class MessagePage implements Page<string> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): string {\n return payload || '';\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async render (content: string): Promise<PageRenderResponse> {\n const message = new Message();\n\n content.split('\\n').forEach(line => {\n message.write(line, {line: MessageWriteOptionsLine.NEXT});\n });\n\n message.centerLines();\n message.center();\n return {message};\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\nimport ical, { VEvent } from 'node-ical';\nimport Config from '../config.js';\nimport TodayPage, {TodayPagePayload} from './today.js';\n\n\nexport interface CalendarPagePayload {\n calendars: string[];\n}\n\nexport type CalendarPageItem = {start: Date, end: Date, summary: string};\n\nexport default class CalendarPage implements Page<CalendarPagePayload> {\n static readonly cache = new Cache('calendar');\n\n public parsePayload(payload: string | null): CalendarPagePayload {\n return {\n calendars: (payload || '').split(',')\n };\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async fetchURL(url: string): Promise<CalendarPageItem[]> {\n let preCached = await CalendarPage.cache.get<CalendarPageItem[]>(url, 1000 * 60 * 10);\n if(!preCached) {\n const calendar = await ical.async.fromURL(url);\n const events = Object.values(calendar)\n .filter(entry =>\n entry.type === 'VEVENT' &&\n entry.start &&\n entry.end &&\n entry.summary\n ) as VEvent[];\n preCached = events\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary).trim()\n }))\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime())\n .slice(0, 6);\n\n await CalendarPage.cache.set(url, preCached);\n }\n\n return preCached\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary)\n }))\n .filter(entry =>\n entry.end > new Date()\n );\n }\n\n public static isSameDay (a: Date, b: Date): boolean {\n const result = Boolean(\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n\n return result;\n }\n\n public static isMidnight (date: Date): boolean {\n const result = Boolean(\n !date.getHours() &&\n !date.getMinutes() &&\n !date.getSeconds() &&\n !date.getMilliseconds()\n );\n\n return result;\n }\n\n public async fetchURLs(urls: string[]): Promise<CalendarPageItem[]> {\n const rawResult: {start: Date, end: Date, summary: string}[][] = await Promise.all(urls.map(url => this.fetchURL(url)));\n return ([] as CalendarPageItem[])\n .concat(...rawResult)\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime());\n }\n\n public static getTimeStr (event: CalendarPageItem, oneLine = false): string {\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(), event.start)\n ) {\n return 'Heute';\n }\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), event.start)\n ) {\n return oneLine ? 'Morgen' : 'Morgn';\n }\n\n return event.start.getHours().toString().padStart(2, '⬛️') + ':' +\n event.start.getMinutes().toString().padStart(2, '0');\n }\n\n public async render (payload: CalendarPagePayload): Promise<PageRenderResponse> {\n const message = new Message();\n const urls = payload.calendars\n .map(id => Config.calendar.urls[id])\n .filter(Boolean);\n\n const calendar = await this.fetchURLs(urls)\n .then(calendar => calendar.filter(entry => {\n if (CalendarPage.isMidnight(entry.start) && CalendarPage.isMidnight(entry.end)) {\n return CalendarPage.isSameDay(new Date(), entry.start) || (\n new Date().getHours() >= 20 &&\n CalendarPage.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), entry.start)\n );\n } else {\n return entry.start < new Date(new Date().getTime() + (1000 * 60 * 60 * 12));\n }\n }));\n\n if(!calendar.length) {\n const today = new TodayPage();\n const pageConfigCache = new Cache('page-config');\n const config = await pageConfigCache.get<Partial<TodayPagePayload>>('today');\n return today.render({}, config || {});\n }\n\n let validTill = new Date(new Date().getTime() + 1000 * 60 * 10);\n calendar.forEach(event => {\n message.write(\n CalendarPage.getTimeStr(event, calendar.length === 1),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write(event.summary, {line: MessageWriteOptionsLine.CURRENT, row: 6});\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n if(event.end < validTill) {\n validTill = event.end;\n }\n });\n\n message.center();\n return {\n message,\n validTill\n };\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\nexport interface TodayPagePayload {\n temp?: [number, number];\n precip?: number;\n locale: string;\n}\n\nexport default class TodayPage implements Page<Partial<TodayPagePayload>, TodayPagePayload> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): Partial<TodayPagePayload> {\n try {\n return JSON.parse(payload || '') as Partial<TodayPagePayload>;\n }\n catch(error) {\n return {};\n }\n }\n\n parseConfig(key: string, value: string | null, config: Partial<TodayPagePayload>): Partial<TodayPagePayload> {\n const result: Partial<TodayPagePayload> = {};\n console.log('parseConfig', config);\n\n if(key === 'min-temp' && Array.isArray(config.temp)) {\n result.temp = [\n parseInt(value || ''),\n Math.max(...config.temp)\n ];\n }\n else if(key === 'min-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp' && Array.isArray(config.temp)) {\n result.temp = [\n Math.min(...config.temp),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'precip') {\n result.precip = parseInt(value || '', 10);\n }\n else if(key === 'locale') {\n result.locale = String(value);\n }\n\n return result;\n }\n\n public async render (payload: Partial<TodayPagePayload>, config: Partial<TodayPagePayload>): Promise<PageRenderResponse> {\n const message = new Message();\n const today = new Date();\n\n const mergedPayload: TodayPagePayload = Object.assign({\n temp: undefined,\n precip: undefined,\n locale: 'en'\n }, config, payload);\n\n message.write(today.toLocaleString(mergedPayload.locale, {weekday: 'long'}));\n message.write(\n today.toLocaleString(mergedPayload.locale, {day: 'numeric', month: 'long'}),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n const varContent = [];\n if(Array.isArray(mergedPayload.temp)) {\n varContent.push(`${Math.min(...mergedPayload.temp)}-${Math.max(...mergedPayload.temp)}°C`);\n }\n if(typeof mergedPayload.precip === 'number' && mergedPayload.precip > 0) {\n varContent.push(`${mergedPayload.precip}%`);\n }\n if(varContent.length) {\n message.write(varContent.join(', '), {line: MessageWriteOptionsLine.NEXT});\n }\n\n message.center();\n\n const validTill = new Date();\n validTill.setHours(24,0,0,0);\n\n return { message, validTill };\n }\n}\n","import Page from '../page.js';\nimport MessagePage from './message.js';\nimport CalendarPage from './calendar.js';\nimport TodayPage from './today.js';\n\nconst pages: Record<string, Page<unknown, unknown>> = {\n message: new MessagePage(),\n calendar: new CalendarPage(),\n today: new TodayPage()\n};\n\nexport default pages;\n"],"mappings":"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","names":["lib_exports","__export","Vestaboard2MQTT","__toCommonJS","import_async_mqtt","import_path","import_fs","readFile","writeFile","_Cache","namespace","__async","fetch","content","error","obj","key","maxAge","item","value","Cache","Config","_a","__async","data","newValue","_b","Cache","import_values","SPECIAL_CHAR_MAP","Message","_Message","board","text","options","firstLine","status","pointer","word","i","charsLeftInLine","chars","c","result","char","fromVestaMap","key","fromMyMap","mapChar","line","space","content","padding","l","add","entry","name","code","MessagePage","payload","content","__async","message","Message","line","Cache","import_node_ical","TodayPage","payload","error","key","value","config","result","__async","message","Message","today","mergedPayload","varContent","validTill","Cache","_CalendarPage","payload","url","__async","preCached","calendar","ical","entry","a","b","date","urls","rawResult","event","oneLine","message","Message","id","Config","today","TodayPage","config","Cache","validTill","CalendarPage","pages","MessagePage","CalendarPage","TodayPage","pages_default","import_vestaboard_api","Vestaboard2MQTT","_Vestaboard2MQTT","Cache","Config","mqtt","error","topic","message","__async","msg","payload","value","pages_default","id","page","key","cache","config","newConfig","pageEntry","pageInstance","parsePayload","response","Message","charArray","subscriptions","i"]}
|
|
1
|
+
{"version":3,"sources":["../../src/lib/index.ts","../../src/lib/cache.ts","../../src/lib/config.ts","../../src/lib/message.ts","../../src/lib/pages/message.ts","../../src/lib/pages/calendar.ts","../../src/lib/pages/today.ts","../../src/lib/pages/index.ts"],"sourcesContent":["import mqtt from 'async-mqtt';\nimport Config from './config.js';\nimport Cache from './cache.js';\nimport { MqttClient } from 'mqtt';\nimport Message from './message.js';\nimport pages from './pages/index.js';\nimport Page from './page.js';\nimport { Vesta } from 'vestaboard-api';\n\nexport default class Vestaboard2MQTT {\n private readonly mqtt: MqttClient;\n private readonly board: Vesta;\n private readonly pageConfig = new Cache('page-config');\n private subscriptionIds?: string[];\n private currentMessage?: {page: Page<unknown>, payload: string};\n private currentTimer?: NodeJS.Timeout;\n\n public static async run(): Promise<Vestaboard2MQTT> {\n await Config.load();\n return new Vestaboard2MQTT();\n }\n\n constructor() {\n this.board = new Vesta({\n apiKey: Config.board.key,\n apiSecret: Config.board.secret\n });\n\n this.mqtt = mqtt.connect(Config.mqtt.url, {\n will: {\n topic: Config.mqtt.prefix + '/status',\n payload: 'offline',\n qos: 0,\n retain: true\n }\n });\n this.mqtt.on('connect', () => {\n this.mqtt.publish(Config.mqtt.prefix + '/status', 'online', {\n retain: true\n });\n\n this.setupBrokerSubscriptions().catch(error => {\n console.error(new Error(`Unable to setup mqtt subscriptions: ${error.stack}`));\n process.exit(1);\n });\n });\n this.mqtt.on('message', (topic, message) => {\n this.handleMessage(topic, message.toString()).catch(error => {\n this.debug(`Unable to handle message: ${error.stack}`);\n });\n });\n }\n\n private async setupBrokerSubscriptions() {\n if(!this.mqtt) {\n throw new Error('Unable to setup subscriptions: client not set.');\n }\n\n await this.mqtt.subscribe(Config.mqtt.prefix + '/#');\n }\n\n private debug(message: Error | string) {\n const msg = String(message);\n this.mqtt.publish(Config.mqtt.prefix + '/debug', msg);\n console.log(msg);\n }\n\n private async handleMessage(topic: string, payload: string): Promise<void> {\n if([Config.mqtt.prefix + '/debug', Config.mqtt.prefix + '/status'].includes(topic)) {\n // just ignore my own events\n }\n else if(topic === Config.mqtt.prefix + '/disabled') {\n const value = ['1', 'true', 'TRUE'].includes(payload);\n await Config.updateDisabled(value);\n if(!value) {\n this.debug('Board not disabled anymore, update with latest message');\n await this.updateCurrentMessage();\n }\n }\n else if(Object.keys(pages).map(id => Config.mqtt.prefix + '/' + id).includes(topic)) {\n const page = pages[ topic.substr(Config.mqtt.prefix.length + 1) ];\n await this.renderMessage(page, payload);\n }\n else {\n const page = Object.entries(pages)\n .find(([id]) => topic.startsWith(Config.mqtt.prefix + '/' + id + '/'));\n\n if(!page) {\n this.debug(`Unknown topic ${topic}, I'm sorry`);\n return;\n }\n\n const key = topic.substr((Config.mqtt.prefix + '/' + page[0] + '/').length);\n const cache = await this.pageConfig.get<Partial<unknown>>(page[0]);\n\n const config = page[1].parseConfig(key, payload, cache || {});\n const newConfig = Object.assign(cache || {}, config);\n await this.pageConfig.set(page[0], newConfig);\n await this.updateCurrentMessage();\n this.debug(`Update config for page module ${page[0]}: ${JSON.stringify(newConfig, null, ' ')}`);\n }\n }\n\n private async updateCurrentMessage() {\n if(this.currentMessage) {\n await this.renderMessage(\n this.currentMessage.page,\n this.currentMessage.payload\n );\n }\n }\n\n private async renderMessage(page: Page<unknown>, payload: string) {\n this.currentMessage = {\n page,\n payload\n };\n\n const pageEntry = Object.entries(pages).find(([,pageInstance]) => pageInstance === page);\n if(Config.board.disabled || !pageEntry) {\n return;\n }\n\n const parsePayload = await page.parsePayload(payload);\n const config = await this.pageConfig.get<Partial<unknown>>(pageEntry[0]);\n\n const response = await page.render(parsePayload, config || {});\n if (response.message) {\n await this.sendMessage(response.message);\n }\n\n if(this.currentTimer) {\n clearTimeout(this.currentTimer);\n delete this.currentTimer;\n }\n if(response.validTill) {\n this.debug(`Set timer for ${response.validTill.toString()} to update message`);\n this.currentTimer = setTimeout(() => {\n this.debug('Here I am again, updating the message now…');\n this.renderMessage(page, payload).catch(error => {\n this.debug(`Unable to update page: ${error}`);\n });\n }, response.validTill.getTime() - new Date().getTime());\n }\n }\n\n private async sendMessage(message: Message = new Message()): Promise<void> {\n const charArray = message.export();\n\n if(!this.subscriptionIds) {\n const subscriptions = await this.board.getSubscriptions();\n this.subscriptionIds = subscriptions.map(i => i._id);\n }\n\n this.debug('Sending Message:\\n\\n' + message.toString());\n\n await Promise.all(this.subscriptionIds.map(id => this.board.postMessage(id, charArray)));\n }\n}\n","import {join} from 'path';\nimport {existsSync} from 'fs';\nimport {promises} from 'fs';\n\ntype CacheData = Record<string, CacheNamespace>;\ntype CacheNamespace = Record<string, CacheItem>;\ntype CacheItem = CacheItemObject | CacheValue;\ntype CacheItemObject = {updated: number, value: CacheValue};\ntype CacheValue = unknown;\n\n// as node@12 has no 'fs/promises'\nconst {readFile, writeFile} = promises;\n\nexport default class Cache {\n private static readonly file: string = join(process.env.HOME || '~', '.vestaboard2mqtt');\n private readonly namespace: string;\n private static currentFetch?: Promise<void>;\n private static currentData?: CacheData = {};\n\n constructor(namespace: string) {\n this.namespace = namespace;\n }\n\n static async fetch(): Promise<void> {\n if(this.currentFetch) {\n return this.currentFetch;\n }\n\n const fetch = this.fetchFromFile();\n this.currentFetch = fetch;\n return fetch;\n }\n\n static async fetchFromFile (): Promise<void> {\n if(!existsSync(this.file)) {\n return;\n }\n\n try {\n const content = await readFile(this.file, {encoding: 'utf8'});\n this.currentData = JSON.parse(content);\n }\n catch(error) {\n console.warn(`Unable to parse cache file: ${error}`);\n }\n }\n\n static isCacheItemObject(obj: CacheItem): obj is CacheItemObject {\n\n // eslint-disable-next-line @typescript-eslint/ban-ts-comment\n // @ts-ignore\n return typeof obj === 'object' && typeof obj.updated === 'number' && typeof obj.value !== 'undefined';\n }\n\n async get<T>(key: string, maxAge = 0): Promise<T | null> {\n await Cache.fetch();\n\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace] ||\n !Cache.currentData[this.namespace][key]\n ) {\n return null;\n }\n\n const item = Cache.currentData[this.namespace][key];\n if(!Cache.isCacheItemObject(item) && maxAge > 0) {\n return null;\n }\n if(!Cache.isCacheItemObject(item)) {\n return item as T;\n }\n\n if(maxAge > 0 && new Date().getTime() - item.updated > maxAge) {\n return null;\n }\n\n return item.value as T;\n }\n\n async set(key: string, value: unknown): Promise<void> {\n await Cache.fetch();\n\n if(!Cache.currentData) {\n throw new Error('Unable to set value: currentData is empty!');\n }\n\n Cache.currentData[this.namespace] = Cache.currentData[this.namespace] || {};\n Cache.currentData[this.namespace][key] = {\n updated: new Date().getTime(),\n value\n };\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n async delete(key: string): Promise<void> {\n if(\n !Cache.currentData ||\n !Cache.currentData[this.namespace]\n ) {\n return;\n }\n\n delete Cache.currentData[this.namespace][key];\n\n if(Object.keys(Cache.currentData[this.namespace]).length === 0) {\n delete Cache.currentData[this.namespace];\n }\n\n await Cache.save().catch(error => {\n console.warn(`Unable to update cache: ${error}`);\n });\n }\n\n static async save(): Promise<void> {\n await writeFile(this.file, JSON.stringify(this.currentData, null, ' '));\n }\n}\n","import Cache from './cache.js';\n\nexport interface ConfigBoardData {\n id: string;\n key: string;\n secret: string;\n disabled: boolean;\n}\n\nexport interface ConfigMQTTData {\n url: string;\n prefix: string;\n}\n\nexport interface ConfigCalendarData {\n urls: Record<string, string>;\n cacheTTL: number;\n}\n\nexport interface ConfigData {\n board: ConfigBoardData;\n mqtt: ConfigMQTTData;\n calendar: ConfigCalendarData;\n}\n\nexport default class Config {\n private static readonly cache = new Cache('config');\n private static data?: ConfigData;\n\n public static get loaded(): boolean {\n return !!this.data && !!this.cache;\n }\n\n public static get board(): ConfigBoardData {\n if(this.loaded && this.data?.board) {\n return this.data.board;\n }\n\n throw new Error('Unable to access board config, is config loaded?');\n }\n\n public static get mqtt(): ConfigMQTTData {\n if(this.loaded && this.data?.mqtt) {\n return this.data.mqtt;\n }\n\n throw new Error('Unable to access mqtt config, is config loaded?');\n }\n\n public static get calendar(): ConfigCalendarData {\n if(this.loaded && this.data?.calendar) {\n return this.data.calendar;\n }\n\n throw new Error('Unable to access calendar config, is config loaded?');\n }\n\n public static async load(): Promise<void> {\n const data = await this.cache.get<ConfigData>('default');\n if(!data) {\n throw new Error('Unable to load configuration, try to run the setup script…');\n }\n\n this.data = data;\n }\n\n public static async save(data: ConfigData): Promise<void> {\n this.data = data;\n await this.cache.set('default', data);\n }\n\n public static async updateDisabled(newValue: boolean): Promise<void> {\n if(newValue === this.data?.board.disabled || !this.loaded || !this.data?.mqtt) {\n return;\n }\n\n this.data.board.disabled = newValue;\n await this.save(this.data);\n }\n}\n","import {BoardCharArray, characterCode, emptyBoard, LINE_LENGTH} from 'vestaboard-api/lib/cjs/values.js';\n\nexport enum MessageWriteOptionsLine {\n CURRENT = 'CURRENT',\n NEXT = 'NEXT'\n}\n\nexport interface MessageWriteOptions {\n line?: number | MessageWriteOptionsLine;\n row?: number;\n}\n\nexport interface MessageDrawOptions {\n line?: number;\n}\n\n\nconst SPECIAL_CHAR_MAP = [\n ['ä', [1, 5]],\n ['Ä', [1, 5]],\n ['ö', [15, 5]],\n ['Ö', [15, 5]],\n ['ü', [21, 5]],\n ['Ü', [21, 5]],\n ['ß', [19, 19]],\n ['🟥', [63]],\n ['🟧', [64]],\n ['🟨', [65]],\n ['🟩', [66]],\n ['🟦', [67]],\n ['🟪', [68]],\n ['⬜️', [69]],\n ['⬜', [69]],\n ['⬛️', [0]],\n ['⬛', [0]],\n ['﹫', [38]],\n ['@', [38]],\n ['0️⃣', [36]],\n ['1️⃣', [27]],\n ['2️⃣', [28]],\n ['3️⃣', [29]],\n ['4️⃣', [30]],\n ['5️⃣', [31]],\n ['6️⃣', [32]],\n ['7️⃣', [33]],\n ['8️⃣', [34]],\n ['9️⃣', [35]],\n ['❕', [37]],\n ['‼️', [37, 37]],\n ['❗️', [37]],\n ['⁉️', [37, 60]],\n ['⚠️', [65]],\n ['#️⃣', [39]],\n ['💸', [40]],\n ['💲', [40]],\n ['💵', [40]],\n ['💰', [40]],\n ['$', [40]],\n ['﹩', [40]],\n ['$', [40]],\n ['←', [44]],\n ['→', [44]],\n ['➡', [44]],\n ['⬅', [44]],\n ['➔', [44]],\n ['↔', [44]],\n ['–', [44]],\n ['➕', [46]],\n ['+', [46]],\n ['﹪', [54]],\n ['%', [54]],\n ['❓', [60]],\n ['❔', [60]],\n ['℃', [62, 3]],\n ['℉', [62, 6]]\n];\n\n\nexport default class Message {\n private board: BoardCharArray;\n private currentLine: number | null = null;\n private isFilled = false;\n\n constructor(board: BoardCharArray = Message.newBoardCharArray()) {\n this.board = board;\n }\n\n static newBoardCharArray(): BoardCharArray {\n return JSON.parse(JSON.stringify(emptyBoard));\n }\n\n write(text: string, options: MessageWriteOptions = {}) : void{\n let firstLine = 0;\n if(this.isFilled) {\n return;\n }\n\n if(options.line === MessageWriteOptionsLine.CURRENT) {\n firstLine = this.currentLine || 0;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT && this.currentLine === null) {\n this.currentLine = 0;\n firstLine = this.currentLine;\n }\n else if(\n options.line === MessageWriteOptionsLine.NEXT &&\n typeof this.currentLine === 'number' &&\n this.currentLine < this.board.length - 1\n ) {\n // console.log('write() → current line + 1');\n this.currentLine++;\n firstLine = this.currentLine;\n }\n else if(options.line === MessageWriteOptionsLine.NEXT) {\n this.isFilled = true;\n return;\n }\n else if(options.line !== undefined) {\n firstLine = options.line;\n }\n\n const status = {\n firstLine,\n lastLine: this.board.length - 1,\n firstRow: options.row || 0,\n lastRow: LINE_LENGTH - 1\n };\n const pointer = [status.firstRow, status.firstLine];\n\n const words = text.split(/\\s+/);\n words.forEach((word, i) => {\n\n // Space\n if(i !== 0 && pointer[0] !== status.firstRow) {\n this.board[pointer[1]][pointer[0]] = 0;\n pointer[0]++;\n }\n\n let charsLeftInLine = status.lastRow - pointer[0] + 1;\n const chars = Message.word2chars(word);\n // console.log(\n // 'write()',\n // 'chars =', chars,\n // 'left =', charsLeftInLine,\n // `(${status.lastRow} - ${pointer[0]})`,\n // 'pointer =', pointer,\n // 'charsLeftInLine =', charsLeftInLine\n // );\n\n // Unsupported Word / emoji?\n if(chars.filter(c => c === 60).length === chars.length && chars.length > 0) {\n return;\n }\n\n // New Line?\n if(\n chars.length > charsLeftInLine &&\n chars.length <= status.lastRow - status.firstRow + 1 &&\n pointer[1] < status.lastLine\n ) {\n pointer[0] = status.firstRow;\n pointer[1]++;\n charsLeftInLine = status.lastRow - pointer[0];\n // console.log('write() → new line', 'chars =', chars, 'left =', charsLeftInLine, `(${status.lastRow} - ${pointer[0]})`, 'pointer =', pointer);\n }\n\n // Add Word\n if(chars.length <= charsLeftInLine) {\n this.board[pointer[1]].splice(pointer[0], chars.length, ...chars);\n pointer[0] += chars.length;\n }\n else if(chars.length > charsLeftInLine && charsLeftInLine > 5) {\n this.board[pointer[1]].splice(pointer[0], charsLeftInLine, ...chars.slice(0, charsLeftInLine));\n pointer[0] += charsLeftInLine;\n }\n });\n this.currentLine = pointer[1];\n }\n\n static word2chars(word: string): number[] {\n const result: number[] = [];\n for(const char of word) {\n result.push(...this.char2char(char));\n }\n\n return result;\n }\n\n static char2char(char: string): number[] {\n const fromVestaMap = Object.entries(characterCode)\n .find(([key]) => char === key);\n if(fromVestaMap) {\n return [ fromVestaMap[1]] ;\n }\n\n const fromMyMap = SPECIAL_CHAR_MAP\n .find(([mapChar]) => char === mapChar);\n if(fromMyMap && Array.isArray(fromMyMap[1])) {\n return fromMyMap[1];\n }\n\n return [60];\n }\n\n repeat(char: string, options: MessageDrawOptions = {}): void {\n const line = this.board[options.line || 0];\n line.fill(Message.char2char(char)[0]);\n }\n\n centerLines(): void {\n this.board.forEach(line => {\n const space = [line.findIndex(c => c !== 0), line.slice().reverse().findIndex(c => c !== 0)];\n\n // Line is completely full, continue…\n if(space[0] === -1 || space[1] === -1) {\n return;\n }\n\n const content = line.slice(space[0], line.length - space[1]);\n const padding = Math.floor((space[0] + space[1]) / 2);\n\n line.fill(0, 0, padding);\n line.splice(padding, content.length, ...content);\n line.fill(0, padding + content.length);\n });\n }\n\n center(): void {\n const space = [\n this.board.findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.slice().reverse().findIndex(c => c !== 0) : l.length)),\n this.board.slice().reverse().findIndex(l => l.find(c => c !== 0)),\n Math.min(...this.board.map(l => l.find(c => c !== 0) ? l.findIndex(c => c !== 0) : l.length)),\n ];\n\n const padding = [\n Math.floor((space[0] + space[2]) / 2),\n Math.floor((space[1] + space[3]) / 2)\n ];\n\n // Move up/down\n if(space[0] !== padding[0]) {\n const add = padding[0] - space[0];\n this.board.splice(space[0] < padding[0] ? 0 : this.board.length - add, 0,\n ...this.board.splice(this.board.length - add, add)\n );\n }\n\n // Move left/right\n if(space[3] !== padding[1]) {\n const add = padding[1] - space[3];\n\n this.board.forEach(line => {\n line.splice(space[3] < padding[1] ? 0 : line.length - add, 0,\n ...line.splice(line.length - add, add)\n );\n });\n }\n }\n\n isEmpty (): boolean {\n return !this.board.find(line =>\n line.find(char => char !== 0)\n );\n }\n\n toString(): string {\n return '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n' +\n this.board\n .map(line => '# ' + line.map(char => Message.charToString(char)).join('') + ' #\\n')\n .join('') +\n '#=' + '='.repeat(LINE_LENGTH * 2) + '=#\\n';\n }\n\n static charToString(char: number): string {\n const entry = Object.entries(characterCode)\n .filter(([name]) => name.length <= 2)\n .find(([, code]) => code === char);\n\n if (entry) {\n return entry[0].toUpperCase() + ' ';\n }\n\n switch (char) {\n case 63:\n return '🟥';\n case 64:\n return '🟧';\n case 65:\n return '🟨';\n case 66:\n return '🟩';\n case 67:\n return '🟦';\n case 68:\n return '🟪';\n case 69:\n return '⬜️';\n default:\n return ' ';\n }\n }\n\n export(): BoardCharArray {\n return this.board;\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\n\nexport default class MessagePage implements Page<string> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): string {\n return payload || '';\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async render (content: string): Promise<PageRenderResponse> {\n const message = new Message();\n\n content.split('\\n').forEach(line => {\n message.write(line, {line: MessageWriteOptionsLine.NEXT});\n });\n\n message.centerLines();\n message.center();\n return {message};\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\nimport ical, { VEvent } from 'node-ical';\nimport Config from '../config.js';\nimport TodayPage, {TodayPagePayload} from './today.js';\n\n\nexport interface CalendarPagePayload {\n calendars: string[];\n}\n\nexport type CalendarPageItem = {start: Date, end: Date, summary: string};\n\nexport default class CalendarPage implements Page<CalendarPagePayload> {\n static readonly cache = new Cache('calendar');\n\n public parsePayload(payload: string | null): CalendarPagePayload {\n return {\n calendars: (payload || '').split(',')\n };\n }\n\n parseConfig(): Partial<Record<string, never>> {\n return {};\n }\n\n public async fetchURL(url: string): Promise<CalendarPageItem[]> {\n let preCached = await CalendarPage.cache.get<CalendarPageItem[]>(url, 1000 * 60 * 10);\n if(!preCached) {\n const calendar = await ical.async.fromURL(url);\n const events = Object.values(calendar)\n .filter(entry =>\n entry.type === 'VEVENT' &&\n entry.start &&\n entry.end &&\n entry.summary\n ) as VEvent[];\n preCached = events\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary).trim()\n }))\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime())\n .slice(0, 6);\n\n await CalendarPage.cache.set(url, preCached);\n }\n\n return preCached\n .map(entry => ({\n start: new Date(String(entry.start)),\n end: new Date(String(entry.end)),\n summary: String(entry.summary)\n }))\n .filter(entry =>\n entry.end > new Date()\n );\n }\n\n public static isSameDay (a: Date, b: Date): boolean {\n const result = Boolean(\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n\n return result;\n }\n\n public static isMidnight (date: Date): boolean {\n const result = Boolean(\n !date.getHours() &&\n !date.getMinutes() &&\n !date.getSeconds() &&\n !date.getMilliseconds()\n );\n\n return result;\n }\n\n public async fetchURLs(urls: string[]): Promise<CalendarPageItem[]> {\n const rawResult: {start: Date, end: Date, summary: string}[][] = await Promise.all(urls.map(url => this.fetchURL(url)));\n return ([] as CalendarPageItem[])\n .concat(...rawResult)\n .filter(entry => entry.end > new Date())\n .sort((a, b) => a.end.getTime() - b.end.getTime());\n }\n\n public static getTimeStr (event: CalendarPageItem, oneLine = false): string {\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(), event.start)\n ) {\n return 'Heute';\n }\n if(\n this.isMidnight(event.start) &&\n this.isMidnight(event.end) &&\n this.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), event.start)\n ) {\n return oneLine ? 'Morgen' : 'Morgn';\n }\n\n return event.start.getHours().toString().padStart(2, '⬛️') + ':' +\n event.start.getMinutes().toString().padStart(2, '0');\n }\n\n public async render (payload: CalendarPagePayload): Promise<PageRenderResponse> {\n const message = new Message();\n const urls = payload.calendars\n .map(id => Config.calendar.urls[id])\n .filter(Boolean);\n\n const calendar = await this.fetchURLs(urls)\n .then(calendar => calendar.filter(entry => {\n if (CalendarPage.isMidnight(entry.start) && CalendarPage.isMidnight(entry.end)) {\n return CalendarPage.isSameDay(new Date(), entry.start) || (\n new Date().getHours() >= 20 &&\n CalendarPage.isSameDay(new Date(new Date().getTime() + 1000 * 60 * 60 * 24), entry.start)\n );\n } else {\n return entry.start < new Date(new Date().getTime() + (1000 * 60 * 60 * 12));\n }\n }));\n\n if(!calendar.length) {\n const today = new TodayPage();\n const pageConfigCache = new Cache('page-config');\n const config = await pageConfigCache.get<Partial<TodayPagePayload>>('today');\n return today.render({}, config || {});\n }\n\n let validTill = new Date(new Date().getTime() + 1000 * 60 * 10);\n calendar.forEach(event => {\n message.write(\n CalendarPage.getTimeStr(event, calendar.length === 1),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write(event.summary, {line: MessageWriteOptionsLine.CURRENT, row: 6});\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n if(event.end < validTill) {\n validTill = event.end;\n }\n });\n\n message.center();\n return {\n message,\n validTill\n };\n }\n}\n","import Cache from '../cache.js';\nimport Page, {PageRenderResponse} from '../page.js';\nimport Message, {MessageWriteOptionsLine} from '../message.js';\n\nexport interface TodayPagePayload {\n temp?: [number, number];\n precip?: number;\n locale: string;\n}\n\nexport default class TodayPage implements Page<Partial<TodayPagePayload>, TodayPagePayload> {\n static readonly cache = new Cache('calendar');\n\n parsePayload(payload: string | null): Partial<TodayPagePayload> {\n try {\n return JSON.parse(payload || '') as Partial<TodayPagePayload>;\n }\n catch(error) {\n return {};\n }\n }\n\n parseConfig(key: string, value: string | null, config: Partial<TodayPagePayload>): Partial<TodayPagePayload> {\n const result: Partial<TodayPagePayload> = {};\n console.log('parseConfig', config);\n\n if(key === 'min-temp' && Array.isArray(config.temp)) {\n result.temp = [\n parseInt(value || ''),\n Math.max(...config.temp)\n ];\n }\n else if(key === 'min-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp' && Array.isArray(config.temp)) {\n result.temp = [\n Math.min(...config.temp),\n parseInt(value || '')\n ];\n }\n else if(key === 'max-temp') {\n result.temp = [\n parseInt(value || ''),\n parseInt(value || '')\n ];\n }\n else if(key === 'precip') {\n result.precip = parseInt(value || '', 10);\n }\n else if(key === 'locale') {\n result.locale = String(value);\n }\n\n return result;\n }\n\n public async render (payload: Partial<TodayPagePayload>, config: Partial<TodayPagePayload>): Promise<PageRenderResponse> {\n const message = new Message();\n const today = new Date();\n\n const mergedPayload: TodayPagePayload = Object.assign({\n temp: undefined,\n precip: undefined,\n locale: 'en'\n }, config, payload);\n\n message.write(today.toLocaleString(mergedPayload.locale, {weekday: 'long'}));\n message.write(\n today.toLocaleString(mergedPayload.locale, {day: 'numeric', month: 'long'}),\n {line: MessageWriteOptionsLine.NEXT}\n );\n\n message.write('', {line: MessageWriteOptionsLine.NEXT});\n\n const varContent = [];\n if(Array.isArray(mergedPayload.temp)) {\n varContent.push(`${Math.min(...mergedPayload.temp)}-${Math.max(...mergedPayload.temp)}°C`);\n }\n if(typeof mergedPayload.precip === 'number' && mergedPayload.precip > 0) {\n varContent.push(`${mergedPayload.precip}%`);\n }\n if(varContent.length) {\n message.write(varContent.join(', '), {line: MessageWriteOptionsLine.NEXT});\n }\n\n message.center();\n\n const validTill = new Date();\n validTill.setHours(24,0,0,0);\n\n return { message, validTill };\n }\n}\n","import Page from '../page.js';\nimport MessagePage from './message.js';\nimport CalendarPage from './calendar.js';\nimport TodayPage from './today.js';\n\nconst pages: Record<string, Page<unknown, unknown>> = {\n message: new MessagePage(),\n calendar: new CalendarPage(),\n today: new TodayPage()\n};\n\nexport default pages;\n"],"mappings":"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","names":["lib_exports","__export","Vestaboard2MQTT","__toCommonJS","import_async_mqtt","import_path","import_fs","readFile","writeFile","_Cache","namespace","__async","fetch","content","error","obj","key","maxAge","item","value","Cache","Config","_a","__async","data","newValue","_b","Cache","import_values","SPECIAL_CHAR_MAP","Message","_Message","board","text","options","firstLine","status","pointer","word","i","charsLeftInLine","chars","c","result","char","fromVestaMap","key","fromMyMap","mapChar","line","space","content","padding","l","add","entry","name","code","MessagePage","payload","content","__async","message","Message","line","Cache","import_node_ical","TodayPage","payload","error","key","value","config","result","__async","message","Message","today","mergedPayload","varContent","validTill","Cache","_CalendarPage","payload","url","__async","preCached","calendar","ical","entry","a","b","date","urls","rawResult","event","oneLine","message","Message","id","Config","today","TodayPage","config","Cache","validTill","CalendarPage","pages","MessagePage","CalendarPage","TodayPage","pages_default","import_vestaboard_api","Vestaboard2MQTT","_Vestaboard2MQTT","Cache","Config","mqtt","error","topic","message","__async","msg","payload","value","pages_default","id","page","key","cache","config","newConfig","pageEntry","pageInstance","parsePayload","response","Message","charArray","subscriptions","i"]}
|
package/dist/lib/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a}from"../chunk-
|
|
1
|
+
import{a}from"../chunk-RW2UBCBQ.js";export{a as default};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -19,14 +19,14 @@
|
|
|
19
19
|
"@semantic-release/exec": "^6.0.3",
|
|
20
20
|
"@semantic-release/git": "^10.0.1",
|
|
21
21
|
"@semantic-release/npm": "^11.0.1",
|
|
22
|
-
"@types/mocha": "^10.0.
|
|
23
|
-
"@types/node": "^20.
|
|
22
|
+
"@types/mocha": "^10.0.6",
|
|
23
|
+
"@types/node": "^20.10.2",
|
|
24
24
|
"@types/node-fetch": "^3.0.3",
|
|
25
|
-
"@types/ws": "^8.5.
|
|
26
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
27
|
-
"@typescript-eslint/parser": "^6.
|
|
25
|
+
"@types/ws": "^8.5.10",
|
|
26
|
+
"@typescript-eslint/eslint-plugin": "^6.13.2",
|
|
27
|
+
"@typescript-eslint/parser": "^6.13.2",
|
|
28
28
|
"c8": "^8.0.1",
|
|
29
|
-
"eslint": "^8.
|
|
29
|
+
"eslint": "^8.55.0",
|
|
30
30
|
"eslint-plugin-jsonc": "^2.10.0",
|
|
31
31
|
"esm": "^3.2.25",
|
|
32
32
|
"license-checker": "^25.0.1",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"semantic-release-license": "^1.0.3",
|
|
36
36
|
"source-map-support": "^0.5.21",
|
|
37
37
|
"ts-node": "^10.8.2",
|
|
38
|
-
"tsup": "^
|
|
39
|
-
"typedoc": "^0.25.
|
|
40
|
-
"typescript": "^5.
|
|
38
|
+
"tsup": "^8.0.1",
|
|
39
|
+
"typedoc": "^0.25.4",
|
|
40
|
+
"typescript": "^5.3.2"
|
|
41
41
|
},
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18.0.0"
|
|
@@ -62,12 +62,12 @@
|
|
|
62
62
|
"build": "tsup && cp ./dist/lib/index.d.ts ./dist/lib/index.d.cts",
|
|
63
63
|
"build-all": "./.github/workflows/build.sh",
|
|
64
64
|
"coverage": "c8 mocha",
|
|
65
|
-
"develop": "ts-node
|
|
65
|
+
"develop": "TS_NODE_TRANSPILE_ONLY=true node --no-warnings --enable-source-maps --loader ts-node/esm src/bin/start.ts",
|
|
66
66
|
"license-check": "license-checker --production --summary",
|
|
67
67
|
"lint": "eslint . --ext .ts,.json",
|
|
68
68
|
"start": "node ./dist/bin/start.js",
|
|
69
69
|
"test": "mocha"
|
|
70
70
|
},
|
|
71
71
|
"type": "module",
|
|
72
|
-
"version": "4.0.3-develop.
|
|
72
|
+
"version": "4.0.3-develop.4"
|
|
73
73
|
}
|