UNPKG

@hakit/core

Version:

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

39 lines (36 loc) 5.76 kB
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S=require("home-assistant-js-websocket"),K=require("ws"),h=require("lodash"),W=require("node:fs"),Q=require("prettier"),X="auth_required",Z="auth_invalid",B="auth_ok",ee=1,L=2;function ne(o){const e=o.wsUrl;console.info("[Auth phase] Initializing WebSocket connection to Home Assistant",e);function i(r,c,a){console.info(`[Auth Phase] Connecting to Home Assistant... Tries left: ${r}`,e);const n=new K(e,{rejectUnauthorized:!1});let l=!1;const d=s=>{let t;s&&s.code&&s.code!==1e3&&(t=`WebSocket connection to Home Assistant closed with code ${s.code} and reason ${s.reason}`),f(t)},m=s=>{n.removeEventListener("close",d);let t="Disconnected from Home Assistant with a WebSocket error";s.message&&(t+=` with message: ${s.message}`),f(t)},f=s=>{if(s&&console.info(`WebSocket Connection to Home Assistant closed with an error: ${s}`),l){a(L);return}if(r===0){a(ee);return}const t=r===-1?-1:r-1;setTimeout(()=>i(t,c,a),1e3)},u=async()=>{try{o.expired&&await o.refreshAccessToken(),n.send(JSON.stringify({type:"auth",access_token:o.accessToken}))}catch(s){l=s===L,n.close()}},p=s=>{const t=JSON.parse(s.data);switch(console.info(`[Auth phase] Received a message of type ${t.type}`,t),t.type){case Z:l=!0,n.close();break;case B:n.removeEventListener("open",u),n.removeEventListener("message",p),n.removeEventListener("close",d),n.removeEventListener("error",m),n.haVersion=t.ha_version,c(n);break;default:t.type!==X&&console.info("[Auth phase] Unhandled message",t)}};n.addEventListener("open",u),n.addEventListener("message",p),n.addEventListener("close",d),n.addEventListener("error",m)}return new Promise((r,c)=>i(3,r,c))}async function te(o,e){try{const i=S.createLongLivedTokenAuth(o,e),r=await S.createConnection({auth:i,createSocket:()=>ne(i)}),c=await S.getServices(r),a=await S.getStates(r);return r.close(),{services:c,states:a}}catch(i){throw console.error("err",i),new Error("Failed to connect to Home Assistant")}}const se="supported-types.d.ts",b={hs_color:"[number, number]",rgb_color:"[number, number, number]",rgbw_color:"[number, number, number, number]",rgbww_color:"[number, number, number, number, number]",group_members:"string[]",media_content_id:"string | number",kelvin:"number | object",white:"boolean",color_temp:"number | object",xy_color:"[number, number]"},oe=o=>{if(!o)return"object";const e=Object.keys(o);if(e.includes("number"))return"number";if(e.includes("object"))return"object";if(e.includes("duration"))return`{ hours?: number; days?: number; minutes?: number; seconds?: number; }`;if(["text","entity","datetime","time","date","addon","backup_location","icon","conversation_agent","device","theme"].some(c=>e.includes(c)))return"string";if(e.includes("boolean"))return"boolean";if(e.includes("select")){const c=o?.select?.options;return!h.isArray(c)||c.length===0?"any":`${c.map(a=>`'${typeof a=="string"?a:a.value}'`).join(" | ")}`}return"any"};function C(o){return`${o}`.replace(/"/g,"'").replace(/[\n\r]+/g," ")}const re=(o,{domainWhitelist:e=[],domainBlacklist:i=[],serviceWhitelist:r=[],serviceBlacklist:c=[]})=>Object.entries(o).map(([n,l])=>{const d=h.camelCase(n);if(i.length>0&&(i.includes(d)||i.includes(n))||e.length>0&&(!e.includes(d)||!e.includes(n)))return"";const m=Object.entries(l).map(([u,{fields:p,description:s}])=>{const t=h.camelCase(u);if(c.length>0&&(c.includes(t)||c.includes(u))||r.length>0&&(!r.includes(t)||!r.includes(u)))return"";function g(H){return Object.entries(H).map(([y,{selector:E,example:w,description:P,...A}])=>{const U=A.required??!1,j=`${n}.${u}.${y}`,O=`${u}.${y}`,k=y,x=j in b?b[j]:void 0,I=O in b?b[O]:void 0,q=k in b?b[k]:void 0,F=E,D=x||I||q,R=typeof D=="string"?D:oe(F);let $="";if(h.isObject(E)){const G=["select","entity","theme","constant","text","device"];$=Object.entries(F||{}).filter(([v,T])=>v&&h.isObject(T)&&!G.includes(v)).map(([v,T])=>` ${v}: ${Object.entries(T||{}).map(([J,V])=>`${J}: ${V}`).join(", ")}`).join(", "),$=$?` @constraints ${$}`:""}const Y=w?` @example ${w}`:"",z=y==="advanced_fields",M=`${P??""}${Y??""}${$}`;return z&&"fields"in A?g(A.fields).join(` `):`//${M?C(` ${M}`):""} ${y}${U?"":"?"}: ${R};`})}const _=g(p),N=`${Object.keys(p).length===0?"object":`{${_.join(` `)}}`}`;return`// ${C(s)} ${t}: ServiceFunction<object, T, ${N}>; `}).join("");return`${d}: { ${m} } `}).join(""),ce=o=>o.map(e=>`'${e.entity_id}'`).join(" | ");async function ie({url:o,token:e,outDir:i,filename:r=se,domainWhitelist:c=[],domainBlacklist:a=[],serviceWhitelist:n=[],serviceBlacklist:l=[],custom:d=!0,prettier:m}){if(!o||!e)throw new Error("Missing url or token arguments");const f=` // this is an auto generated file, do not change this manually `,{states:u,services:p}=await te(o,e),s=await re(p,{domainWhitelist:c,domainBlacklist:a,serviceWhitelist:n,serviceBlacklist:l}),t=d?` ${f} import { ServiceFunction, ServiceFunctionTypes } from "@hakit/core"; declare module '@hakit/core' { export interface CustomSupportedServices<T extends ServiceFunctionTypes = "target"> { ${s} } export interface CustomEntityNameContainer { names: ${ce(u)}; } } `:` ${f} import type { ServiceFunctionTypes, ServiceFunction } from "./"; export interface DefaultServices<T extends ServiceFunctionTypes = "target"> { ${s} } `,g=i||process.cwd(),_=m?.disable?t:await Q.format(t,{parser:"typescript",...m?.options});W.writeFileSync(`${g}/${r}`,_),console.info(`Succesfully generated types: ${g}/${r} `),console.info(`IMPORTANT: Don't forget to add the "${r}" file to your tsconfig.app.json include array `)}exports.typeSync=ie; //# sourceMappingURL=index.cjs.map