From 876f370994f99e3b9133c6ced3e68738e5e89467 Mon Sep 17 00:00:00 2001 From: qJake Date: Sat, 8 Feb 2020 10:06:49 -0600 Subject: [PATCH] 1.0.4 Hass.io API fixes --- Docker/BuildHaccContainers.ps1 | 2 +- HADotNet.CommandCenter/Controllers/AdminController.cs | 4 +++- HADotNet.CommandCenter/HADotNet.CommandCenter.csproj | 4 ++-- HADotNet.CommandCenter/Views/Admin/Settings.cshtml | 6 ++++++ HADotNet.CommandCenter/Views/Dashboard/Index.cshtml | 1 + HADotNet.CommandCenter/wwwroot/js/app.js | 4 ++-- HADotNet.CommandCenter/wwwroot/js/app.min.js | 2 +- HADotNet.CommandCenter/wwwroot/js/app.ts | 4 ++-- .../wwwroot/js/typings/window-options.d.ts | 1 + 9 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Docker/BuildHaccContainers.ps1 b/Docker/BuildHaccContainers.ps1 index 70dc93d..3c45b1d 100644 --- a/Docker/BuildHaccContainers.ps1 +++ b/Docker/BuildHaccContainers.ps1 @@ -1,4 +1,4 @@ -$version = '1.0.3' +$version = '1.0.4' function Test-ExitCode ([int] $Expected = 0) { diff --git a/HADotNet.CommandCenter/Controllers/AdminController.cs b/HADotNet.CommandCenter/Controllers/AdminController.cs index 8d7166f..56bea25 100644 --- a/HADotNet.CommandCenter/Controllers/AdminController.cs +++ b/HADotNet.CommandCenter/Controllers/AdminController.cs @@ -90,12 +90,14 @@ public async Task Settings() } [HttpPost] - public async Task Settings(SystemSettings newSettings) + public async Task Settings([FromForm] SystemSettings newSettings) { try { if (ModelState.IsValid) { + newSettings ??= new SystemSettings(); + if (newSettings.IsHassIo) { newSettings.BaseUri = "http://hassio/homeassistant"; diff --git a/HADotNet.CommandCenter/HADotNet.CommandCenter.csproj b/HADotNet.CommandCenter/HADotNet.CommandCenter.csproj index 88e16ba..3330e8a 100644 --- a/HADotNet.CommandCenter/HADotNet.CommandCenter.csproj +++ b/HADotNet.CommandCenter/HADotNet.CommandCenter.csproj @@ -5,8 +5,8 @@ win10;alpine.3.10-x64;debian.10-arm Latest Linux - 1.0.3.0 - 1.0.3.0 + 1.0.4.0 + 1.0.4.0 false false false diff --git a/HADotNet.CommandCenter/Views/Admin/Settings.cshtml b/HADotNet.CommandCenter/Views/Admin/Settings.cshtml index 342be49..f51f9da 100644 --- a/HADotNet.CommandCenter/Views/Admin/Settings.cshtml +++ b/HADotNet.CommandCenter/Views/Admin/Settings.cshtml @@ -49,6 +49,9 @@ }
+ + + @if (Model?.IsHassIo ?? false) {
@@ -59,6 +62,9 @@
+ + +
diff --git a/HADotNet.CommandCenter/Views/Dashboard/Index.cshtml b/HADotNet.CommandCenter/Views/Dashboard/Index.cshtml index 15c2405..c12ceda 100644 --- a/HADotNet.CommandCenter/Views/Dashboard/Index.cshtml +++ b/HADotNet.CommandCenter/Views/Dashboard/Index.cshtml @@ -130,6 +130,7 @@ window.ccOptions.autoReturn = @Model.CurrentPage.AutoReturnSeconds; window.ccOptions.pageId = '@Model.CurrentPage.Name'; window.ccOptions.baseUrl = '@Model.SystemSettings.BaseUri'; + window.ccOptions.socketUrl = '@(Model.SystemSettings.IsHassIo ? "http://hassio/homeassistant/websocket" : Model.SystemSettings.BaseUri)'; window.ccOptions.overrideAssetUrl = '@Model.SystemSettings.OverrideAssetUri'; window.ccOptions.token = '@Model.SystemSettings.AccessToken'; diff --git a/HADotNet.CommandCenter/wwwroot/js/app.js b/HADotNet.CommandCenter/wwwroot/js/app.js index 0f48b6e..0686d61 100644 --- a/HADotNet.CommandCenter/wwwroot/js/app.js +++ b/HADotNet.CommandCenter/wwwroot/js/app.js @@ -997,8 +997,8 @@ class CommandCenter { } } initUser() { - if (window.ccOptions.baseUrl) { - this.conn = new HAConnection(window.ccOptions.baseUrl); + if (window.ccOptions.socketUrl) { + this.conn = new HAConnection(window.ccOptions.socketUrl); } this.conn.OnConnectionStateChanged.on(state => { if (state == HAConnectionState.Closed) { diff --git a/HADotNet.CommandCenter/wwwroot/js/app.min.js b/HADotNet.CommandCenter/wwwroot/js/app.min.js index 6d5f38e..8240827 100644 --- a/HADotNet.CommandCenter/wwwroot/js/app.min.js +++ b/HADotNet.CommandCenter/wwwroot/js/app.min.js @@ -1 +1 @@ -var HAMessageType,HAErrorType,HAEventType,HAResponseType,HAConnectionState,PageMode,__app,WeatherTileEntities;(function(n){n.Auth="auth";n.AuthRequired="auth_required";n.AuthOK="auth_ok";n.AuthInvalid="auth_invalid";n.GetStates="get_states";n.StateChanged="state_changed";n.SubscribeToEvents="subscribe_events";n.CameraThumbnail="camera_thumbnail";n.MediaThumbnail="media_player_thumbnail";n.Result="result";n.Event="event";n.Ping="ping";n.Pong="pong"})(HAMessageType||(HAMessageType={})),function(n){n.IDReuse="id_reuse"}(HAErrorType||(HAErrorType={})),function(n){n.StateChanged="state_changed"}(HAEventType||(HAEventType={})),function(n){n[n.StateList=0]="StateList"}(HAResponseType||(HAResponseType={})),function(n){n[n.Closed=0]="Closed";n[n.Opening=1]="Opening";n[n.Auth=2]="Auth";n[n.Open=3]="Open"}(HAConnectionState||(HAConnectionState={}));class ConnectionEvent{constructor(){this.handlers=[]}on(n){this.handlers.push(n)}off(n){this.handlers=this.handlers.filter(t=>t!==n)}invoke(n){this.handlers.slice(0).forEach(t=>t(n))}event(){return this}}class HAConnection{constructor(n){this.targetInstance=n;this.PING_INTERVAL=3e4;this.evStateChanged=new ConnectionEvent;this.evConnectionStateChanged=new ConnectionEvent;this.expectedResults={};this.expectedPromises={};this.state=HAConnectionState.Closed}get OnStateChanged(){return this.evStateChanged.event()}get OnConnectionStateChanged(){return this.evConnectionStateChanged.event()}get ConnectionState(){return this.state}initialize(){this.state=HAConnectionState.Opening;this.evConnectionStateChanged.invoke(this.state);this.msgId=1;this.ws&&this.ws.readyState===this.ws.OPEN||(this.ws=new ReconnectingWebSocket(this.parseSocketUrl(this.targetInstance),null,{automaticOpen:!1}));this.ws.addEventListener("open",()=>this.handleOpen());this.ws.addEventListener("close",()=>this.handleClose());this.ws.addEventListener("message",n=>this.handleMessage(n));this.ws.open()}refreshAllStates(){this.sendStateRequest()}getCameraImage(n){return new Promise(t=>{this.sendCameraThumbnailRequest(n,t)})}getMediaImage(n){return new Promise(t=>{this.sendMediaThumbnailRequest(n,t)})}handleMessage(n){let t=JSON.parse(n.data);if(this.isHAMessage(t)){console.debug("-> RCV:"+t.type,t);switch(t.type){case HAMessageType.AuthRequired:this.sendAuth();return;case HAMessageType.AuthOK:this.isReady();return;case HAMessageType.Result:let n=t;n.success?this.handleHaExpectedResult(n)?delete this.expectedResults[n.id]:console.info("HA result OK",n):this.handleHaError(n.error);return;case HAMessageType.Event:this.handleHaEvent(t);return;case HAMessageType.AuthInvalid:console.error("Unable to authenticate with Home Assistant API. Check settings.");this.ws.maxReconnectAttempts=1;this.ws.close();return}}else console.warn("-> RCV",n.data)}isReady(){this.state=HAConnectionState.Open;this.evConnectionStateChanged.invoke(this.state);this.pingInterval=window.setInterval(()=>this.sendPing(),this.PING_INTERVAL);this.sendEventSubscriptionRequest(HAEventType.StateChanged)}handleHaEvent(n){this.isHAEventStateChanged(n.event)&&this.eventStateChanged(n.event)}handleHaExpectedResult(n){let t=this.expectedResults[n.id];if(typeof t!="undefined"){switch(t){case HAResponseType.StateList:this.resultStateList(n.result);break;default:console.warn("Unhandled response type for this message.",n)}return!0}let i=this.expectedPromises[n.id];return typeof i!="undefined"?(i(n),!0):!1}resultStateList(n){for(var t of n)this.evStateChanged.invoke({data:{entity_id:t.entity_id,new_state:t,old_state:null},event_type:HAEventType.StateChanged,origin:null,time_fired:null})}eventStateChanged(n){var i,t;console.info(`HA State Changed [${n.data.entity_id}] ${t=(i=n.data.old_state)===null||i===void 0?void 0:i.state,t!==null&&t!==void 0?t:""} -> ${n.data.new_state.state}`);this.evStateChanged.invoke(n)}sendAuth(){this.send({type:HAMessageType.Auth,access_token:window.ccOptions.token})}sendStateRequest(){let n=this.send({type:HAMessageType.GetStates});this.expectedResults[n]=HAResponseType.StateList}sendCameraThumbnailRequest(n,t){let i=this.send({type:HAMessageType.CameraThumbnail,entity_id:n});this.expectedPromises[i]=t}sendMediaThumbnailRequest(n,t){let i=this.send({type:HAMessageType.MediaThumbnail,entity_id:n});this.expectedPromises[i]=t}sendEventSubscriptionRequest(n){this.send({type:HAMessageType.SubscribeToEvents,event_type:n})}handleHaError(n){console.error("HA API Error ["+n.code+"] "+n.message)}sendPing(){this.send({type:HAMessageType.Ping})}send(n){return console.debug("<- SND:"+n.type,n),this.state!==HAConnectionState.Closed&&this.state!==HAConnectionState.Opening?(this.state===HAConnectionState.Open&&(n.id=this.msgId++),this.ws.send(JSON.stringify(n)),n.id):(console.warn("Tried to send socket message, but connection isn't ready.",n),-1)}handleOpen(){this.state=HAConnectionState.Auth;this.evConnectionStateChanged.invoke(this.state)}handleClose(){this.state=HAConnectionState.Closed;this.evConnectionStateChanged.invoke(this.state);this.pingInterval&&(window.clearInterval(this.pingInterval),this.pingInterval=0)}parseSocketUrl(n){let t=document.createElement("a");return t.href=n,`${t.protocol.toLowerCase()==="https:"?"wss":"ws"}://${t.host}/api/websocket`}isHAMessage(n){return n&&typeof n.type=="string"}isHAEventStateChanged(n){return n&&typeof n.event_type=="string"&&n.event_type===HAEventType.StateChanged}}class PageUtils{static ConfirmDelete(n){return confirm("This item will be permanently deleted. This action cannot be undone.\n\nAre you sure?")?!0:(n.preventDefault(),!1)}}(function(n){n[n.User=0]="User";n[n.Admin=1]="Admin"})(PageMode||(PageMode={}));class Utils{static delayPromise(n,...t){return new Promise(i=>setTimeout(()=>i(t),n))}static sleep(n){const i=Date.now();let t=null;do t=Date.now();while(t-i{let r=new Image;r.onload=()=>t(n);r.onerror=n=>i(n);r.src=n})}static convertDegreesToCardinal(n){return["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"][Math.floor(n/22.5+.5)%16]}static convertCardinalToIcon(n){return{N:"arrow-up-thick",NNE:"arrow-up-thick",NE:"arrow-top-right-thick",ENE:"arrow-right-thick",E:"arrow-right-thick",ESE:"arrow-right-thick",SE:"arrow-bottom-right-thick",SSE:"arrow-down-thick",S:"arrow-down-thick",SSW:"arrow-down-thick",SW:"arrow-bottom-left-thick",WSW:"arrow-left-thick",W:"arrow-left-thick",WNW:"arrow-left-thick",NW:"arrow-top-left-thick",NNW:"arrow-up-thick"}[n]}}class Tile{constructor(n,t,i,r,u){var e,f;this.page=n;this.name=t;this.conn=i;this.canLoad=u.canLoad;this.canClick=u.canClick;this.entityIds=[];this.loaded=!this.canLoad;this.el=$(`.tiles .tile[data-tile-name="${t}"]`);this.canClick&&this.el.click(()=>{this.onClick()});f=this.el.data("tile-entityid");typeof f=="object"&&Array.isArray(f)?this.entityIds=f:this.entityIds.push((e=f)===null||e===void 0?void 0:e.toString());i.on("SendSystemConfig",(n,i)=>{t==n&&(console.debug(`Received "SendSystemConfig" for tile: ${n}`),this.config=i,this.canLoad)});i.on("SendTile",n=>{t==n.name&&(console.debug(`Received "SendTile" for tile: ${n.name}`),this.updateTile(n))});i.on("SendCalendarInfo",(n,i,r)=>{t==n.name&&this.updateCalendar(i,r)});i.on("SendWarning",n=>console.warn(n));i.on("SendDateTime",(n,i,r)=>{t==n.name&&this.updateDateTime(n,i,r)});this.requestConfig(n);this.canLoad&&this.requestState()}onClick(){return this.conn.invoke("OnTileClicked",this.page,this.name)}updateTile(){this.loaded=!0;this.disableLoading()}updateState(){}updateCalendar(){}updateDateTime(){}requestState(){this.conn.invoke("RequestTileState",this.page,this.name)}requestConfig(n){this.conn.invoke("RequestConfig",n,this.name)}disableLoading(){this.loadingDebouncer&&window.clearTimeout(this.loadingDebouncer);this.loadingDebouncer=null;this.el.removeClass("tile-loading")}getEntityIds(){return this.entityIds}}class BlankTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}}class LabelTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}}class DateTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!0})}updateDateTime(n,t,i){$(`#tile-${n.name}`).find("span[value-date]").text(t);$(`#tile-${n.name}`).find("span[value-time]").text(i);super.updateDateTime();setTimeout(()=>{this.requestState(9500)},1e4)}}class StateTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let i=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(i=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(i);let t=this.tile.roundDecimals?parseInt(n.new_state.state).toString():n.new_state.state;this.tile.displayTextOff&&t.toLowerCase()==="off"?t=this.tile.displayTextOff:this.tile.displayTextOn&&t.toLowerCase()==="on"&&(t=this.tile.displayTextOn);n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());$(`#tile-${this.tile.name}`).find("span[value-state]").text(t)}}class LightTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(t=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(t);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass(`mdi-${this.tile.displayIcon} mdi-${this.tile.displayOffIcon}`).addClass(`mdi mdi-${n.new_state.state.toLowerCase()==="on"?Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayIcon):Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayOffIcon||this.tile.displayIcon)}`);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass("state-off state-on").addClass(n.new_state.state.toLowerCase()==="on"?"state-on":"state-off");this.tile.onColor&&n.new_state.state.toLowerCase()==="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.onColor);this.tile.offColor&&n.new_state.state.toLowerCase()!=="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.offColor)}}class SwitchTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(t=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(t);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass(`mdi-${this.tile.displayIcon} mdi-${this.tile.displayOffIcon}`).addClass(`mdi mdi-${n.new_state.state.toLowerCase()==="on"?Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayIcon):Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayOffIcon||this.tile.displayIcon)}`);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass("state-off state-on").addClass(n.new_state.state.toLowerCase()==="on"?"state-on":"state-off");this.tile.onColor&&n.new_state.state.toLowerCase()==="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.onColor);this.tile.offColor&&n.new_state.state.toLowerCase()!=="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.offColor);super.updateState(n)}}class PersonTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.entity_picture?n.new_state.attributes.entity_picture.toString():"",i=n.new_state.state.replace("_"," "),r=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(r=this.tile.overrideLabel);let u=i.toLowerCase()==="home";t.toLowerCase().startsWith("http")||(t=Utils.resolveAssetUrl(window.ccOptions.baseUrl,window.ccOptions.overrideAssetUrl,t));$(`#tile-${this.tile.name}`).find("span[value-name]").text(r);$(`#tile-${this.tile.name}`).find("span[value-location]").text(i);$(`#tile-${this.tile.name}`).find("span[value-picture]").css("background-image",`url(${t})`).removeClass("bw");u||$(`#tile-${this.tile.name}`).find("span[value-picture]").addClass("bw")}}class WeatherTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canLoad:!0,canClick:!1});this.page=n;this.name=t;this.conn=i;this.windSpeed="";this.windDir="";this.hi="";this.lo="";this.iconEl=this.el.find(".condition-icon")[0];const u=document.defaultView.getComputedStyle(this.el[0]).color;this.skycons=new Skycons({color:u,resizeClear:!0});this.skycons.add(this.iconEl,Skycons.CLEAR_DAY)}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state==null?null:n.new_state.state;switch(n.entity_id){case this.tile.entityId:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());$(`#tile-${this.tile.name}`).find("span[value-temp]").text(t);break;case this.tile.highTempEntity:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());this.hi=` ${t}`;break;case this.tile.lowTempEntity:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());this.lo=` ${t}`;break;case this.tile.summaryEntity:$(`#tile-${this.tile.name}`).find("span[value-summary]").text(t);break;case this.tile.precipChanceEntity:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());$(`#tile-${this.tile.name}`).find("span[value-rain]").text(`Rain: ${t}`);break;case this.tile.windSpeedEntity:this.windSpeed=this.tile.roundWindSpeed?parseInt(t).toString():t;n.new_state.attributes.unit_of_measurement&&(this.windSpeed+=n.new_state.attributes.unit_of_measurement.toString());break;case this.tile.windDirectionEntity:this.windDir=Utils.convertDegreesToCardinal(parseInt(t));this.windDir=` ${this.windDir}`;break;case this.tile.iconEntity:t?(this.skycons.set(this.iconEl,t),this.skycons.play()):(this.skycons.remove(this.iconEl),$(this.iconEl).hide())}$(`#tile-${this.tile.name}`).find("span[value-hi-lo]").html(`${this.hi&&this.lo?this.hi+" / "+this.lo:this.hi+this.lo}`);$(`#tile-${this.tile.name}`).find("span[value-wind]").html(`Wind: ${(this.windSpeed+" "+this.windDir).trim()}`)}}class CameraTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!0});this.haConn=r}updateTile(n){this.tile=n;super.updateTile(n);this.updateCameraFeed();window.setInterval(()=>this.updateCameraFeed(),(this.tile.refreshRate>0?this.tile.refreshRate:1)*1e3)}updateCameraFeed(){this.haConn.getCameraImage(this.tile.entityId).then(n=>{let t=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?this.tile.imageCropMode.toLowerCase():"100% 100%",i=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?"50% 50%":"0 0";$(`#tile-${this.tile.name}`).css({backgroundImage:`url('data:${n.result.content_type};base64,${n.result.content}')`,backgroundRepeat:"no-repeat",backgroundPosition:i,backgroundSize:t})})}}class SceneTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(t=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(t);$(`#tile-${this.tile.name}`).find("span[value-icon]").addClass(`mdi mdi-${this.tile.displayIcon||"filmstrip"}`);$(`#tile-${this.tile.name} .value`).css("color",this.tile.iconColor)}}class MediaTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1});this.haConn=r}updateTile(n){this.tile=n;super.updateTile(n);this.updateMediaImage();window.setInterval(()=>this.updateMediaImage(),(this.tile.refreshRate>0?this.tile.refreshRate:1)*1e3)}updateMediaImage(){var n;if((n=this.state)===null||n===void 0?void 0:n.new_state){let t=this.state.new_state.attributes.friendly_name.toString();(this.tile.overrideLabel&&(t=this.tile.overrideLabel),this.state.new_state.attributes.entity_picture||(t+=` (${this.state.new_state.state})`),$(`#tile-${this.tile.name}`).toggleClass("media-idle",this.state.new_state.attributes.media_title==="Nothing playing"||(this.state.new_state.state=="paused"||this.state.new_state.state=="idle")&&!this.state.new_state.attributes.entity_picture),$(`#tile-${this.tile.name}`).find("span[value-name]").text(this.tile.showLabel?t:""),$(`#tile-${this.tile.name}`).find("span[value-title]").text(this.tile.showTitle&&this.state.new_state.attributes.media_title&&this.state.new_state.attributes.media_title!=="Nothing playing"?this.state.new_state.attributes.media_title.toString():""),this.state.new_state.attributes.entity_picture)&&this.haConn.getMediaImage(this.tile.entityId).then(n=>{let t=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?this.tile.imageCropMode.toLowerCase():"100% 100%",i=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?"50% 50%":"0 0";$(`#tile-${this.tile.name}`).css({backgroundImage:`url('data:${n.result.content_type};base64,${n.result.content}')`,backgroundRepeat:"no-repeat",backgroundPosition:i,backgroundSize:t})})}}updateState(n){this.state=n}}class NavigationTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.navTile=n;$(`#tile-${n.name}`).find("span[value-name]").text(this.navTile.label);$(`#tile-${n.name}`).find("span[value-icon]").addClass(`mdi mdi-${this.navTile.displayIcon}`);super.updateTile()}onClick(){switch(this.navTile.mode.toLowerCase().trim()){case"home":window.location.href="/d/";return;case"refresh":window.location.reload();return;case"nav":window.location.href=`/d/${this.navTile.target}`;return}}}class CalendarTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canLoad:!0,canClick:!1});this.eventContainer=$(`#tile-${t} div.calendar-events`)}updateTile(n){this.tile=n;super.updateTile(n)}updateCalendar(n,t){let i=n.attributes.friendly_name.toString();this.tile.overrideLabel&&(i=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(i);this.refreshEvents(t);super.updateCalendar();this.tile.refreshRate>0&&setTimeout(()=>{this.requestState(1e3)},this.tile.refreshRate*1e3)}refreshEvents(n){var t;if(this.eventContainer.empty(),n.length){let i="";for(let r=0;r${f}`),i=f);this.eventContainer.append(`

${u.summary}${u.start.dateTime?moment((t=u.start.dateTime,t!==null&&t!==void 0?t:u.start.date)).format("LT"):"All Day"}

`)}}else this.eventContainer.append('No events!<\/span>')}getEventHeader(n){var i;const r=moment(),u=moment().add(1,"day");let f=this.formatHeader(r),e=this.formatHeader(u);const o=moment((i=n.start.dateTime,i!==null&&i!==void 0?i:n.start.date));let t=this.formatHeader(o);return t===f?t+=" (Today)":t===e&&(t+=" (Tomorrow)"),t}formatHeader(n){return n.format("ddd")+", "+n.format("ll")}}class TileMap{}TileMap.ClassMap={Blank:BlankTile,Label:LabelTile,Date:DateTile,State:StateTile,Light:LightTile,Switch:SwitchTile,Person:PersonTile,Weather:WeatherTile,Camera:CameraTile,Scene:SceneTile,Media:MediaTile,Navigation:NavigationTile,Calendar:CalendarTile};class CommandCenter{constructor(){this.tiles=[];$(()=>this.init())}init(){window.ccOptions.mode==PageMode.Admin?this.initAdmin():this.initUser();this.initializeMdiPreview();this.initializeColorPreview();this.initializeNightlyRefresh()}initAdmin(){$(window).on("beforeunload",n=>{if(this.pageIsDirty&&$(n.target.activeElement).prop("type")!=="submit")return"You have unsaved changes. Are you sure you want to leave?"});if($("#importTheme, #importConfig").click(()=>{confirm("WARNING: This will OVERWRITE your current settings. Export first if you want to save what you have now! Continue?")&&$("#importBrowser").click()}),$("#importBrowser").change(()=>{$("#importForm").submit()}),$("#resetConfig").click(n=>confirm("WARNING: This will COMPLETELY RESET your HACC installation and PERMANENTLY DELETE all of your tiles, themes, and settings. Are you sure you want to reset your config?")?!0:(n.preventDefault(),!1)),$(".ui.accordion").accordion(),$(".ui.checkbox").checkbox(),$(".ui.dropdown").not(".no-placeholder").dropdown({fullTextSearch:!0}),$(".ui.no-placeholder.dropdown").dropdown({placeholder:!1}),$("#Page_PageFontFace option").each(function(n,t){$(t).parent().siblings(".menu").find('.item[data-value="'+$(t).text()+'"]').css("font-family",$(t).text())}),$(".preview-layout-grid").length){$("#auto-layout").click(()=>{confirm("This will reset the layout for this page and attempt to automatically arrange all tiles evenly.\n\nYour tiles will all probably change locations. You have been warned. :)\n\nAre you sure you want to reset the layout?")&&this.pk.layout()});$(".preview-layout-grid").prepend(`
`);this.pk=window.ccOptions?new Packery(".preview-layout-grid",{itemSelector:".preview-layout-item",columnWidth:window.ccOptions.tilePreviewSize,rowHeight:window.ccOptions.tilePreviewSize,gutter:window.ccOptions.tilePreviewPadding,initLayout:!1}):new Packery(".preview-layout-grid",{itemSelector:".preview-layout-item",columnWidth:".preview-layout-item",rowHeight:".preview-layout-item",gutter:window.ccOptions.tilePreviewPadding,initLayout:!1});this.pk.on("layoutComplete",()=>this.writeItemLayout());this.pk.on("dragItemPositioned",()=>{setTimeout(()=>{this.writeItemLayout(),this.pageIsDirty=!0},25)});this.writeItemLayout();typeof Draggabilly=="function"?$(".preview-layout-item").each((n,t)=>this.pk.bindDraggabillyEvents(new Draggabilly(t,{containment:".preview-layout-grid"}))):console.warn("Draggabilly is not available - drag and drop interface will not work.");$("#grid__tmp").remove();this.pk.initShiftLayout(Array.from(document.querySelectorAll(".preview-layout-grid > .preview-layout-item")))}}initUser(){window.ccOptions.baseUrl&&(this.conn=new HAConnection(window.ccOptions.baseUrl));this.conn.OnConnectionStateChanged.on(n=>{n==HAConnectionState.Closed?$("#alerts").show().find(".alert-message").text("[H] Connection lost, reconnecting..."):n==HAConnectionState.Open&&$("#alerts").hide()});this.conn.OnStateChanged.on(n=>{var t=this.findTilesByEntityId(n.data.entity_id);for(var i of t)i.updateState(n.data),console.info(`Updating tile for entity "${n.data.entity_id}" to state "${n.data.new_state.state}".`)});this.conn.initialize();this.tileConn=(new signalR.HubConnectionBuilder).withUrl("/hubs/tile").build();this.tileConn.onclose(()=>{$("#alerts").show().find(".alert-message").text("[S] Connection lost, reconnecting..."),window.setTimeout(()=>{window.location.reload()},1e4)});this.tileConn.start().then(()=>{$(".tiles .tile").each((n,t)=>{try{let n=new TileMap.ClassMap[$(t).data("tile-type").toString()](window.ccOptions.pageId,$(t).data("tile-name"),this.tileConn,this.conn);this.tiles.push(n)}catch(i){console.error('Error instantiating class "'+($(t).data("tile-type")||"__MISSING__")+'Tile". Was it added to the tile type map?',i,t)}}),this.initHandle||(this.initHandle=window.setInterval(()=>this.waitAndPerformInit(),25)),window.ccOptions.autoReturn>0&&window.setTimeout(()=>window.location.href="/d/",window.ccOptions.autoReturn*1e3)})}waitAndPerformInit(){(this.conn||(console.warn("Cancelling tile initialization - connection is not set up."),window.clearInterval(this.initHandle)),this.conn.ConnectionState===HAConnectionState.Open)&&this.tiles.filter(n=>n.loaded).length===this.tiles.length&&(this.conn.refreshAllStates(),window.clearInterval(this.initHandle))}findTilesByEntityId(n){return this.tiles.filter(t=>{let i=t.getEntityIds();return i.some(t=>t.toLowerCase()===n.toLowerCase())})}initializeNightlyRefresh(){window.setInterval(()=>{(new Date).getHours()==2&&window.setTimeout(()=>{window.location.reload()},36e5)},35e5)}initializeMdiPreview(){$(".mdi-icon-placeholder + input").each((n,t)=>{$(t).keyup(n=>{this.refreshDynamicIcon(n.currentTarget)}),this.refreshDynamicIcon(t)})}refreshDynamicIcon(n){$(n).parent().children(".mdi-icon-placeholder").attr("class","large icon mdi-icon-placeholder").addClass(`mdi mdi-${$(n).val()}`)}initializeColorPreview(){$(".color-preview + input").each((n,t)=>{$(t).keyup(n=>{this.refreshDynamicColor(n.currentTarget)}),this.refreshDynamicColor(t)})}refreshDynamicColor(n){$(n).parent().children(".color-preview").css("color",`${$(n).val()}`)}writeItemLayout(){var n=[],t=this.pk.getItemElements();for(let i=0;it!==n)}invoke(n){this.handlers.slice(0).forEach(t=>t(n))}event(){return this}}class HAConnection{constructor(n){this.targetInstance=n;this.PING_INTERVAL=3e4;this.evStateChanged=new ConnectionEvent;this.evConnectionStateChanged=new ConnectionEvent;this.expectedResults={};this.expectedPromises={};this.state=HAConnectionState.Closed}get OnStateChanged(){return this.evStateChanged.event()}get OnConnectionStateChanged(){return this.evConnectionStateChanged.event()}get ConnectionState(){return this.state}initialize(){this.state=HAConnectionState.Opening;this.evConnectionStateChanged.invoke(this.state);this.msgId=1;this.ws&&this.ws.readyState===this.ws.OPEN||(this.ws=new ReconnectingWebSocket(this.parseSocketUrl(this.targetInstance),null,{automaticOpen:!1}));this.ws.addEventListener("open",()=>this.handleOpen());this.ws.addEventListener("close",()=>this.handleClose());this.ws.addEventListener("message",n=>this.handleMessage(n));this.ws.open()}refreshAllStates(){this.sendStateRequest()}getCameraImage(n){return new Promise(t=>{this.sendCameraThumbnailRequest(n,t)})}getMediaImage(n){return new Promise(t=>{this.sendMediaThumbnailRequest(n,t)})}handleMessage(n){let t=JSON.parse(n.data);if(this.isHAMessage(t)){console.debug("-> RCV:"+t.type,t);switch(t.type){case HAMessageType.AuthRequired:this.sendAuth();return;case HAMessageType.AuthOK:this.isReady();return;case HAMessageType.Result:let n=t;n.success?this.handleHaExpectedResult(n)?delete this.expectedResults[n.id]:console.info("HA result OK",n):this.handleHaError(n.error);return;case HAMessageType.Event:this.handleHaEvent(t);return;case HAMessageType.AuthInvalid:console.error("Unable to authenticate with Home Assistant API. Check settings.");this.ws.maxReconnectAttempts=1;this.ws.close();return}}else console.warn("-> RCV",n.data)}isReady(){this.state=HAConnectionState.Open;this.evConnectionStateChanged.invoke(this.state);this.pingInterval=window.setInterval(()=>this.sendPing(),this.PING_INTERVAL);this.sendEventSubscriptionRequest(HAEventType.StateChanged)}handleHaEvent(n){this.isHAEventStateChanged(n.event)&&this.eventStateChanged(n.event)}handleHaExpectedResult(n){let t=this.expectedResults[n.id];if(typeof t!="undefined"){switch(t){case HAResponseType.StateList:this.resultStateList(n.result);break;default:console.warn("Unhandled response type for this message.",n)}return!0}let i=this.expectedPromises[n.id];return typeof i!="undefined"?(i(n),!0):!1}resultStateList(n){for(var t of n)this.evStateChanged.invoke({data:{entity_id:t.entity_id,new_state:t,old_state:null},event_type:HAEventType.StateChanged,origin:null,time_fired:null})}eventStateChanged(n){var i,t;console.info(`HA State Changed [${n.data.entity_id}] ${t=(i=n.data.old_state)===null||i===void 0?void 0:i.state,t!==null&&t!==void 0?t:""} -> ${n.data.new_state.state}`);this.evStateChanged.invoke(n)}sendAuth(){this.send({type:HAMessageType.Auth,access_token:window.ccOptions.token})}sendStateRequest(){let n=this.send({type:HAMessageType.GetStates});this.expectedResults[n]=HAResponseType.StateList}sendCameraThumbnailRequest(n,t){let i=this.send({type:HAMessageType.CameraThumbnail,entity_id:n});this.expectedPromises[i]=t}sendMediaThumbnailRequest(n,t){let i=this.send({type:HAMessageType.MediaThumbnail,entity_id:n});this.expectedPromises[i]=t}sendEventSubscriptionRequest(n){this.send({type:HAMessageType.SubscribeToEvents,event_type:n})}handleHaError(n){console.error("HA API Error ["+n.code+"] "+n.message)}sendPing(){this.send({type:HAMessageType.Ping})}send(n){return console.debug("<- SND:"+n.type,n),this.state!==HAConnectionState.Closed&&this.state!==HAConnectionState.Opening?(this.state===HAConnectionState.Open&&(n.id=this.msgId++),this.ws.send(JSON.stringify(n)),n.id):(console.warn("Tried to send socket message, but connection isn't ready.",n),-1)}handleOpen(){this.state=HAConnectionState.Auth;this.evConnectionStateChanged.invoke(this.state)}handleClose(){this.state=HAConnectionState.Closed;this.evConnectionStateChanged.invoke(this.state);this.pingInterval&&(window.clearInterval(this.pingInterval),this.pingInterval=0)}parseSocketUrl(n){let t=document.createElement("a");return t.href=n,`${t.protocol.toLowerCase()==="https:"?"wss":"ws"}://${t.host}/api/websocket`}isHAMessage(n){return n&&typeof n.type=="string"}isHAEventStateChanged(n){return n&&typeof n.event_type=="string"&&n.event_type===HAEventType.StateChanged}}class PageUtils{static ConfirmDelete(n){return confirm("This item will be permanently deleted. This action cannot be undone.\n\nAre you sure?")?!0:(n.preventDefault(),!1)}}(function(n){n[n.User=0]="User";n[n.Admin=1]="Admin"})(PageMode||(PageMode={}));class Utils{static delayPromise(n,...t){return new Promise(i=>setTimeout(()=>i(t),n))}static sleep(n){const i=Date.now();let t=null;do t=Date.now();while(t-i{let r=new Image;r.onload=()=>t(n);r.onerror=n=>i(n);r.src=n})}static convertDegreesToCardinal(n){return["N","NNE","NE","ENE","E","ESE","SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW"][Math.floor(n/22.5+.5)%16]}static convertCardinalToIcon(n){return{N:"arrow-up-thick",NNE:"arrow-up-thick",NE:"arrow-top-right-thick",ENE:"arrow-right-thick",E:"arrow-right-thick",ESE:"arrow-right-thick",SE:"arrow-bottom-right-thick",SSE:"arrow-down-thick",S:"arrow-down-thick",SSW:"arrow-down-thick",SW:"arrow-bottom-left-thick",WSW:"arrow-left-thick",W:"arrow-left-thick",WNW:"arrow-left-thick",NW:"arrow-top-left-thick",NNW:"arrow-up-thick"}[n]}}class Tile{constructor(n,t,i,r,u){var e,f;this.page=n;this.name=t;this.conn=i;this.canLoad=u.canLoad;this.canClick=u.canClick;this.entityIds=[];this.loaded=!this.canLoad;this.el=$(`.tiles .tile[data-tile-name="${t}"]`);this.canClick&&this.el.click(()=>{this.onClick()});f=this.el.data("tile-entityid");typeof f=="object"&&Array.isArray(f)?this.entityIds=f:this.entityIds.push((e=f)===null||e===void 0?void 0:e.toString());i.on("SendSystemConfig",(n,i)=>{t==n&&(console.debug(`Received "SendSystemConfig" for tile: ${n}`),this.config=i,this.canLoad)});i.on("SendTile",n=>{t==n.name&&(console.debug(`Received "SendTile" for tile: ${n.name}`),this.updateTile(n))});i.on("SendCalendarInfo",(n,i,r)=>{t==n.name&&this.updateCalendar(i,r)});i.on("SendWarning",n=>console.warn(n));i.on("SendDateTime",(n,i,r)=>{t==n.name&&this.updateDateTime(n,i,r)});this.requestConfig(n);this.canLoad&&this.requestState()}onClick(){return this.conn.invoke("OnTileClicked",this.page,this.name)}updateTile(){this.loaded=!0;this.disableLoading()}updateState(){}updateCalendar(){}updateDateTime(){}requestState(){this.conn.invoke("RequestTileState",this.page,this.name)}requestConfig(n){this.conn.invoke("RequestConfig",n,this.name)}disableLoading(){this.loadingDebouncer&&window.clearTimeout(this.loadingDebouncer);this.loadingDebouncer=null;this.el.removeClass("tile-loading")}getEntityIds(){return this.entityIds}}class BlankTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}}class LabelTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}}class DateTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!0})}updateDateTime(n,t,i){$(`#tile-${n.name}`).find("span[value-date]").text(t);$(`#tile-${n.name}`).find("span[value-time]").text(i);super.updateDateTime();setTimeout(()=>{this.requestState(9500)},1e4)}}class StateTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let i=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(i=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(i);let t=this.tile.roundDecimals?parseInt(n.new_state.state).toString():n.new_state.state;this.tile.displayTextOff&&t.toLowerCase()==="off"?t=this.tile.displayTextOff:this.tile.displayTextOn&&t.toLowerCase()==="on"&&(t=this.tile.displayTextOn);n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());$(`#tile-${this.tile.name}`).find("span[value-state]").text(t)}}class LightTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(t=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(t);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass(`mdi-${this.tile.displayIcon} mdi-${this.tile.displayOffIcon}`).addClass(`mdi mdi-${n.new_state.state.toLowerCase()==="on"?Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayIcon):Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayOffIcon||this.tile.displayIcon)}`);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass("state-off state-on").addClass(n.new_state.state.toLowerCase()==="on"?"state-on":"state-off");this.tile.onColor&&n.new_state.state.toLowerCase()==="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.onColor);this.tile.offColor&&n.new_state.state.toLowerCase()!=="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.offColor)}}class SwitchTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(t=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(t);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass(`mdi-${this.tile.displayIcon} mdi-${this.tile.displayOffIcon}`).addClass(`mdi mdi-${n.new_state.state.toLowerCase()==="on"?Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayIcon):Utils.resolveIcon(n.new_state.attributes.icon,this.tile.displayOffIcon||this.tile.displayIcon)}`);$(`#tile-${this.tile.name}`).find("span[value-icon]").removeClass("state-off state-on").addClass(n.new_state.state.toLowerCase()==="on"?"state-on":"state-off");this.tile.onColor&&n.new_state.state.toLowerCase()==="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.onColor);this.tile.offColor&&n.new_state.state.toLowerCase()!=="on"&&$(`#tile-${this.tile.name} .value`).css("color",this.tile.offColor);super.updateState(n)}}class PersonTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.entity_picture?n.new_state.attributes.entity_picture.toString():"",i=n.new_state.state.replace("_"," "),r=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(r=this.tile.overrideLabel);let u=i.toLowerCase()==="home";t.toLowerCase().startsWith("http")||(t=Utils.resolveAssetUrl(window.ccOptions.baseUrl,window.ccOptions.overrideAssetUrl,t));$(`#tile-${this.tile.name}`).find("span[value-name]").text(r);$(`#tile-${this.tile.name}`).find("span[value-location]").text(i);$(`#tile-${this.tile.name}`).find("span[value-picture]").css("background-image",`url(${t})`).removeClass("bw");u||$(`#tile-${this.tile.name}`).find("span[value-picture]").addClass("bw")}}class WeatherTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canLoad:!0,canClick:!1});this.page=n;this.name=t;this.conn=i;this.windSpeed="";this.windDir="";this.hi="";this.lo="";this.iconEl=this.el.find(".condition-icon")[0];const u=document.defaultView.getComputedStyle(this.el[0]).color;this.skycons=new Skycons({color:u,resizeClear:!0});this.skycons.add(this.iconEl,Skycons.CLEAR_DAY)}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state==null?null:n.new_state.state;switch(n.entity_id){case this.tile.entityId:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());$(`#tile-${this.tile.name}`).find("span[value-temp]").text(t);break;case this.tile.highTempEntity:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());this.hi=` ${t}`;break;case this.tile.lowTempEntity:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());this.lo=` ${t}`;break;case this.tile.summaryEntity:$(`#tile-${this.tile.name}`).find("span[value-summary]").text(t);break;case this.tile.precipChanceEntity:n.new_state.attributes.unit_of_measurement&&(t+=n.new_state.attributes.unit_of_measurement.toString());$(`#tile-${this.tile.name}`).find("span[value-rain]").text(`Rain: ${t}`);break;case this.tile.windSpeedEntity:this.windSpeed=this.tile.roundWindSpeed?parseInt(t).toString():t;n.new_state.attributes.unit_of_measurement&&(this.windSpeed+=n.new_state.attributes.unit_of_measurement.toString());break;case this.tile.windDirectionEntity:this.windDir=Utils.convertDegreesToCardinal(parseInt(t));this.windDir=` ${this.windDir}`;break;case this.tile.iconEntity:t?(this.skycons.set(this.iconEl,t),this.skycons.play()):(this.skycons.remove(this.iconEl),$(this.iconEl).hide())}$(`#tile-${this.tile.name}`).find("span[value-hi-lo]").html(`${this.hi&&this.lo?this.hi+" / "+this.lo:this.hi+this.lo}`);$(`#tile-${this.tile.name}`).find("span[value-wind]").html(`Wind: ${(this.windSpeed+" "+this.windDir).trim()}`)}}class CameraTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!1,canLoad:!0});this.haConn=r}updateTile(n){this.tile=n;super.updateTile(n);this.updateCameraFeed();window.setInterval(()=>this.updateCameraFeed(),(this.tile.refreshRate>0?this.tile.refreshRate:1)*1e3)}updateCameraFeed(){this.haConn.getCameraImage(this.tile.entityId).then(n=>{let t=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?this.tile.imageCropMode.toLowerCase():"100% 100%",i=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?"50% 50%":"0 0";$(`#tile-${this.tile.name}`).css({backgroundImage:`url('data:${n.result.content_type};base64,${n.result.content}')`,backgroundRepeat:"no-repeat",backgroundPosition:i,backgroundSize:t})})}}class SceneTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.tile=n;super.updateTile(n)}updateState(n){let t=n.new_state.attributes.friendly_name.toString();this.tile.overrideLabel&&(t=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(t);$(`#tile-${this.tile.name}`).find("span[value-icon]").addClass(`mdi mdi-${this.tile.displayIcon||"filmstrip"}`);$(`#tile-${this.tile.name} .value`).css("color",this.tile.iconColor)}}class MediaTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1});this.haConn=r}updateTile(n){this.tile=n;super.updateTile(n);this.updateMediaImage();window.setInterval(()=>this.updateMediaImage(),(this.tile.refreshRate>0?this.tile.refreshRate:1)*1e3)}updateMediaImage(){var n;if((n=this.state)===null||n===void 0?void 0:n.new_state){let t=this.state.new_state.attributes.friendly_name.toString();(this.tile.overrideLabel&&(t=this.tile.overrideLabel),this.state.new_state.attributes.entity_picture||(t+=` (${this.state.new_state.state})`),$(`#tile-${this.tile.name}`).toggleClass("media-idle",this.state.new_state.attributes.media_title==="Nothing playing"||(this.state.new_state.state=="paused"||this.state.new_state.state=="idle")&&!this.state.new_state.attributes.entity_picture),$(`#tile-${this.tile.name}`).find("span[value-name]").text(this.tile.showLabel?t:""),$(`#tile-${this.tile.name}`).find("span[value-title]").text(this.tile.showTitle&&this.state.new_state.attributes.media_title&&this.state.new_state.attributes.media_title!=="Nothing playing"?this.state.new_state.attributes.media_title.toString():""),this.state.new_state.attributes.entity_picture)&&this.haConn.getMediaImage(this.tile.entityId).then(n=>{let t=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?this.tile.imageCropMode.toLowerCase():"100% 100%",i=this.tile.imageCropMode.toLowerCase()==="cover"||this.tile.imageCropMode.toLowerCase()==="contain"?"50% 50%":"0 0";$(`#tile-${this.tile.name}`).css({backgroundImage:`url('data:${n.result.content_type};base64,${n.result.content}')`,backgroundRepeat:"no-repeat",backgroundPosition:i,backgroundSize:t})})}}updateState(n){this.state=n}}class NavigationTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canClick:!0,canLoad:!1})}updateTile(n){this.navTile=n;$(`#tile-${n.name}`).find("span[value-name]").text(this.navTile.label);$(`#tile-${n.name}`).find("span[value-icon]").addClass(`mdi mdi-${this.navTile.displayIcon}`);super.updateTile()}onClick(){switch(this.navTile.mode.toLowerCase().trim()){case"home":window.location.href="/d/";return;case"refresh":window.location.reload();return;case"nav":window.location.href=`/d/${this.navTile.target}`;return}}}class CalendarTile extends Tile{constructor(n,t,i,r){super(n,t,i,r,{canLoad:!0,canClick:!1});this.eventContainer=$(`#tile-${t} div.calendar-events`)}updateTile(n){this.tile=n;super.updateTile(n)}updateCalendar(n,t){let i=n.attributes.friendly_name.toString();this.tile.overrideLabel&&(i=this.tile.overrideLabel);$(`#tile-${this.tile.name}`).find("span[value-name]").text(i);this.refreshEvents(t);super.updateCalendar();this.tile.refreshRate>0&&setTimeout(()=>{this.requestState(1e3)},this.tile.refreshRate*1e3)}refreshEvents(n){var t;if(this.eventContainer.empty(),n.length){let i="";for(let r=0;r${f}`),i=f);this.eventContainer.append(`

${u.summary}${u.start.dateTime?moment((t=u.start.dateTime,t!==null&&t!==void 0?t:u.start.date)).format("LT"):"All Day"}

`)}}else this.eventContainer.append('No events!<\/span>')}getEventHeader(n){var i;const r=moment(),u=moment().add(1,"day");let f=this.formatHeader(r),e=this.formatHeader(u);const o=moment((i=n.start.dateTime,i!==null&&i!==void 0?i:n.start.date));let t=this.formatHeader(o);return t===f?t+=" (Today)":t===e&&(t+=" (Tomorrow)"),t}formatHeader(n){return n.format("ddd")+", "+n.format("ll")}}class TileMap{}TileMap.ClassMap={Blank:BlankTile,Label:LabelTile,Date:DateTile,State:StateTile,Light:LightTile,Switch:SwitchTile,Person:PersonTile,Weather:WeatherTile,Camera:CameraTile,Scene:SceneTile,Media:MediaTile,Navigation:NavigationTile,Calendar:CalendarTile};class CommandCenter{constructor(){this.tiles=[];$(()=>this.init())}init(){window.ccOptions.mode==PageMode.Admin?this.initAdmin():this.initUser();this.initializeMdiPreview();this.initializeColorPreview();this.initializeNightlyRefresh()}initAdmin(){$(window).on("beforeunload",n=>{if(this.pageIsDirty&&$(n.target.activeElement).prop("type")!=="submit")return"You have unsaved changes. Are you sure you want to leave?"});if($("#importTheme, #importConfig").click(()=>{confirm("WARNING: This will OVERWRITE your current settings. Export first if you want to save what you have now! Continue?")&&$("#importBrowser").click()}),$("#importBrowser").change(()=>{$("#importForm").submit()}),$("#resetConfig").click(n=>confirm("WARNING: This will COMPLETELY RESET your HACC installation and PERMANENTLY DELETE all of your tiles, themes, and settings. Are you sure you want to reset your config?")?!0:(n.preventDefault(),!1)),$(".ui.accordion").accordion(),$(".ui.checkbox").checkbox(),$(".ui.dropdown").not(".no-placeholder").dropdown({fullTextSearch:!0}),$(".ui.no-placeholder.dropdown").dropdown({placeholder:!1}),$("#Page_PageFontFace option").each(function(n,t){$(t).parent().siblings(".menu").find('.item[data-value="'+$(t).text()+'"]').css("font-family",$(t).text())}),$(".preview-layout-grid").length){$("#auto-layout").click(()=>{confirm("This will reset the layout for this page and attempt to automatically arrange all tiles evenly.\n\nYour tiles will all probably change locations. You have been warned. :)\n\nAre you sure you want to reset the layout?")&&this.pk.layout()});$(".preview-layout-grid").prepend(`
`);this.pk=window.ccOptions?new Packery(".preview-layout-grid",{itemSelector:".preview-layout-item",columnWidth:window.ccOptions.tilePreviewSize,rowHeight:window.ccOptions.tilePreviewSize,gutter:window.ccOptions.tilePreviewPadding,initLayout:!1}):new Packery(".preview-layout-grid",{itemSelector:".preview-layout-item",columnWidth:".preview-layout-item",rowHeight:".preview-layout-item",gutter:window.ccOptions.tilePreviewPadding,initLayout:!1});this.pk.on("layoutComplete",()=>this.writeItemLayout());this.pk.on("dragItemPositioned",()=>{setTimeout(()=>{this.writeItemLayout(),this.pageIsDirty=!0},25)});this.writeItemLayout();typeof Draggabilly=="function"?$(".preview-layout-item").each((n,t)=>this.pk.bindDraggabillyEvents(new Draggabilly(t,{containment:".preview-layout-grid"}))):console.warn("Draggabilly is not available - drag and drop interface will not work.");$("#grid__tmp").remove();this.pk.initShiftLayout(Array.from(document.querySelectorAll(".preview-layout-grid > .preview-layout-item")))}}initUser(){window.ccOptions.socketUrl&&(this.conn=new HAConnection(window.ccOptions.socketUrl));this.conn.OnConnectionStateChanged.on(n=>{n==HAConnectionState.Closed?$("#alerts").show().find(".alert-message").text("[H] Connection lost, reconnecting..."):n==HAConnectionState.Open&&$("#alerts").hide()});this.conn.OnStateChanged.on(n=>{var t=this.findTilesByEntityId(n.data.entity_id);for(var i of t)i.updateState(n.data),console.info(`Updating tile for entity "${n.data.entity_id}" to state "${n.data.new_state.state}".`)});this.conn.initialize();this.tileConn=(new signalR.HubConnectionBuilder).withUrl("/hubs/tile").build();this.tileConn.onclose(()=>{$("#alerts").show().find(".alert-message").text("[S] Connection lost, reconnecting..."),window.setTimeout(()=>{window.location.reload()},1e4)});this.tileConn.start().then(()=>{$(".tiles .tile").each((n,t)=>{try{let n=new TileMap.ClassMap[$(t).data("tile-type").toString()](window.ccOptions.pageId,$(t).data("tile-name"),this.tileConn,this.conn);this.tiles.push(n)}catch(i){console.error('Error instantiating class "'+($(t).data("tile-type")||"__MISSING__")+'Tile". Was it added to the tile type map?',i,t)}}),this.initHandle||(this.initHandle=window.setInterval(()=>this.waitAndPerformInit(),25)),window.ccOptions.autoReturn>0&&window.setTimeout(()=>window.location.href="/d/",window.ccOptions.autoReturn*1e3)})}waitAndPerformInit(){(this.conn||(console.warn("Cancelling tile initialization - connection is not set up."),window.clearInterval(this.initHandle)),this.conn.ConnectionState===HAConnectionState.Open)&&this.tiles.filter(n=>n.loaded).length===this.tiles.length&&(this.conn.refreshAllStates(),window.clearInterval(this.initHandle))}findTilesByEntityId(n){return this.tiles.filter(t=>{let i=t.getEntityIds();return i.some(t=>t.toLowerCase()===n.toLowerCase())})}initializeNightlyRefresh(){window.setInterval(()=>{(new Date).getHours()==2&&window.setTimeout(()=>{window.location.reload()},36e5)},35e5)}initializeMdiPreview(){$(".mdi-icon-placeholder + input").each((n,t)=>{$(t).keyup(n=>{this.refreshDynamicIcon(n.currentTarget)}),this.refreshDynamicIcon(t)})}refreshDynamicIcon(n){$(n).parent().children(".mdi-icon-placeholder").attr("class","large icon mdi-icon-placeholder").addClass(`mdi mdi-${$(n).val()}`)}initializeColorPreview(){$(".color-preview + input").each((n,t)=>{$(t).keyup(n=>{this.refreshDynamicColor(n.currentTarget)}),this.refreshDynamicColor(t)})}refreshDynamicColor(n){$(n).parent().children(".color-preview").css("color",`${$(n).val()}`)}writeItemLayout(){var n=[],t=this.pk.getItemElements();for(let i=0;i diff --git a/HADotNet.CommandCenter/wwwroot/js/typings/window-options.d.ts b/HADotNet.CommandCenter/wwwroot/js/typings/window-options.d.ts index 886865a..519f65b 100644 --- a/HADotNet.CommandCenter/wwwroot/js/typings/window-options.d.ts +++ b/HADotNet.CommandCenter/wwwroot/js/typings/window-options.d.ts @@ -4,6 +4,7 @@ declare interface ICcOptions { baseUrl: string; overrideAssetUrl: string; + socketUrl: string; token: string; tilePadding: number; tilePreviewPadding: number;