UNPKG

iobroker.lovelace

Version:

With this adapter you can build visualization for ioBroker with Home Assistant Lovelace UI

3 lines 18.4 kB
export const __rspack_esm_id=28845;export const __rspack_esm_ids=[28845];export const __webpack_modules__={85404(e,t,s){s.a(e,async function(e,t){try{s(44114),s(16573),s(78100),s(77936),s(18111),s(61701),s(33110),s(37467),s(44732),s(79577),s(16632),s(34226),s(9486),s(20456);var a=s(62826),i=s(5124),o=s(22441),n=s(6412),r=s(82286),d=s(69150),l=s(88433),c=s(65063),h=s(14503),p=s(74209),_=s(36918),g=s(38962),u=(s(3587),s(91485)),v=e([g,u]);[g,u]=v.then?(await v)():v;const m="M13,13H11V7H13M13,17H11V15H13M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z",b="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z",y="M7.41,15.41L12,10.83L16.59,15.41L18,14L12,8L6,14L7.41,15.41Z",k="M9,22A1,1 0 0,1 8,21V18H4A2,2 0 0,1 2,16V4C2,2.89 2.9,2 4,2H20A2,2 0 0,1 22,4V16A2,2 0 0,1 20,18H13.9L10.2,21.71C10,21.9 9.75,22 9.5,22V22H9M10,16V19.08L13.08,16H20V4H4V16H10M17,11H15V9H17V11M13,11H11V9H13V11M9,11H7V9H9V11Z",f="M12,2A3,3 0 0,1 15,5V11A3,3 0 0,1 12,14A3,3 0 0,1 9,11V5A3,3 0 0,1 12,2M19,11C19,14.53 16.39,17.44 13,17.93V21H11V17.93C7.61,17.44 5,14.53 5,11H7A5,5 0 0,0 12,16A5,5 0 0,0 17,11H19Z",x="M2,21L23,12L2,3V10L17,12L2,14V21Z";class w extends i.WF{willUpdate(e){this.hasUpdated&&!e.has("pipeline")||(this._conversation=[{who:"hass",text:this.hass.localize("ui.dialogs.voice_command.how_can_i_help"),thinking:"",tool_calls:{}}])}firstUpdated(e){super.firstUpdated(e),this.startListening&&this.pipeline&&this.pipeline.stt_engine&&p.N.isSupported&&this._toggleListening(),setTimeout(()=>this._messageInput.focus(),0)}updated(e){super.updated(e),e.has("_conversation")&&this._scrollMessagesBottom()}disconnectedCallback(){super.disconnectedCallback(),this._audioRecorder?.close(),this._unloadAudio()}render(){const e=!!this.pipeline&&(this.pipeline.prefer_local_intents||!this.hass.states[this.pipeline.conversation_engine]||(0,r.$)(this.hass.states[this.pipeline.conversation_engine],l.ZE.CONTROL)),t=p.N.isSupported,s=this.pipeline?.stt_engine&&!this.disableSpeech;return i.qy` <div class="messages ha-scrollbar"> ${e?i.s6:i.qy` <ha-alert> ${this.hass.localize("ui.dialogs.voice_command.conversation_no_control")} </ha-alert> `} <div class="spacer"></div> ${this._conversation.map((e,t)=>i.qy` <div class="message-container ${(0,n.H)({[e.who]:!0})}"> ${e.text||e.error||e.thinking||e.tool_calls&&Object.keys(e.tool_calls).length>0?i.qy` <div class="message ${(0,n.H)({error:!!e.error,[e.who]:!0})}"> ${e.thinking||e.tool_calls&&Object.keys(e.tool_calls).length>0?i.qy` <div class="thinking-wrapper ${(0,n.H)({expanded:!!e.thinking_expanded})}"> <button class="thinking-header" .index="${t}" @click="${this._handleToggleThinking}" aria-expanded="${e.thinking_expanded?"true":"false"}"> <ha-svg-icon .path="${k}"></ha-svg-icon> <span class="thinking-label"> ${this.hass.localize("ui.dialogs.voice_command.show_details")} </span> <ha-svg-icon .path="${e.thinking_expanded?y:b}"></ha-svg-icon> </button> <div class="thinking-content"> ${e.thinking?i.qy`<ha-markdown .content="${e.thinking}"></ha-markdown>`:i.s6} ${e.tool_calls&&Object.keys(e.tool_calls).length>0?i.qy` <div class="tool-calls"> ${Object.values(e.tool_calls).map(e=>i.qy` <div class="tool-call"> <div class="tool-name"> ${e.tool_name} </div> <div class="tool-data"> <pre> ${JSON.stringify(e.tool_args,null,2)}</pre> </div> ${e.result?i.qy` <div class="tool-data"> <pre> ${JSON.stringify(e.result,null,2)}</pre> </div> `:i.s6} </div> `)} </div> `:i.s6} </div> </div> `:i.s6} ${e.text?i.qy` <ha-markdown breaks cache .content="${e.text}"></ha-markdown> `:i.s6} </div> `:i.s6} </div> `)} </div> <div class="input" slot="primaryAction"> <ha-input id="message-input" @keyup="${this._handleKeyUp}" @input="${this._handleInput}" .label="${this.hass.localize("ui.dialogs.voice_command.input_label")}"> <div slot="end"> ${this._showSendButton||!s?i.qy` <ha-icon-button class="listening-icon" .path="${x}" @click="${this._handleSendMessage}" .disabled="${this._processing}" .label="${this.hass.localize("ui.dialogs.voice_command.send_text")}"> </ha-icon-button> `:i.qy` ${this._audioRecorder?.active?i.qy` <div class="bouncer"> <div class="double-bounce1"></div> <div class="double-bounce2"></div> </div> `:i.s6} <div class="listening-icon"> <ha-icon-button .path="${f}" @click="${this._handleListeningButton}" .disabled="${this._processing}" .label="${this.hass.localize("ui.dialogs.voice_command.start_listening")}"> </ha-icon-button> ${t?null:i.qy` <ha-svg-icon .path="${m}" class="unsupported"></ha-svg-icon> `} </div> `} </div> </ha-input> </div> `}async _scrollMessagesBottom(){const e=this._lastChatMessage;if(e.hasUpdated||await e.updateComplete,this._lastChatMessageImage&&!this._lastChatMessageImage.naturalHeight)try{await this._lastChatMessageImage.decode()}catch(e){console.warn("Failed to decode image:",e)}e.getBoundingClientRect().y<this.getBoundingClientRect().top+24||e.scrollIntoView({behavior:"smooth",block:"start"})}_handleKeyUp(e){const t=e.target;!this._processing&&"Enter"===e.key&&t.value&&(this._processText(t.value),t.value="",this._showSendButton=!1)}_handleInput(e){const t=e.target.value;t&&!this._showSendButton?this._showSendButton=!0:!t&&this._showSendButton&&(this._showSendButton=!1)}_handleSendMessage(){this._messageInput.value&&(this._processText(this._messageInput.value.trim()),this._messageInput.value="",this._showSendButton=!1)}_handleListeningButton(e){e.stopPropagation(),e.preventDefault(),this._toggleListening()}async _toggleListening(){p.N.isSupported?this._audioRecorder?.active?this._stopListening():this._startListening():this._showNotSupportedMessage()}_handleToggleThinking(e){const t=e.currentTarget.index;this._conversation[t]={...this._conversation[t],thinking_expanded:!this._conversation[t].thinking_expanded},this.requestUpdate("_conversation")}_addMessage(e){this._conversation=[...this._conversation,e]}async _showNotSupportedMessage(){this._addMessage({who:"hass",text:i.qy`${this.hass.localize("ui.dialogs.voice_command.not_supported_microphone_browser")} ${this.hass.localize("ui.dialogs.voice_command.not_supported_microphone_documentation",{documentation_link:i.qy`<a target="_blank" rel="noopener noreferrer" href="${(0,_.o)(this.hass,"/docs/configuration/securing/#remote-access")}">${this.hass.localize("ui.dialogs.voice_command.not_supported_microphone_documentation_link")}</a>`})}`,thinking:"",tool_calls:{}})}async _startListening(){this._unloadAudio(),this._processing=!0,this._audioRecorder||(this._audioRecorder=new p.N(e=>{this._audioBuffer?this._audioBuffer.push(e):this._sendAudioChunk(e)})),this._stt_binary_handler_id=void 0,this._audioBuffer=[];const e={who:"user",text:"…",thinking:"",tool_calls:{}};await this._audioRecorder.start(),this._addMessage(e);const t=this._createAddHassMessageProcessor();try{const s=await(0,d.vU)(this.hass,a=>{if("run-start"===a.type)this._stt_binary_handler_id=a.data.runner_data.stt_binary_handler_id,this._audio=new Audio(a.data.tts_output.url),this._audio.play(),this._audio.addEventListener("ended",()=>{this._unloadAudio(),t.continueConversation&&this._startListening()}),this._audio.addEventListener("pause",this._unloadAudio),this._audio.addEventListener("canplaythrough",()=>this._audio?.play()),this._audio.addEventListener("error",()=>{this._unloadAudio(),(0,c.showAlertDialog)(this,{title:"Error playing audio."})});else if("stt-start"===a.type&&this._audioBuffer){for(const e of this._audioBuffer)this._sendAudioChunk(e);this._audioBuffer=void 0}else"stt-end"===a.type?(this._stt_binary_handler_id=void 0,this._stopListening(),e.text=a.data.stt_output.text,this.requestUpdate("_conversation"),t.addMessage()):a.type.startsWith("intent-")?t.processEvent(a):"run-end"===a.type?(this._stt_binary_handler_id=void 0,s()):"error"===a.type&&(this._unloadAudio(),this._stt_binary_handler_id=void 0,"…"===e.text?(e.text=a.data.message,e.error=!0):t.setError(a.data.message),this._stopListening(),this.requestUpdate("_conversation"),s())},{start_stage:"stt",end_stage:this.pipeline?.tts_engine?"tts":"intent",input:{sample_rate:this._audioRecorder.sampleRate},pipeline:this.pipeline?.id,conversation_id:this._conversationId})}catch(e){await(0,c.showAlertDialog)(this,{title:"Error starting pipeline",text:e.message||e}),this._stopListening()}finally{this._processing=!1}}_stopListening(){if(this._audioRecorder?.stop(),this.requestUpdate("_audioRecorder"),this._stt_binary_handler_id){if(this._audioBuffer)for(const e of this._audioBuffer)this._sendAudioChunk(e);this._sendAudioChunk(new Int16Array),this._stt_binary_handler_id=void 0}this._audioBuffer=void 0}_sendAudioChunk(e){if(this.hass.connection.socket.binaryType="arraybuffer",null==this._stt_binary_handler_id)return;const t=new Uint8Array(1+2*e.length);t[0]=this._stt_binary_handler_id,t.set(new Uint8Array(e.buffer),1),this.hass.connection.socket.send(t)}async _processText(e){this._unloadAudio(),this._processing=!0,this._addMessage({who:"user",text:e,thinking:"",tool_calls:{}});const t=this._createAddHassMessageProcessor();t.addMessage();try{const s=await(0,d.vU)(this.hass,e=>{e.type.startsWith("intent-")&&t.processEvent(e),"intent-end"===e.type&&s(),"error"===e.type&&(t.setError(e.data.message),s())},{start_stage:"intent",input:{text:e},end_stage:"intent",pipeline:this.pipeline?.id,conversation_id:this._conversationId})}catch{t.setError(this.hass.localize("ui.dialogs.voice_command.error"))}finally{this._processing=!1}}_createAddHassMessageProcessor(){let e="";const t=()=>{("…"!==s.hassMessage.text||s.hassMessage.thinking||s.hassMessage.tool_calls&&0!==Object.keys(s.hassMessage.tool_calls).length)&&(s.hassMessage.text?.endsWith("…")&&(s.hassMessage.text=s.hassMessage.text.slice(0,-1)),s.hassMessage={who:"hass",text:"…",thinking:"",tool_calls:{},error:!1},this._addMessage(s.hassMessage))},s={continueConversation:!1,hassMessage:{who:"hass",text:"…",thinking:"",tool_calls:{},error:!1},addMessage:()=>{this._addMessage(s.hassMessage)},setError:e=>{t(),s.hassMessage.text=e,s.hassMessage.error=!0,this.requestUpdate("_conversation")},processEvent:t=>{if("intent-progress"===t.type&&t.data.chat_log_delta){const a=t.data.chat_log_delta;if(a.role&&(e=a.role),"assistant"===e){if(a.content&&(s.hassMessage.text.endsWith("…")?s.hassMessage.text=s.hassMessage.text.substring(0,s.hassMessage.text.length-1)+a.content+"…":s.hassMessage.text+=a.content+"…"),a.thinking_content&&(s.hassMessage.thinking+=a.thinking_content),a.tool_calls)for(const e of a.tool_calls)s.hassMessage.tool_calls[e.id]=e;this.requestUpdate("_conversation")}else"tool_result"===e&&s.hassMessage.tool_calls[a.tool_call_id]&&(s.hassMessage.tool_calls[a.tool_call_id].result=a.tool_result,this.requestUpdate("_conversation"))}else if("intent-end"===t.type){this._conversationId=t.data.intent_output.conversation_id,s.continueConversation=t.data.intent_output.continue_conversation;const e=t.data.intent_output.response.speech?.plain.speech;if(!e)return;"error"===t.data.intent_output.response.response_type?s.setError(e):(s.hassMessage.text=e,this.requestUpdate("_conversation"))}}};return s}static get styles(){return[h.dp,i.AH`:host{flex:1;display:flex;flex-direction:column;min-height:0}ha-alert{margin-bottom:var(--ha-space-2)}#message-input::part(wa-base){padding-right:var(--ha-space-1)}.messages{flex:1 1 400px;display:block;box-sizing:border-box;overflow-y:auto;min-height:0;max-height:100%;display:flex;flex-direction:column;padding:0 var(--ha-space-3) var(--ha-space-4)}.input{padding:var(--ha-space-1) var(--ha-space-4) var(--ha-space-6)}.spacer{flex:1}.message-container{display:flex;flex-direction:column;margin:var(--ha-space-2) 0}.message-container.user{align-self:flex-end}.message-container.hass{align-self:flex-start}.message{font-size:var(--ha-font-size-l);clear:both;max-width:-webkit-fill-available;overflow-wrap:break-word;scroll-margin-top:var(--ha-space-6);margin:var(--ha-space-2) 0;padding:var(--ha-space-2);border-radius:var(--ha-border-radius-xl)}@media all and (max-width:450px),all and (max-height:500px){.message{font-size:var(--ha-font-size-l)}}.message.user{margin-left:var(--ha-space-6);margin-inline-start:var(--ha-space-6);margin-inline-end:initial;align-self:flex-end;border-bottom-right-radius:0px;--markdown-link-color:var(--text-primary-color);background-color:var(--chat-background-color-user,var(--primary-color));color:var(--text-primary-color);direction:var(--direction)}.message.hass{margin-right:var(--ha-space-6);margin-inline-end:var(--ha-space-6);margin-inline-start:initial;align-self:flex-start;border-bottom-left-radius:0px;background-color:var(--chat-background-color-hass,var(--secondary-background-color));color:var(--primary-text-color);direction:var(--direction)}.message.error{background-color:var(--error-color);color:var(--text-primary-color)}.thinking-wrapper{margin:calc(var(--ha-space-2) * -1) calc(var(--ha-space-2) * -1) 0 calc(var(--ha-space-2) * -1);overflow:hidden}.thinking-wrapper:last-child{margin-bottom:calc(var(--ha-space-2) * -1)}.thinking-header{display:flex;align-items:center;gap:var(--ha-space-2);width:100%;background:0 0;border:none;padding:var(--ha-space-2);cursor:pointer;text-align:left;color:var(--secondary-text-color);transition:color .2s}.thinking-header:focus,.thinking-header:hover{outline:0;color:var(--primary-text-color)}.thinking-label{font-size:var(--ha-font-size-m);display:flex;align-items:center;gap:var(--ha-space-2)}.thinking-header ha-svg-icon{--mdc-icon-size:16px}.thinking-content{max-height:0;overflow:hidden;transition:max-height .3s ease-in-out,padding .3s;padding:0 var(--ha-space-2);font-size:var(--ha-font-size-m);color:var(--secondary-text-color)}.thinking-wrapper.expanded .thinking-content{max-height:500px;padding:var(--ha-space-2);overflow-y:auto;display:flex;flex-direction:column;gap:var(--ha-space-2)}.tool-calls{display:flex;flex-direction:column;gap:var(--ha-space-1)}.tool-call{padding:var(--ha-space-1) var(--ha-space-2);border-left:2px solid var(--divider-color);margin-bottom:var(--ha-space-1)}.tool-name{font-weight:700;display:flex;align-items:center;gap:var(--ha-space-1)}.tool-data{font-family:var(--code-font-family, monospace);font-size:.9em;background:var(--markdown-code-background-color);padding:var(--ha-space-1);border-radius:var(--ha-border-radius-s);margin-top:var(--ha-space-1);overflow-x:auto}.tool-data pre{margin:0;white-space:pre-wrap;word-break:break-all}ha-markdown{--markdown-image-border-radius:calc(var(--ha-border-radius-xl) / 2);--markdown-table-border-color:var(--divider-color);--markdown-code-background-color:var(--primary-background-color);--markdown-code-text-color:var(--primary-text-color);--markdown-list-indent:1.15em}ha-markdown:not(:has(ha-markdown-element)){min-height:1lh;min-width:1lh;flex-shrink:0}.bouncer{width:48px;height:48px;position:absolute}.double-bounce1,.double-bounce2{width:48px;height:48px;border-radius:var(--ha-border-radius-circle);background-color:var(--primary-color);opacity:.2;position:absolute;top:0;left:0;-webkit-animation:sk-bounce 2s infinite ease-in-out;animation:sk-bounce 2s infinite ease-in-out}.double-bounce2{-webkit-animation-delay:-1s;animation-delay:-1s}@-webkit-keyframes sk-bounce{0%,100%{-webkit-transform:scale(0)}50%{-webkit-transform:scale(1)}}@keyframes sk-bounce{0%,100%{transform:scale(0);-webkit-transform:scale(0)}50%{transform:scale(1);-webkit-transform:scale(1)}}.unsupported{color:var(--error-color);position:absolute;--mdc-icon-size:16px;right:5px;inset-inline-end:5px;inset-inline-start:initial;top:0px}`]}constructor(...e){super(...e),this.disableSpeech=!1,this._conversation=[],this._showSendButton=!1,this._processing=!1,this._conversationId=null,this._unloadAudio=()=>{this._audio&&(this._audio.pause(),this._audio.removeAttribute("src"),this._audio=void 0)}}}(0,a.Cg)([(0,o.MZ)({attribute:!1})],w.prototype,"hass",void 0),(0,a.Cg)([(0,o.MZ)({attribute:!1})],w.prototype,"pipeline",void 0),(0,a.Cg)([(0,o.MZ)({type:Boolean,attribute:"disable-speech"})],w.prototype,"disableSpeech",void 0),(0,a.Cg)([(0,o.MZ)({attribute:!1})],w.prototype,"startListening",void 0),(0,a.Cg)([(0,o.P)("#message-input")],w.prototype,"_messageInput",void 0),(0,a.Cg)([(0,o.P)(".message:last-child")],w.prototype,"_lastChatMessage",void 0),(0,a.Cg)([(0,o.P)(".message:last-child img:last-of-type")],w.prototype,"_lastChatMessageImage",void 0),(0,a.Cg)([(0,o.wk)()],w.prototype,"_conversation",void 0),(0,a.Cg)([(0,o.wk)()],w.prototype,"_showSendButton",void 0),(0,a.Cg)([(0,o.wk)()],w.prototype,"_processing",void 0),w=(0,a.Cg)([(0,o.EM)("ha-assist-chat")],w),t()}catch(e){t(e)}})},88433(e,t,s){if(s.d(t,{RW:()=>n,ZE:()=>i,e1:()=>r,vc:()=>o}),66649!=s.j)var a=s(44537);var i=function(e){return e[e.CONTROL=1]="CONTROL",e}({});const o=(e,t,s)=>e.callWS({type:"conversation/agent/list",language:t,country:s}),n=(e,t,s,i)=>e.callWS({type:"conversation/agent/homeassistant/debug",sentences:(0,a.e)(t),language:s,device_id:i}),r=(e,t,s)=>e.callWS({type:"conversation/agent/homeassistant/language_scores",language:t,country:s})},74209(e,t,s){s.d(t,{N:()=>a});s(14603),s(47566),s(98721);class a{get active(){return this._active}get sampleRate(){return this._context?.sampleRate}static get isSupported(){return window.isSecureContext&&(window.AudioContext||window.webkitAudioContext)}async start(){if(this._context&&this._stream&&this._source&&this._recorder)this._stream.getTracks()[0].enabled=!0,await this._context.resume(),this._active=!0;else try{await this._createContext()}catch(e){console.error(e),this._active=!1}}async stop(){this._active=!1,this._stream&&(this._stream.getTracks()[0].enabled=!1),await(this._context?.suspend())}close(){this._active=!1,this._stream?.getTracks()[0].stop(),this._recorder&&(this._recorder.port.onmessage=null),this._source?.disconnect(),this._context?.close(),this._stream=void 0,this._source=void 0,this._recorder=void 0,this._context=void 0}async _createContext(){const e=new(AudioContext||webkitAudioContext);this._stream=await navigator.mediaDevices.getUserMedia({audio:!0}),await e.audioWorklet.addModule(new URL(s.p+s.u(33921),s.b)),this._context=e,this._source=this._context.createMediaStreamSource(this._stream),this._recorder=new AudioWorkletNode(this._context,"recorder-worklet"),this._recorder.port.onmessage=e=>{this._active&&this._callback(e.data)},this._active=!0,this._source.connect(this._recorder)}constructor(e){this._active=!1,this._callback=e}}}};