UNPKG

@hakit/core

Version:

A collection of React hooks and helpers for Home Assistant to easily communicate with the Home Assistant WebSocket API.

1 lines 22.1 kB
{"version":3,"file":"index.cjs","sources":["../../../scripts/sync-user-types/connection.ts","../../../scripts/sync-user-types/constants.ts","../../../scripts/sync-user-types/action-generator.ts","../../../scripts/sync-user-types/entity-generator.ts","../../../scripts/sync-user-types/index.ts"],"sourcesContent":["import { createConnection, createLongLivedTokenAuth, Auth, getServices, getStates, HassEntity, HassServices } from 'home-assistant-js-websocket';\nimport WebSocket from 'ws';\n\ninterface HaWebSocket extends WebSocket {\n haVersion: string;\n}\n\nconst MSG_TYPE_AUTH_REQUIRED = \"auth_required\";\nconst MSG_TYPE_AUTH_INVALID = \"auth_invalid\";\nconst MSG_TYPE_AUTH_OK = \"auth_ok\";\nconst ERR_CANNOT_CONNECT = 1;\nconst ERR_INVALID_AUTH = 2;\n\nexport function createSocket(\n auth: Auth,\n): Promise<HaWebSocket> {\n // Convert from http:// -> ws://, https:// -> wss://\n const url = auth.wsUrl;\n\n console.info(\n \"[Auth phase] Initializing WebSocket connection to Home Assistant\",\n url,\n );\n\n function connect(\n triesLeft: number,\n promResolve: (socket: HaWebSocket) => void,\n promReject: (err: number) => void,\n ) {\n console.info(\n `[Auth Phase] Connecting to Home Assistant... Tries left: ${triesLeft}`,\n url,\n );\n\n const socket = new WebSocket(url, {\n rejectUnauthorized: false,\n }) as HaWebSocket;\n\n // If invalid auth, we will not try to reconnect.\n let invalidAuth = false;\n\n const closeMessage = (ev: {\n wasClean: boolean;\n code: number;\n reason: string;\n target: WebSocket;\n }) => {\n let errorMessage;\n if (ev && ev.code && ev.code !== 1000) {\n errorMessage = `WebSocket connection to Home Assistant closed with code ${ev.code} and reason ${ev.reason}`;\n }\n closeOrError(errorMessage);\n };\n\n const errorMessage = (ev: {\n message: string;\n type: string;\n target: WebSocket;\n }) => {\n // If we are in error handler make sure close handler doesn't also fire.\n socket.removeEventListener(\"close\", closeMessage);\n let errMessage =\n \"Disconnected from Home Assistant with a WebSocket error\";\n if (ev.message) {\n errMessage += ` with message: ${ev.message}`;\n }\n closeOrError(errMessage);\n };\n\n const closeOrError = (errorText?: string) => {\n if (errorText) {\n console.info(\n `WebSocket Connection to Home Assistant closed with an error: ${errorText}`,\n );\n }\n if (invalidAuth) {\n promReject(ERR_INVALID_AUTH);\n return;\n }\n\n // Reject if we no longer have to retry\n if (triesLeft === 0) {\n // We never were connected and will not retry\n promReject(ERR_CANNOT_CONNECT);\n return;\n }\n\n const newTries = triesLeft === -1 ? -1 : triesLeft - 1;\n // Try again in a second\n setTimeout(() => connect(newTries, promResolve, promReject), 1000);\n };\n\n // Auth is mandatory, so we can send the auth message right away.\n const handleOpen = async (): Promise<void> => {\n try {\n if (auth.expired) {\n await auth.refreshAccessToken();\n }\n socket.send(\n JSON.stringify({\n type: \"auth\",\n access_token: auth.accessToken,\n }),\n );\n } catch (err) {\n // Refresh token failed\n invalidAuth = err === ERR_INVALID_AUTH;\n socket.close();\n }\n };\n\n const handleMessage = (event: WebSocket.MessageEvent) => {\n const message = JSON.parse(event.data as string);\n\n console.info(\n `[Auth phase] Received a message of type ${message.type}`,\n message,\n );\n\n switch (message.type) {\n case MSG_TYPE_AUTH_INVALID:\n invalidAuth = true;\n socket.close();\n break;\n\n case MSG_TYPE_AUTH_OK:\n socket.removeEventListener(\"open\", handleOpen);\n socket.removeEventListener(\"message\", handleMessage);\n socket.removeEventListener(\"close\", closeMessage);\n socket.removeEventListener(\"error\", errorMessage);\n socket.haVersion = message.ha_version;\n promResolve(socket);\n break;\n\n default:\n // We already send this message when socket opens\n if (message.type !== MSG_TYPE_AUTH_REQUIRED) {\n console.info(\"[Auth phase] Unhandled message\", message);\n }\n }\n };\n\n socket.addEventListener(\"open\", handleOpen);\n socket.addEventListener(\"message\", handleMessage);\n socket.addEventListener(\"close\", closeMessage);\n socket.addEventListener(\"error\", errorMessage);\n }\n\n return new Promise((resolve, reject) => connect(3, resolve, reject));\n}\n\n\nexport async function connect(url: string, token: string): Promise<{\n services: HassServices,\n states: HassEntity[],\n}> {\n try {\n const auth = createLongLivedTokenAuth(url, token);\n const connection = await createConnection({\n auth,\n // @ts-expect-error - no way to fix this without providing an override for the types\n // as the websocket definition is different\n createSocket: () => createSocket(auth),\n });\n const services = await getServices(connection);\n const states = await getStates(connection);\n connection.close();\n return {\n services,\n states,\n }\n } catch (err) {\n console.error('err', err);\n throw new Error('Failed to connect to Home Assistant');\n }\n}\n","\n\nexport const DEFAULT_FILENAME = 'supported-types.d.ts';\n\nexport const REMAPPED_TYPES: Record<string, string> = {\n hs_color: `[number, number]`,\n rgb_color: `[number, number, number]`,\n rgbw_color: `[number, number, number, number]`,\n rgbww_color: `[number, number, number, number, number]`,\n group_members: `string[]`,\n media_content_id: `string | number`,\n color_temp_kelvin: `number`,\n white: 'boolean',\n color_temp: `number`,\n xy_color: `[number, number]`,\n};\n","import { HassService, HassServices } from 'home-assistant-js-websocket';\nimport _ from 'lodash';\nimport { REMAPPED_TYPES } from './constants';\n\ntype SelectorValues = string | number | boolean | null;\ntype SelectorOption = {\n [key: string]: SelectorValues;\n}\n\ntype Selector = {\n \n select?: {\n [key: string]: object | {\n value: string;\n }[] | string[];\n }\n number?: SelectorOption;\n object?: SelectorOption;\n text?: SelectorOption;\n entity?: SelectorOption;\n boolean?: SelectorOption;\n datetime?: SelectorOption;\n duration?: SelectorOption;\n};\n\nconst resolveSelectorType = (selector: Selector) => {\n if (!selector) return 'object';\n // there only ever seems to be one key under the selector object\n const keys = Object.keys(selector);\n if (keys.includes('number')) return 'number';\n if (keys.includes('object')) return 'object';\n if (keys.includes('duration')) return `{\n hours?: number;\n days?: number;\n minutes?: number;\n seconds?: number;\n }`;\n const stringTypes = [\n 'text',\n 'entity',\n 'datetime',\n 'time',\n 'date',\n 'addon',\n 'backup_location',\n 'icon',\n 'conversation_agent',\n 'device',\n 'theme',\n ];\n const isStringType = stringTypes.some(type => keys.includes(type));\n if (isStringType) return 'string';\n if (keys.includes('boolean')) return 'boolean';\n if (keys.includes('select')) {\n const options = selector?.select?.options;\n if (!_.isArray(options) || options.length === 0) return 'unknown';\n return `${options.map(option => `'${typeof option === 'string' ? option : option.value}'`).join(' | ')}`;\n }\n // unknown types\n return 'unknown';\n}\n\nfunction sanitizeString(str: string | boolean | number): string {\n return `${str}`.replace(/\"/g, \"'\").replace(/[\\n\\r]+/g, ' ');\n}\n\ninterface ActionTypeOptions {\n domainWhitelist?: string[];\n domainBlacklist?: string[];\n serviceWhitelist?: string[];\n serviceBlacklist?: string[];\n}\n\nexport const generateActionTypes = (input: HassServices, {\n domainWhitelist = [],\n domainBlacklist = [],\n serviceWhitelist = [],\n serviceBlacklist = [],\n}: ActionTypeOptions) => {\n const interfaces = Object.entries(input).map(([domain, actions]) => {\n const camelDomain = _.camelCase(domain);\n if (domainBlacklist.length > 0 && (domainBlacklist.includes(camelDomain) || domainBlacklist.includes(domain))) return '';\n if (domainWhitelist.length > 0 && (!domainWhitelist.includes(camelDomain) || !domainWhitelist.includes(domain))) return '';\n const domainActions = Object.entries(actions).map(([action, { fields, description }]) => {\n const camelAction = _.camelCase(action);\n if (serviceBlacklist.length > 0 && (serviceBlacklist.includes(camelAction) || serviceBlacklist.includes(action))) return '';\n if (serviceWhitelist.length > 0 && (!serviceWhitelist.includes(camelAction) || !serviceWhitelist.includes(action))) return '';\n function processFields(fields: HassService['fields']): string[] {\n return Object.entries(fields).map(([field, { selector, example, description, ...rest }]) => {\n const required = rest.required ?? false;\n const remapByDomainActionField = `${domain}.${action}.${field}`;\n const remapByActionField = `${action}.${field}`;\n const remapByField = field;\n const domainActionFieldOverride = remapByDomainActionField in REMAPPED_TYPES ? REMAPPED_TYPES[remapByDomainActionField] : undefined;\n const actionFieldOverride = remapByActionField in REMAPPED_TYPES ? REMAPPED_TYPES[remapByActionField] : undefined;\n const fieldOverride = remapByField in REMAPPED_TYPES ? REMAPPED_TYPES[remapByField] : undefined;\n const _selector = selector as Selector;\n const overrides = domainActionFieldOverride || actionFieldOverride || fieldOverride;\n // some fields come back as an incorrect type but we know these should be something specific, these are hard coded in the REMAPPED_TYPES constant\n const type = typeof overrides === 'string' ? overrides : resolveSelectorType(_selector);\n let constraints = '';\n if (_.isObject(selector)) {\n const ignoredKeys = ['select', 'entity', 'theme', 'constant', 'text', 'device'];\n constraints = Object.entries(_selector || {})\n .filter(([_key, value]) => _key && _.isObject(value) && !ignoredKeys.includes(_key))\n .map(([key, value]) => ` ${key}: ${Object.entries(value || {} as object).map(([key, value]) => `${key}: ${value}`).join(', ')}`).join(', ');\n constraints = constraints ? ` @constraints ${constraints}` : '';\n }\n const exampleUsage = example ? ` @example ${example}` : '';\n const isAdvancedFields = field === 'advanced_fields';\n const comment = `${description ?? ''}${exampleUsage ?? ''}${constraints}`;\n return isAdvancedFields && 'fields' in rest ? processFields(rest.fields as HassService['fields']).join('\\n') : `//${comment ? sanitizeString(` ${comment}`) : ''}\\n${field}${required ? '' : '?'}: ${type};`;\n });\n } \n // the data passed to the ServiceFunction<object>\n const data = processFields(fields);\n // the data passed to the ServiceFunction<object>\n const actionData = `${Object.keys(fields).length === 0 ? 'object' : `{${data.join('\\n')}}`}`;\n return `// ${sanitizeString(description)}\n ${camelAction}: ServiceFunction<object, T, ${actionData}>;\n `;\n }).join('')\n const result = `${camelDomain}: {\n ${domainActions}\n }\n `;\n return result;\n });\n return interfaces.join('');\n}","import { HassEntity } from 'home-assistant-js-websocket';\n\nexport const generateEntityType = (input: HassEntity[]) => {\n return input.map(e => `'${e.entity_id}'`).join(' | ');\n}\n","// purposely adding js extensions here so the extensions stay in the output.\nimport { connect } from './connection.js';\nimport { generateActionTypes } from './action-generator.js';\nimport { generateEntityType } from './entity-generator.js';\nimport { writeFileSync } from 'fs';\nimport { DEFAULT_FILENAME } from './constants.js';\nimport { format, Options } from \"prettier\";\n\nexport interface TypeSyncOptions {\n url: string;\n token: string;\n outDir?: string;\n filename?: string;\n /** this is used internally to generate the default supported services, you will most definitely need to leave this as true */\n custom?: boolean;\n domainWhitelist?: string[];\n domainBlacklist?: string[];\n serviceWhitelist?: string[];\n serviceBlacklist?: string[];\n prettier?: {\n options: Options;\n disable: boolean;\n }\n}\n\nexport async function typeSync({\n url,\n token,\n outDir: _outDir,\n filename = DEFAULT_FILENAME,\n domainWhitelist = [],\n domainBlacklist = [],\n serviceWhitelist = [],\n serviceBlacklist = [],\n custom = true,\n prettier,\n}: TypeSyncOptions) {\n if (!url || !token) {\n throw new Error('Missing url or token arguments');\n }\n const warning = `\n // this is an auto generated file, do not change this manually\n `;\n \n const { states, services } = await connect(url, token);\n \n const serviceInterfaces = await generateActionTypes(services, {\n domainWhitelist,\n domainBlacklist,\n serviceWhitelist,\n serviceBlacklist,\n });\n const output = custom ? `\n ${warning}\n import { ServiceFunction, ServiceFunctionTypes } from \"@hakit/core\";\n declare module '@hakit/core' {\n export interface CustomSupportedServices<T extends ServiceFunctionTypes = \"target\"> {\n ${serviceInterfaces}\n }\n export interface CustomEntityNameContainer {\n names: ${generateEntityType(states)};\n }\n }\n ` : `\n ${warning}\n import type { ServiceFunctionTypes, ServiceFunction } from \"./\";\n export interface DefaultServices<T extends ServiceFunctionTypes = \"target\"> {\n ${serviceInterfaces}\n }\n `;\n const outDir = _outDir || process.cwd();\n const formatted = prettier?.disable ? output: await format(output, {\n parser: 'typescript',\n ...prettier?.options\n });\n // now write the file\n writeFileSync(`${outDir}/${filename}`, formatted);\n console.info(`Succesfully generated types: ${outDir}/${filename}\\n\\n`);\n // reminder to add the generated file to the tsconfig.app.json include array\n console.info(`IMPORTANT: Don't forget to add the \"${filename}\" file to your tsconfig.app.json include array\\n\\n`);\n}"],"names":["MSG_TYPE_AUTH_REQUIRED","MSG_TYPE_AUTH_INVALID","MSG_TYPE_AUTH_OK","ERR_CANNOT_CONNECT","ERR_INVALID_AUTH","createSocket","auth","url","connect","triesLeft","promResolve","promReject","socket","WebSocket","invalidAuth","closeMessage","ev","errorMessage","closeOrError","errMessage","errorText","newTries","handleOpen","err","handleMessage","event","message","resolve","reject","token","createLongLivedTokenAuth","connection","createConnection","services","getServices","states","getStates","DEFAULT_FILENAME","REMAPPED_TYPES","resolveSelectorType","selector","keys","type","options","_","option","sanitizeString","str","generateActionTypes","input","domainWhitelist","domainBlacklist","serviceWhitelist","serviceBlacklist","domain","actions","camelDomain","domainActions","action","fields","description","camelAction","processFields","field","example","rest","required","remapByDomainActionField","remapByActionField","remapByField","domainActionFieldOverride","actionFieldOverride","fieldOverride","_selector","overrides","constraints","ignoredKeys","_key","value","key","exampleUsage","isAdvancedFields","comment","data","actionData","generateEntityType","typeSync","_outDir","filename","custom","prettier","warning","serviceInterfaces","output","outDir","formatted","format","writeFileSync"],"mappings":"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"}