diff --git a/.gitignore b/.gitignore index 2ccbe465..857c1158 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /node_modules/ +.idea diff --git a/README.md b/README.md index dbb961d8..701c1ab1 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,8 @@ Visit the [issues](https://github.com/AalianKhan/mushroom-strategy/issues/new/ch * [Johan Frick](https://github.com/johanfrick) +* [Marcel Tinner](https://github.com/mtinner) + ## Credits * The cards used are from [Mushroom][mushroomUrl] and [Mini graph card][miniGraphUrl]. diff --git a/dist/mushroom-strategy.js b/dist/mushroom-strategy.js index edea6062..fd7d7983 100644 --- a/dist/mushroom-strategy.js +++ b/dist/mushroom-strategy.js @@ -1 +1 @@ -(()=>{var e={996:e=>{"use strict";var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===r}(e)}(e)},r="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function i(e,t){return!1!==t.clone&&t.isMergeableObject(e)?s((r=e,Array.isArray(r)?[]:{}),e,t):e;var r}function a(e,t,r){return e.concat(t).map((function(e){return i(e,r)}))}function o(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return Object.propertyIsEnumerable.call(e,t)})):[]}(e))}function n(e,t){try{return t in e}catch(e){return!1}}function s(e,r,c){(c=c||{}).arrayMerge=c.arrayMerge||a,c.isMergeableObject=c.isMergeableObject||t,c.cloneUnlessOtherwiseSpecified=i;var d=Array.isArray(r);return d===Array.isArray(e)?d?c.arrayMerge(e,r,c):function(e,t,r){var a={};return r.isMergeableObject(e)&&o(e).forEach((function(t){a[t]=i(e[t],r)})),o(t).forEach((function(o){(function(e,t){return n(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,o)||(n(e,o)&&r.isMergeableObject(t[o])?a[o]=function(e,t){if(!t.customMerge)return s;var r=t.customMerge(e);return"function"==typeof r?r:s}(o,r)(e[o],t[o],r):a[o]=i(t[o],r))})),a}(e,r,c):i(r,c)}s.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,r){return s(e,r,t)}),{})};var c=s;e.exports=c},371:(e,t,r)=>{"use strict";r.d(t,{W:()=>v});const i={areas:{undisclosed:{aliases:[],area_id:"undisclosed",name:"Undisclosed",picture:null,hidden:!1}},debug:!1,domains:{_:{hide_config_entities:!1},default:{title:"Miscellaneous",showControls:!1,hidden:!1},light:{title:"Lights",showControls:!0,iconOn:"mdi:lightbulb",iconOff:"mdi:lightbulb-off",onService:"light.turn_on",offService:"light.turn_off",hidden:!1},fan:{title:"Fans",showControls:!0,iconOn:"mdi:fan",iconOff:"mdi:fan-off",onService:"fan.turn_on",offService:"fan.turn_off",hidden:!1},cover:{title:"Covers",showControls:!0,iconOn:"mdi:arrow-up",iconOff:"mdi:arrow-down",onService:"cover.open_cover",offService:"cover.close_cover",hidden:!1},switch:{title:"Switches",showControls:!0,iconOn:"mdi:power-plug",iconOff:"mdi:power-plug-off",onService:"switch.turn_on",offService:"switch.turn_off",hidden:!1},camera:{title:"Cameras",showControls:!1,hidden:!1},lock:{title:"Locks",showControls:!1,hidden:!1},climate:{title:"Climates",showControls:!1,hidden:!1},media_player:{title:"Media Players",showControls:!1,hidden:!1},sensor:{title:"Sensors",showControls:!1,hidden:!1},binary_sensor:{title:"Binary Sensors",showControls:!1,hidden:!1},number:{title:"Numbers",showControls:!1,hidden:!1},vacuum:{title:"Vacuums",showControls:!0,hidden:!1}},home_view:{hidden:[]},views:{home:{order:1,hidden:!1},light:{order:2,hidden:!1},fan:{order:3,hidden:!1},cover:{order:4,hidden:!1},switch:{order:5,hidden:!1},climate:{order:6,hidden:!1},camera:{order:7,hidden:!1},vacuum:{order:8,hidden:!1}}};var a,o,n,s,c,d,l,h,f,u,p=r(996),w=r.n(p),m=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)},g=function(e,t,r,i,a){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?a.call(e,r):a?a.value=r:t.set(e,r),r};class v{constructor(){throw new Error("This class should be invoked with method initialize() instead of using the keyword new!")}static get strategyOptions(){return m(this,a,"f",l)}static get areas(){return m(this,a,"f",s)}static get devices(){return m(this,a,"f",n)}static get entities(){return m(this,a,"f",o)}static get debug(){return m(this,a,"f",h)}static async initialize(e){g(this,a,e.hass.states,"f",c),g(this,a,w()(i,e.config?.strategy?.options??{}),"f",l),g(this,a,m(this,a,"f",l).debug,"f",h);try{[{set value(e){g(a,a,e,"f",o)}}.value,{set value(e){g(a,a,e,"f",n)}}.value,{set value(e){g(a,a,e,"f",s)}}.value]=await Promise.all([e.hass.callWS({type:"config/entity_registry/list"}),e.hass.callWS({type:"config/device_registry/list"}),e.hass.callWS({type:"config/area_registry/list"})])}catch(e){throw a.logError("An error occurred while querying Home assistant's registries!",e),"Check the console for details"}m(this,a,"f",l).areas.undisclosed?.hidden||(m(this,a,"f",l).areas.undisclosed={...i.areas.undisclosed,...m(this,a,"f",l).areas.undisclosed},m(this,a,"f",l).areas.undisclosed.area_id="undisclosed",m(this,a,"f",s).push(m(this,a,"f",l).areas.undisclosed)),g(this,a,a.areas.map((e=>({...e,...m(this,a,"f",l).areas?.[e.area_id]}))),"f",s),m(this,a,"f",s).sort(((e,t)=>(e.order??1/0)-(t.order??1/0)||e.name.localeCompare(t.name))),m(this,a,"f",l).views=Object.fromEntries(Object.entries(m(this,a,"f",l).views).sort((([,e],[,t])=>(e.order??1/0)-(t.order??1/0)||(e.title??"undefined").localeCompare(t.title??"undefined")))),m(this,a,"f",l).domains=Object.fromEntries(Object.entries(m(this,a,"f",l).domains).sort((([,e],[,t])=>(e.order??1/0)-(t.order??1/0)||(e.title??"undefined").localeCompare(t.title??"undefined")))),g(this,a,!0,"f",d)}static isInitialized(){return m(this,a,"f",d)}static getCountTemplate(e,t,r){const i=[];this.isInitialized()||console.warn("Helper class should be initialized before calling this method!");for(const t of m(this,a,"f",s)){const r=m(this,a,"f",n).filter((e=>e.area_id===t.area_id)).map((e=>e.id)),s=m(this,a,"f",o).filter(m(this,a,"m",f),{area:t,domain:e,areaDeviceIds:r}).map((e=>`states['${e.entity_id}']`));i.push(...s)}return`{% set entities = [${i}] %} {{ entities | selectattr('state','${t}','${r}') | list | count }}`}static getDeviceEntities(e,t){this.isInitialized()||console.warn("Helper class should be initialized before calling this method!");const r=m(this,a,"f",n).filter((t=>(t.area_id??"undisclosed")===e.area_id)).map((e=>e.id));return m(this,a,"f",o).filter(m(this,a,"m",f),{area:e,domain:t,areaDeviceIds:r}).sort(((e,t)=>(e.original_name??"undefined").localeCompare(t.original_name??"undefined")))}static getStateEntities(e,t){this.isInitialized()||console.warn("Helper class should be initialized before calling this method!");const r=[],i=Object.fromEntries(m(this,a,"f",o).map((e=>[e.entity_id,e]))),s=Object.fromEntries(m(this,a,"f",n).map((e=>[e.id,e]))),d=Object.values(m(this,a,"f",c)).filter((e=>e.entity_id.startsWith(`${t}.`)));for(const t of d){const a=i[t.entity_id],o=s[a?.device_id??""];(a?.area_id===e.area_id||o&&o.area_id===e.area_id)&&r.push(t)}return r}static sanitizeClassName(e){return(e=e.charAt(0).toUpperCase()+e.slice(1)).replace(/([-_][a-z])/g,(e=>e.toUpperCase().replace("-","").replace("_","")))}static getExposedViewIds(){return this.isInitialized()||console.warn("Helper class should be initialized before calling this method!"),m(this,a,"m",u).call(this,m(this,a,"f",l).views,"hidden",!1)}static getExposedDomainIds(){return this.isInitialized()||console.warn("Helper class should be initialized before calling this method!"),m(this,a,"m",u).call(this,m(this,a,"f",l).domains,"hidden",!1)}static logError(e,t){a.debug?console.error(e,t):console.error(e)}}a=v,f=function(e){const t=null===e.hidden_by&&null===e.disabled_by,r=e.entity_id.startsWith(`${this.domain}.`),i="undisclosed"===this.area.area_id?!e.area_id&&(this.areaDeviceIds.includes(e.device_id??"")||!e.device_id):this.areaDeviceIds.includes(e.device_id??"")||e.area_id===this.area.area_id;return t&&r&&i},u=function(e,t,r){const i=[];for(const a of Object.keys(e))e[a][t]===r&&i.push(a);return i},o={value:void 0},n={value:void 0},s={value:[]},c={value:void 0},d={value:!1},l={value:void 0},h={value:void 0}},976:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AbstractCard:()=>a});var i=r(371);class a{constructor(e){if(this.config={type:"custom:mushroom-entity-card",icon:"mdi:help-circle"},!i.W.isInitialized())throw new Error("The Helper module must be initialized before using this one.");this.entity=e}getCard(){return{...this.config,entity:"entity_id"in this.entity?this.entity.entity_id:void 0}}}},396:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AreaCard:()=>n});var i,a=r(976),o=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class n extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-template-card",primary:void 0,icon:"mdi:texture-box",icon_color:"blue",tap_action:{action:"navigate",navigation_path:""},hold_action:{action:"none"}}),"default"===t.type&&delete t.type,o(this,i,"f").primary=e.name,o(this,i,"f").tap_action&&"navigation_path"in o(this,i,"f").tap_action&&(o(this,i,"f").tap_action.navigation_path=e.area_id),this.config=Object.assign(this.config,o(this,i,"f"),t)}}i=new WeakMap},418:(e,t,r)=>{"use strict";r.r(t),r.d(t,{BinarySensorCard:()=>o});var i,a=r(642);class o extends a.SensorCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon:"mdi:power-cycle",icon_color:"green"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},604:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CameraCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{entity:"",type:"picture-entity",show_name:!1,show_state:!1,camera_view:"live"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},782:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ClimateCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-climate-card",icon:void 0,hvac_modes:["off","cool","heat","fan_only"],show_temperature_control:!0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},607:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ControllerCard:()=>s});var i,a,o=function(e,t,r,i,a){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?a.call(e,r):a?a.value=r:t.set(e,r),r},n=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class s{constructor(e,t={}){i.set(this,void 0),a.set(this,{type:"mushroom-title-card",showControls:!0,iconOn:"mdi:power-on",iconOff:"mdi:power-off",onService:"none",offService:"none"}),o(this,i,e,"f"),o(this,a,{...n(this,a,"f"),...t},"f")}createCard(){const e=[{type:"custom:mushroom-title-card",title:n(this,a,"f").title,subtitle:n(this,a,"f").subtitle}];return n(this,a,"f").showControls&&e.push({type:"horizontal-stack",cards:[{type:"custom:mushroom-template-card",icon:n(this,a,"f").iconOff,layout:"vertical",icon_color:"red",tap_action:{action:"call-service",service:n(this,a,"f").offService,target:n(this,i,"f"),data:{}}},{type:"custom:mushroom-template-card",icon:n(this,a,"f").iconOn,layout:"vertical",icon_color:"amber",tap_action:{action:"call-service",service:n(this,a,"f").onService,target:n(this,i,"f"),data:{}}}]}),{type:"horizontal-stack",cards:e}}}i=new WeakMap,a=new WeakMap},758:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CoverCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-cover-card",icon:void 0,show_buttons_control:!0,show_position_control:!0,show_tilt_position_control:!0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},287:(e,t,r)=>{"use strict";r.r(t),r.d(t,{FanCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-fan-card",icon:void 0,show_percentage_control:!0,show_oscillate_control:!0,icon_animation:!0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},387:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AreaCard:()=>n});var i,a=r(976),o=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class n extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"area",area:""}),o(this,i,"f").area=e.area_id,o(this,i,"f").navigation_path=o(this,i,"f").area,delete t.type,this.config=Object.assign(this.config,o(this,i,"f"),t)}}i=new WeakMap},475:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LightCard:()=>d});var i,a=r(976),o=r(596),n=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)},s=o.R.isCallServiceActionConfig,c=o.R.isCallServiceActionTarget;class d extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-light-card",icon:void 0,show_brightness_control:!0,show_color_control:!0,show_color_temp_control:!0,use_light_color:!0,double_tap_action:{action:"call-service",service:"light.turn_on",target:{entity_id:void 0},data:{rgb_color:[255,255,255]}}}),s(n(this,i,"f").double_tap_action)&&c(n(this,i,"f").double_tap_action.target)&&(n(this,i,"f").double_tap_action.target.entity_id=e.entity_id),this.config=Object.assign(this.config,n(this,i,"f"),t)}}i=new WeakMap},696:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LockCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-lock-card",icon:void 0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},499:(e,t,r)=>{"use strict";r.r(t),r.d(t,{MediaPlayerCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-media-player-card",use_media_info:!0,media_controls:["on_off","play_pause_stop"],show_volume_level:!0,volume_controls:["volume_mute","volume_set","volume_buttons"]}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},571:(e,t,r)=>{"use strict";r.r(t),r.d(t,{MiscellaneousCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon_color:"blue-grey"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},420:(e,t,r)=>{"use strict";r.r(t),r.d(t,{NumberCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-number-card",icon:void 0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},877:(e,t,r)=>{"use strict";r.r(t),r.d(t,{PersonCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-person-card",layout:"vertical",primary_info:"none",secondary_info:"none",icon_type:"entity-picture"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},642:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SensorCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon:"mdi:information",animate:!0,line_color:"green"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},330:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SwitchCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon:void 0,tap_action:{action:"toggle"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},695:(e,t,r)=>{"use strict";r.r(t),r.d(t,{VacuumCard:()=>n});var i=r(976);const a=["on_off","start_pause","stop","locate","clean_spot","return_home"];var o;class n extends i.AbstractCard{constructor(e,t={}){super(e),o.set(this,{type:"custom:mushroom-vacuum-card",icon:void 0,icon_animation:!0,commands:[...a],tap_action:{action:"more-info"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,o,"f"),t)}}o=new WeakMap},370:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AbstractChip:()=>o});var i=r(371),a=r(596).R.isCallServiceActionConfig;class o{constructor(){if(this.config={type:"template"},!i.W.isInitialized())throw new Error("The Helper module must be initialized before using this one.")}getChip(){return this.config}setTapActionTarget(e){"tap_action"in this.config&&a(this.config.tap_action)?this.config.tap_action.target=e:i.W.debug&&console.warn(this.constructor.name+" - Target not set: Invalid target or tap action.")}}},660:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ClimateChip:()=>n});var i,a=r(371),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:thermostat",icon_color:"orange",content:a.W.getCountTemplate("climate","ne","off"),tap_action:{action:"none"},hold_action:{action:"navigate",navigation_path:"climates"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},993:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CoverChip:()=>n});var i,a=r(371),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:window-open",icon_color:"cyan",content:a.W.getCountTemplate("cover","eq","open"),tap_action:{action:"none"},hold_action:{action:"navigate",navigation_path:"covers"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},966:(e,t,r)=>{"use strict";r.r(t),r.d(t,{FanChip:()=>n});var i,a=r(371),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:fan",icon_color:"green",content:a.W.getCountTemplate("fan","eq","on"),tap_action:{action:"call-service",service:"fan.turn_off"},hold_action:{action:"navigate",navigation_path:"fans"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},95:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LightChip:()=>n});var i,a=r(371),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:lightbulb-group",icon_color:"amber",content:a.W.getCountTemplate("light","eq","on"),tap_action:{action:"call-service",service:"light.turn_off"},hold_action:{action:"navigate",navigation_path:"lights"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},817:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SwitchChip:()=>n});var i,a=r(371),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:dip-switch",icon_color:"blue",content:a.W.getCountTemplate("switch","eq","on"),tap_action:{action:"call-service",service:"switch.turn_off"},hold_action:{action:"navigate",navigation_path:"switches"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},977:(e,t,r)=>{"use strict";r.r(t),r.d(t,{WeatherChip:()=>n});var i,a=r(370),o=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class n extends a.AbstractChip{constructor(e,t={}){super(),i.set(this,{type:"weather",show_temperature:!0,show_conditions:!0}),function(e,t,r,i,a){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===i?a.call(e,r):a?a.value=r:t.set(e,r)}(this,i,{...o(this,i,"f"),entity:e,...t},"f"),this.config=Object.assign(this.config,o(this,i,"f"),t)}}i=new WeakMap},596:(e,t,r)=>{"use strict";var i;r.d(t,{R:()=>i}),function(e){e.isCallServiceActionConfig=function(e){return e&&"call-service"===e.action&&["action","service"].every((t=>t in e))},e.isCallServiceActionTarget=function(e){return e&&["entity_id","device_id","area_id"].some((t=>t in e))}}(i||(i={}))},1:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AbstractView:()=>s});var i,a=r(371),o=r(607),n=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class s{constructor(e=""){if(this.config={icon:"mdi:view-dashboard",subview:!1},this.viewControllerCard={cards:[],type:""},i.set(this,void 0),!a.W.isInitialized())throw new Error("The Helper module must be initialized before using this one.");e&&function(e,t,r,i,a){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===i?a.call(e,r):a?a.value=r:t.set(e,r)}(this,i,e,"f")}async createViewCards(){const e=[],t=a.W.strategyOptions.domains[n(this,i,"f")??"_"].hide_config_entities||a.W.strategyOptions.domains._.hide_config_entities;for(const s of a.W.areas){const c=[],d=a.W.getDeviceEntities(s,n(this,i,"f")??""),l=a.W.sanitizeClassName(n(this,i,"f")+"Card"),h=await r(175)(`./${l}`);let f={area_id:[s.area_id]};"undisclosed"===s.area_id&&(f={entity_id:d.map((e=>e.entity_id))});for(const e of d){let r=a.W.strategyOptions.card_options?.[e.entity_id],i=a.W.strategyOptions.card_options?.[e.device_id??"null"];r?.hidden||i?.hidden||"config"===e.entity_category&&t||c.push(new h[l](e,r).getCard())}if(c.length){const t="controllerCardOptions"in this.config?this.config.controllerCardOptions:{};c.unshift(new o.ControllerCard(f,Object.assign({title:s.name},t)).createCard()),e.push({type:"vertical-stack",cards:c})}}return e.length&&e.unshift(this.viewControllerCard),e}async getView(){return{...this.config,cards:await this.createViewCards()}}targetDomain(e){return{entity_id:a.W.entities.filter((t=>t.entity_id.startsWith(e+".")&&!t.hidden_by&&!a.W.strategyOptions.card_options?.[t.entity_id]?.hidden)).map((e=>e.entity_id))}}}i=new WeakMap},89:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CameraView:()=>h});var i,a,o,n,s=r(607),c=r(1),d=r(371),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends c.AbstractView{constructor(e={}){super(l(i,i,"f",a)),o.set(this,{title:"Cameras",path:"cameras",icon:"mdi:cctv",subview:!1,controllerCardOptions:{showControls:!1}}),n.set(this,{title:"All Cameras",subtitle:d.W.getCountTemplate(l(i,i,"f",a),"ne","off")+" cameras on"}),this.config=Object.assign(this.config,l(this,o,"f"),e),this.viewControllerCard=new s.ControllerCard({},{...l(this,n,"f"),..."controllerCardOptions"in this.config?this.config.controllerCardOptions:{}}).createCard()}}i=h,o=new WeakMap,n=new WeakMap,a={value:"camera"}},81:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ClimateView:()=>h});var i,a,o,n,s=r(371),c=r(607),d=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends d.AbstractView{constructor(e={}){super(l(i,i,"f",a)),o.set(this,{title:"Climates",path:"climates",icon:"mdi:thermostat",subview:!1,controllerCardOptions:{showControls:!1}}),n.set(this,{title:"All Climates",subtitle:s.W.getCountTemplate(l(i,i,"f",a),"ne","off")+" climates on"}),this.config=Object.assign(this.config,l(this,o,"f"),e),this.viewControllerCard=new c.ControllerCard(this.targetDomain(l(i,i,"f",a)),{...l(this,n,"f"),..."controllerCardOptions"in this.config?this.config.controllerCardOptions:{}}).createCard()}}i=h,o=new WeakMap,n=new WeakMap,a={value:"climate"}},136:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CoverView:()=>h});var i,a,o,n,s=r(371),c=r(607),d=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends d.AbstractView{constructor(e={}){super(l(i,i,"f",a)),o.set(this,{title:"Covers",path:"covers",icon:"mdi:window-open",subview:!1,controllerCardOptions:{iconOn:"mdi:arrow-up",iconOff:"mdi:arrow-down",onService:"cover.open_cover",offService:"cover.close_cover"}}),n.set(this,{title:"All Covers",subtitle:s.W.getCountTemplate(l(i,i,"f",a),"eq","open")+" covers open"}),this.config=Object.assign(this.config,l(this,o,"f"),e),this.viewControllerCard=new c.ControllerCard(this.targetDomain(l(i,i,"f",a)),{...l(this,n,"f"),..."controllerCardOptions"in this.config?this.config.controllerCardOptions:{}}).createCard()}}i=h,o=new WeakMap,n=new WeakMap,a={value:"cover"}},964:(e,t,r)=>{"use strict";r.r(t),r.d(t,{FanView:()=>h});var i,a,o,n,s=r(371),c=r(607),d=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends d.AbstractView{constructor(e={}){super(l(i,i,"f",a)),o.set(this,{title:"Fans",path:"fans",icon:"mdi:fan",subview:!1,controllerCardOptions:{iconOn:"mdi:fan",iconOff:"mdi:fan-off",onService:"fan.turn_on",offService:"fan.turn_off"}}),n.set(this,{title:"All Fans",subtitle:s.W.getCountTemplate(l(i,i,"f",a),"eq","on")+" fans on"}),this.config=Object.assign(this.config,l(this,o,"f"),e),this.viewControllerCard=new c.ControllerCard(this.targetDomain(l(i,i,"f",a)),{...l(this,n,"f"),..."controllerCardOptions"in this.config?this.config.controllerCardOptions:{}}).createCard()}}i=h,o=new WeakMap,n=new WeakMap,a={value:"fan"}},985:(e,t,r)=>{"use strict";r.r(t),r.d(t,{HomeView:()=>h});var i,a,o,n,s,c=r(371),d=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends d.AbstractView{constructor(e={}){super(),i.add(this),a.set(this,{title:"Home",icon:"mdi:home-assistant",path:"home",subview:!1}),this.config=Object.assign(this.config,l(this,a,"f"),e)}async createViewCards(){return await Promise.all([l(this,i,"m",o).call(this),l(this,i,"m",n).call(this),l(this,i,"m",s).call(this)]).then((([e,t,r])=>{const i=c.W.strategyOptions,a=[];return e.length&&a.push({type:"custom:mushroom-chips-card",alignment:"center",chips:e}),t.length&&a.push({type:"horizontal-stack",cards:t}),c.W.strategyOptions.home_view.hidden.includes("greeting")||a.push({type:"custom:mushroom-template-card",primary:"{% set time = now().hour %} {% if (time >= 18) %} Good Evening, {{user}}! {% elif (time >= 12) %} Good Afternoon, {{user}}! {% elif (time >= 5) %} Good Morning, {{user}}! {% else %} Hello, {{user}}! {% endif %}",icon:"mdi:hand-wave",icon_color:"orange",tap_action:{action:"none"},double_tap_action:{action:"none"},hold_action:{action:"none"}}),i.quick_access_cards&&a.push(...i.quick_access_cards),a.push({type:"vertical-stack",cards:r}),i.extra_cards&&a.push(...i.extra_cards),a}))}}a=new WeakMap,i=new WeakSet,o=async function(){if(c.W.strategyOptions.home_view.hidden.includes("chips"))return[];const e=[],t=c.W.strategyOptions.chips,i=["light","fan","cover","switch","climate"],a=c.W.areas.map((e=>e.area_id??""));let o;const n=t?.weather_entity??c.W.entities.find((e=>e.entity_id.startsWith("weather.")&&null===e.disabled_by&&null===e.hidden_by))?.entity_id;if(n)try{o=await Promise.resolve().then(r.bind(r,977));const t=new o.WeatherChip(n);e.push(t.getChip())}catch(e){c.W.logError("An error occurred while creating the weather chip!",e)}for(let n of i)if(t?.[`${n}_count`]??1){const t=c.W.sanitizeClassName(n+"Chip");try{o=await r(837)(`./${t}`);const i=new o[t];i.setTapActionTarget({area_id:a}),e.push(i.getChip())}catch(e){c.W.logError(`An error occurred while creating the ${n} chip!`,e)}}return t?.extra_chips&&e.push(...t.extra_chips),e},n=function(){if(c.W.strategyOptions.home_view.hidden.includes("persons"))return[];const e=[];return Promise.resolve().then(r.bind(r,877)).then((t=>{for(const r of c.W.entities.filter((e=>e.entity_id.startsWith("person.")&&null==e.hidden_by&&null==e.disabled_by)))e.push(new t.PersonCard(r).getCard())})),e},s=async function(){if(c.W.strategyOptions.home_view.hidden.includes("areas"))return[];const e=[];let t=[];c.W.strategyOptions.home_view.hidden.includes("areasTitle")||e.push({type:"custom:mushroom-title-card",title:"Areas"});for(const[i,a]of c.W.areas.entries()){let o,n=c.W.strategyOptions.areas[a.area_id]?.type??c.W.strategyOptions.areas._?.type??"default";try{o=await r(175)(`./${n}`)}catch(e){o=await Promise.resolve().then(r.bind(r,396)),c.W.strategyOptions.debug&&"default"!==n&&console.error(e)}if(!c.W.strategyOptions.areas[a.area_id]?.hidden){let e={...c.W.strategyOptions.areas._,...c.W.strategyOptions.areas[a.area_id]};t.push(new o.AreaCard(a,e).getCard())}if(i===c.W.areas.length-1)for(let r=0;r{"use strict";r.r(t),r.d(t,{LightView:()=>h});var i,a,o,n,s=r(371),c=r(607),d=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends d.AbstractView{constructor(e={}){super(l(i,i,"f",a)),o.set(this,{title:"Lights",path:"lights",icon:"mdi:lightbulb-group",subview:!1,controllerCardOptions:{iconOn:"mdi:lightbulb",iconOff:"mdi:lightbulb-off",onService:"light.turn_on",offService:"light.turn_off"}}),n.set(this,{title:"All Lights",subtitle:s.W.getCountTemplate(l(i,i,"f",a),"eq","on")+" lights on"}),this.config=Object.assign(this.config,l(this,o,"f"),e),this.viewControllerCard=new c.ControllerCard(this.targetDomain(l(i,i,"f",a)),{...l(this,n,"f"),..."controllerCardOptions"in this.config?this.config.controllerCardOptions:{}}).createCard()}}i=h,o=new WeakMap,n=new WeakMap,a={value:"light"}},683:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SwitchView:()=>h});var i,a,o,n,s=r(371),c=r(607),d=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends d.AbstractView{constructor(e={}){super(l(i,i,"f",a)),o.set(this,{title:"Switches",path:"switches",icon:"mdi:dip-switch",subview:!1,controllerCardOptions:{iconOn:"mdi:power-plug",iconOff:"mdi:power-plug-off",onService:"switch.turn_on",offService:"switch.turn_off"}}),n.set(this,{title:"All Switches",subtitle:s.W.getCountTemplate(l(i,i,"f",a),"eq","on")+" switches on"}),this.config=Object.assign(this.config,l(this,o,"f"),e),this.viewControllerCard=new c.ControllerCard(this.targetDomain(l(i,i,"f",a)),{...l(this,n,"f"),..."controllerCardOptions"in this.config?this.config.controllerCardOptions:{}}).createCard()}}i=h,o=new WeakMap,n=new WeakMap,a={value:"switch"}},610:(e,t,r)=>{"use strict";r.r(t),r.d(t,{VacuumView:()=>h});var i,a,o,n,s=r(371),c=r(607),d=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class h extends d.AbstractView{constructor(e={}){super(l(i,i,"f",a)),o.set(this,{title:"Vacuums",path:"vacuums",icon:"mdi:robot-vacuum",subview:!1,controllerCardOptions:{iconOn:"mdi:robot-vacuum",iconOff:"mdi:robot-vacuum-off",onService:"vacuum.start",offService:"vacuum.stop"}}),n.set(this,{title:"All Vacuums",subtitle:s.W.getCountTemplate(l(i,i,"f",a),"ne","off")+" vacuums on"}),this.config=Object.assign(this.config,l(this,o,"f"),e),this.viewControllerCard=new c.ControllerCard(this.targetDomain(l(i,i,"f",a)),{...l(this,n,"f"),..."controllerCardOptions"in this.config?this.config.controllerCardOptions:{}}).createCard()}}i=h,o=new WeakMap,n=new WeakMap,a={value:"vacuum"}},175:(e,t,r)=>{var i={"./AbstractCard":[976],"./AbstractCard.ts":[976],"./AreaCard":[396,179],"./AreaCard.ts":[396,179],"./BinarySensorCard":[418,179],"./BinarySensorCard.ts":[418,179],"./CameraCard":[604,179],"./CameraCard.ts":[604,179],"./ClimateCard":[782,179],"./ClimateCard.ts":[782,179],"./ControllerCard":[607],"./ControllerCard.ts":[607],"./CoverCard":[758,179],"./CoverCard.ts":[758,179],"./FanCard":[287,179],"./FanCard.ts":[287,179],"./HaAreaCard":[387,179],"./HaAreaCard.ts":[387,179],"./LightCard":[475,179],"./LightCard.ts":[475,179],"./LockCard":[696,179],"./LockCard.ts":[696,179],"./MediaPlayerCard":[499,179],"./MediaPlayerCard.ts":[499,179],"./MiscellaneousCard":[571,179],"./MiscellaneousCard.ts":[571,179],"./NumberCard":[420,179],"./NumberCard.ts":[420,179],"./PersonCard":[877,179],"./PersonCard.ts":[877,179],"./SensorCard":[642],"./SensorCard.ts":[642],"./SwitchCard":[330,179],"./SwitchCard.ts":[330,179],"./VacuumCard":[695,179],"./VacuumCard.ts":[695,179]};function a(e){if(!r.o(i,e))return Promise.resolve().then((()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=i[e],a=t[0];return Promise.all(t.slice(1).map(r.e)).then((()=>r(a)))}a.keys=()=>Object.keys(i),a.id=175,e.exports=a},837:(e,t,r)=>{var i={"./AbstractChip":[370,179],"./AbstractChip.ts":[370,179],"./ClimateChip":[660,179],"./ClimateChip.ts":[660,179],"./CoverChip":[993,179],"./CoverChip.ts":[993,179],"./FanChip":[966,179],"./FanChip.ts":[966,179],"./LightChip":[95,179],"./LightChip.ts":[95,179],"./SwitchChip":[817,179],"./SwitchChip.ts":[817,179],"./WeatherChip":[977,179],"./WeatherChip.ts":[977,179]};function a(e){if(!r.o(i,e))return Promise.resolve().then((()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=i[e],a=t[0];return r.e(t[1]).then((()=>r(a)))}a.keys=()=>Object.keys(i),a.id=837,e.exports=a},968:(e,t,r)=>{var i={"./AbstractView":[1,179],"./AbstractView.ts":[1,179],"./CameraView":[89,179],"./CameraView.ts":[89,179],"./ClimateView":[81,179],"./ClimateView.ts":[81,179],"./CoverView":[136,179],"./CoverView.ts":[136,179],"./FanView":[964,179],"./FanView.ts":[964,179],"./HomeView":[985,179],"./HomeView.ts":[985,179],"./LightView":[144,179],"./LightView.ts":[144,179],"./SwitchView":[683,179],"./SwitchView.ts":[683,179],"./VacuumView":[610,179],"./VacuumView.ts":[610,179]};function a(e){if(!r.o(i,e))return Promise.resolve().then((()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=i[e],a=t[0];return r.e(t[1]).then((()=>r(a)))}a.keys=()=>Object.keys(i),a.id=968,e.exports=a}},t={};function r(i){var a=t[i];if(void 0!==a)return a.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,r),o.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.e=()=>Promise.resolve(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{"use strict";var e=r(371),t=r(642),i=r(607);class a extends HTMLTemplateElement{static async generateDashboard(t){await e.W.initialize(t);const i=t.config?.views??[];let a;for(let t of e.W.getExposedViewIds())try{const o=e.W.sanitizeClassName(t+"View");a=await r(968)(`./${o}`);const n=await new a[o](e.W.strategyOptions.views[t]).getView();n.cards?.length&&i.push(n)}catch(r){e.W.logError(`View '${t}' couldn't be loaded!`,r)}for(let t of e.W.areas)t.hidden||i.push({title:t.name,path:t.area_id??t.name,subview:!0,strategy:{type:"custom:mushroom-strategy",options:{area:t}}});return e.W.strategyOptions.extra_views&&i.push(...e.W.strategyOptions.extra_views),{views:i}}static async generateView(a){const o=e.W.getExposedDomainIds(),n=a.view.strategy?.options?.area??{},s=[...n.extra_cards??[]];let c={area_id:[n.area_id]};for(const a of o){if("default"===a)continue;const o=e.W.sanitizeClassName(a+"Card");let d=[];try{d=await r(175)(`./${o}`).then((r=>{let s=[];const d=e.W.getDeviceEntities(n,a);let l=e.W.strategyOptions.domains[a??"_"].hide_config_entities||e.W.strategyOptions.domains._.hide_config_entities;if("undisclosed"===n.area_id&&(c={entity_id:d.map((e=>e.entity_id))}),d.length){const h=new i.ControllerCard(c,e.W.strategyOptions.domains[a]).createCard();if("sensor"===a){const r=e.W.getStateEntities(n,"sensor"),i=[];for(const a of d){const o=r.find((e=>e.entity_id===a.entity_id));let n=e.W.strategyOptions.card_options?.[a.entity_id],s=e.W.strategyOptions.card_options?.[a.device_id??"null"];n?.hidden||s?.hidden||(o?.attributes.unit_of_measurement&&(n={type:"custom:mini-graph-card",entities:[a.entity_id],...n}),i.push(new t.SensorCard(a,n).getCard()))}return i.length&&(s.push({type:"vertical-stack",cards:i}),s.unshift(h)),s}for(const t of d){let i,a=e.W.strategyOptions.card_options?.[t.entity_id];t.device_id&&(i=e.W.strategyOptions.card_options?.[t.device_id]),a?.hidden||i?.hidden||"config"===t.entity_category&&l||s.push(new r[o](t,a).getCard())}if("binary_sensor"===a){const e=[];for(let t=0;te.area_id===n.area_id)).map((e=>e.id)),a=e.W.entities.filter((e=>{const r=t.includes(e.device_id??"null")||e.area_id===n.area_id,i=null===e.hidden_by&&null===e.disabled_by,a=o.includes(e.entity_id.split(".",1)[0]);return i&&!a&&r}));if(a.length){let t=[];try{t=await Promise.resolve().then(r.bind(r,571)).then((t=>{const r=[new i.ControllerCard(c,e.W.strategyOptions.domains.default).createCard()];for(const i of a){let a=e.W.strategyOptions.card_options?.[i.entity_id],o=e.W.strategyOptions.card_options?.[i.device_id??"null"];a?.hidden||o?.hidden||"config"===i.entity_category&&e.W.strategyOptions.domains._.hide_config_entities||r.push(new t.MiscellaneousCard(i,a).getCard())}return r}))}catch(t){e.W.logError("An error occurred while creating the domain cards!",t)}s.push({type:"vertical-stack",cards:t})}}return{cards:s}}}customElements.define("ll-strategy-mushroom-strategy",a),console.info("%c Mushroom Strategy %c ".concat("v2.1.0"," "),"color: white; background: coral; font-weight: 700;","color: coral; background: white; font-weight: 700;")})()})(); \ No newline at end of file +(()=>{var e={996:e=>{"use strict";var t=function(e){return function(e){return!!e&&"object"==typeof e}(e)&&!function(e){var t=Object.prototype.toString.call(e);return"[object RegExp]"===t||"[object Date]"===t||function(e){return e.$$typeof===r}(e)}(e)},r="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function i(e,t){return!1!==t.clone&&t.isMergeableObject(e)?s((r=e,Array.isArray(r)?[]:{}),e,t):e;var r}function a(e,t,r){return e.concat(t).map((function(e){return i(e,r)}))}function o(e){return Object.keys(e).concat(function(e){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e).filter((function(t){return Object.propertyIsEnumerable.call(e,t)})):[]}(e))}function n(e,t){try{return t in e}catch(e){return!1}}function s(e,r,c){(c=c||{}).arrayMerge=c.arrayMerge||a,c.isMergeableObject=c.isMergeableObject||t,c.cloneUnlessOtherwiseSpecified=i;var l=Array.isArray(r);return l===Array.isArray(e)?l?c.arrayMerge(e,r,c):function(e,t,r){var a={};return r.isMergeableObject(e)&&o(e).forEach((function(t){a[t]=i(e[t],r)})),o(t).forEach((function(o){(function(e,t){return n(e,t)&&!(Object.hasOwnProperty.call(e,t)&&Object.propertyIsEnumerable.call(e,t))})(e,o)||(n(e,o)&&r.isMergeableObject(t[o])?a[o]=function(e,t){if(!t.customMerge)return s;var r=t.customMerge(e);return"function"==typeof r?r:s}(o,r)(e[o],t[o],r):a[o]=i(t[o],r))})),a}(e,r,c):i(r,c)}s.all=function(e,t){if(!Array.isArray(e))throw new Error("first argument should be an array");return e.reduce((function(e,r){return s(e,r,t)}),{})};var c=s;e.exports=c},728:(e,t,r)=>{"use strict";r.d(t,{W:()=>_});const i={areas:{undisclosed:{floor_id:null,aliases:[],area_id:"undisclosed",name:"Undisclosed",picture:null,hidden:!1}},debug:!1,domains:{_:{hide_config_entities:!1},default:{title:"Miscellaneous",showControls:!1,hidden:!1},light:{title:"Lights",showControls:!0,iconOn:"mdi:lightbulb",iconOff:"mdi:lightbulb-off",onService:"light.turn_on",offService:"light.turn_off",hidden:!1},fan:{title:"Fans",showControls:!0,iconOn:"mdi:fan",iconOff:"mdi:fan-off",onService:"fan.turn_on",offService:"fan.turn_off",hidden:!1},cover:{title:"Covers",showControls:!0,iconOn:"mdi:arrow-up",iconOff:"mdi:arrow-down",onService:"cover.open_cover",offService:"cover.close_cover",hidden:!1},switch:{title:"Switches",showControls:!0,iconOn:"mdi:power-plug",iconOff:"mdi:power-plug-off",onService:"switch.turn_on",offService:"switch.turn_off",hidden:!1},camera:{title:"Cameras",showControls:!1,hidden:!1},lock:{title:"Locks",showControls:!1,hidden:!1},climate:{title:"Climates",showControls:!1,hidden:!1},media_player:{title:"Media Players",showControls:!1,hidden:!1},sensor:{title:"Sensors",showControls:!1,hidden:!1},binary_sensor:{title:"Binary Sensors",showControls:!1,hidden:!1},number:{title:"Numbers",showControls:!1,hidden:!1},vacuum:{title:"Vacuums",showControls:!0,hidden:!1},lawn_mower:{title:"Lawn Mower",showControls:!0,hidden:!1}},home_view:{hidden:[]},views:{home:{},light:{},fan:{},cover:{},switch:{},climate:{},camera:{},vacuum:{},lawn_mower:{},binary_sensor:{}}};var a=r(996),o=r.n(a);function n(e){return void 0!==e}var s,c,l,d,h,f,u,p,w,m,g,v,y,C=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)},b=function(e,t,r,i,a){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");return"a"===i?a.call(e,r):a?a.value=r:t.set(e,r),r};class _{constructor(){throw new Error("This class should be invoked with method initialize() instead of using the keyword new!")}static get strategyOptions(){return C(this,s,"f",w)}static get areas(){return C(this,s,"f",d)}static get devices(){return C(this,s,"f",l)}static get entities(){return C(this,s,"f",c)}static get labels(){return C(this,s,"f",h)}static get debug(){return C(this,s,"f",g)}static async initialize(e){b(this,s,e.hass.states,"f",u),b(this,s,o()(i,e.config?.strategy?.options??{}),"f",w),b(this,s,C(this,s,"f",w).debug,"f",g);try{[{set value(e){b(s,s,e,"f",c)}}.value,{set value(e){b(s,s,e,"f",l)}}.value,{set value(e){b(s,s,e,"f",d)}}.value,{set value(e){b(s,s,e,"f",h)}}.value,{set value(e){b(s,s,e,"f",f)}}.value]=await Promise.all([e.hass.callWS({type:"config/entity_registry/list"}),e.hass.callWS({type:"config/device_registry/list"}),e.hass.callWS({type:"config/area_registry/list"}),e.hass.callWS({type:"config/label_registry/list"}),e.hass.callWS({type:"config/floor_registry/list"})])}catch(e){throw s.logError("An error occurred while querying Home assistant's registries!",e),"Check the console for details"}C(this,s,"f",w).areas.undisclosed?.hidden||(C(this,s,"f",w).areas.undisclosed={...i.areas.undisclosed,...C(this,s,"f",w).areas.undisclosed},C(this,s,"f",w).areas.undisclosed.area_id="undisclosed",C(this,s,"f",d).push(C(this,s,"f",w).areas.undisclosed)),b(this,s,s.areas.map((e=>({...e,...C(this,s,"f",w).areas?.[e.area_id]}))),"f",d),b(this,s,C(this,s,"f",f).reduce(((e,t)=>({...e,[t.floor_id]:t.level})),{}),"f",m),C(this,s,"f",d).sort(((e,t)=>(e.order??1/0)-(t.order??1/0)||(C(this,s,"f",m)[e.floor_id??""]??1/0)-(C(this,s,"f",m)[t.floor_id??""]??1/0)||e.name.localeCompare(t.name))),C(this,s,"f",w).domains=Object.fromEntries(Object.entries(C(this,s,"f",w).domains).sort((([,e],[,t])=>(e.order??1/0)-(t.order??1/0)||(e.title??"undefined").localeCompare(t.title??"undefined")))),b(this,s,!0,"f",p)}static sortViews(e){const t=e.filter((e=>!Number.isInteger(e.order)));return e.filter((e=>Number.isInteger(e.order))).sort(((e,t)=>(e.order??1/0)-(t.order??1/0))).forEach((e=>t.splice(e.order??1/0,0,e))),t}static isInitialized(){return C(this,s,"f",p)}static getCountTemplate(e,t,r){const i=[];this.isInitialized()||console.warn("Helper class should be initialized before calling this method!");for(const t of C(this,s,"f",d)){const r=C(this,s,"f",l).filter((e=>e.area_id===t.area_id)).map((e=>e.id));C(this,s,"f",c).filter(C(this,s,"m",v),{area:t,domain:e,areaDeviceIds:r}).forEach((e=>i.push(e)))}return this.getCountEntityTemplate(i,t,r)}static getCountEntityTemplate(e,t,r){return this.isInitialized()||console.warn("Helper class should be initialized before calling this method!"),`{% set entities = [${e.map((e=>`states['${e.entity_id}']`))}] %} {{ entities | selectattr('state','${t}','${r}') | list | count }}`}static getDeviceEntities(e,t){this.isInitialized()||console.warn("Helper class should be initialized before calling this method!");const r=C(this,s,"f",l).filter((t=>(t.area_id??"undisclosed")===e.area_id)).map((e=>e.id));return C(this,s,"f",c).filter(C(this,s,"m",v),{area:e,domain:t,areaDeviceIds:r}).sort(((e,t)=>(e.original_name??"undefined").localeCompare(t.original_name??"undefined")))}static getStateEntities(e,t){this.isInitialized()||console.warn("Helper class should be initialized before calling this method!");const r=[],i=Object.fromEntries(C(this,s,"f",c).map((e=>[e.entity_id,e]))),a=Object.fromEntries(C(this,s,"f",l).map((e=>[e.id,e]))),o=Object.values(C(this,s,"f",u)).filter((e=>e.entity_id.startsWith(`${t}.`)));for(const t of o){const o=i[t.entity_id],n=a[o?.device_id??""];(o?.area_id===e.area_id||n&&n.area_id===e.area_id)&&r.push(t)}return r}static sanitizeClassName(e){return(e=e.charAt(0).toUpperCase()+e.slice(1)).replace(/([-_][a-z])/g,(e=>e.toUpperCase().replace("-","").replace("_","")))}static entitiesOfDomain(e){return s.entities.filter((t=>t.entity_id.startsWith(e+".")&&!t.hidden_by&&!s.strategyOptions.card_options?.[t.entity_id]?.hidden))}static labelsOfDomain(e){const t=this.entitiesOfDomain(e).flatMap((e=>e.labels));return[...new Set(t)].map((e=>this.getLabelById(e))).filter(n).filter((t=>t.name.startsWith(this.getLabelPrefix(e))))}static getLabelById(e){return C(this,s,"f",h).find((t=>t.label_id===e))}static getViewIds(){return this.isInitialized()||console.warn("Helper class should be initialized before calling this method!"),Object.keys(C(this,s,"f",w).views).filter((e=>i.views[e]))}static getExposedDomainIds(){return this.isInitialized()||console.warn("Helper class should be initialized before calling this method!"),C(this,s,"m",y).call(this,C(this,s,"f",w).domains,"hidden",!1)}static toTargetEntities(e){return{entity_id:e.map((e=>e.entity_id))}}static logError(e,t){s.debug?console.error(e,t):console.error(e)}}s=_,v=function(e){const t=null===e.hidden_by&&null===e.disabled_by,r=e.entity_id.startsWith(`${this.domain}.`),i="undisclosed"===this.area.area_id?!e.area_id&&(this.areaDeviceIds.includes(e.device_id??"")||!e.device_id):this.areaDeviceIds.includes(e.device_id??"")||e.area_id===this.area.area_id;return t&&r&&i},y=function(e,t,r){const i=[];for(const a of Object.keys(e))e[a][t]===r&&i.push(a);return i},c={value:void 0},l={value:void 0},d={value:[]},h={value:[]},f={value:[]},u={value:void 0},p={value:!1},w={value:void 0},m={value:void 0},g={value:void 0},_.getLabelPrefix=e=>`ms_${e}_`},976:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AbstractCard:()=>a});var i=r(728);class a{constructor(e){if(this.config={type:"custom:mushroom-entity-card",icon:"mdi:help-circle"},!i.W.isInitialized())throw new Error("The Helper module must be initialized before using this one.");this.entity=e}getCard(){return{...this.config,entity:"entity_id"in this.entity?this.entity.entity_id:void 0}}}},396:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AreaCard:()=>n});var i,a=r(976),o=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class n extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-template-card",primary:void 0,icon:"mdi:texture-box",icon_color:"blue",tap_action:{action:"navigate",navigation_path:""},hold_action:{action:"none"}}),"default"===t.type&&delete t.type,o(this,i,"f").primary=e.name,o(this,i,"f").tap_action&&"navigation_path"in o(this,i,"f").tap_action&&(o(this,i,"f").tap_action.navigation_path=e.area_id),this.config=Object.assign(this.config,o(this,i,"f"),t)}}i=new WeakMap},418:(e,t,r)=>{"use strict";r.r(t),r.d(t,{BinarySensorCard:()=>o});var i,a=r(642);class o extends a.SensorCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon:"mdi:power-cycle",icon_color:"green"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},604:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CameraCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{entity:"",type:"picture-entity",show_name:!1,show_state:!1,camera_view:"live"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},782:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ClimateCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-climate-card",icon:void 0,hvac_modes:["off","cool","heat","fan_only"],show_temperature_control:!0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},607:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ControllerCard:()=>o});var i,a=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class o{constructor(e,t={}){this.target=e,i.set(this,{type:"mushroom-title-card",showControls:!0,iconOn:"mdi:power-on",iconOff:"mdi:power-off",onService:"none",offService:"none"}),function(e,t,r,i,a){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===i?a.call(e,r):a?a.value=r:t.set(e,r)}(this,i,{...a(this,i,"f"),...t},"f")}createCard(){const e=[{type:"custom:mushroom-title-card",title:a(this,i,"f").title,subtitle:a(this,i,"f").subtitle}];return a(this,i,"f").showControls&&e.push({type:"horizontal-stack",cards:[{type:"custom:mushroom-template-card",icon:a(this,i,"f").iconOn,layout:"vertical",icon_color:"amber",tap_action:{action:"call-service",service:a(this,i,"f").onService,target:this.target,data:{}}},{type:"custom:mushroom-template-card",icon:a(this,i,"f").iconOff,layout:"vertical",icon_color:"red",tap_action:{action:"call-service",service:a(this,i,"f").offService,target:this.target,data:{}}}]}),{type:"horizontal-stack",cards:e}}}i=new WeakMap},758:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CoverCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-cover-card",icon:void 0,show_buttons_control:!0,show_position_control:!0,show_tilt_position_control:!0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},287:(e,t,r)=>{"use strict";r.r(t),r.d(t,{FanCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-fan-card",icon:void 0,show_percentage_control:!0,show_oscillate_control:!0,icon_animation:!0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},387:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AreaCard:()=>n});var i,a=r(976),o=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class n extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"area",area:""}),o(this,i,"f").area=e.area_id,o(this,i,"f").navigation_path=o(this,i,"f").area,delete t.type,this.config=Object.assign(this.config,o(this,i,"f"),t)}}i=new WeakMap},548:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LawnMowerCard:()=>n});var i=r(976);const a=["on_off","start_pause","return_home"];var o;class n extends i.AbstractCard{constructor(e,t={}){super(e),o.set(this,{type:"custom:mushroom-vacuum-card",icon:void 0,icon_animation:!0,commands:[...a],tap_action:{action:"more-info"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,o,"f"),t)}}o=new WeakMap},475:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LightCard:()=>l});var i,a=r(976),o=r(596),n=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)},s=o.R.isCallServiceActionConfig,c=o.R.isCallServiceActionTarget;class l extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-light-card",icon:void 0,show_brightness_control:!0,show_color_control:!0,show_color_temp_control:!0,use_light_color:!0,double_tap_action:{action:"call-service",service:"light.turn_on",target:{entity_id:void 0},data:{rgb_color:[255,255,255]}}}),s(n(this,i,"f").double_tap_action)&&c(n(this,i,"f").double_tap_action.target)&&(n(this,i,"f").double_tap_action.target.entity_id=e.entity_id),this.config=Object.assign(this.config,n(this,i,"f"),t)}}i=new WeakMap},696:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LockCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-lock-card",icon:void 0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},499:(e,t,r)=>{"use strict";r.r(t),r.d(t,{MediaPlayerCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-media-player-card",use_media_info:!0,media_controls:["on_off","play_pause_stop"],show_volume_level:!0,volume_controls:["volume_mute","volume_set","volume_buttons"]}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},571:(e,t,r)=>{"use strict";r.r(t),r.d(t,{MiscellaneousCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon_color:"blue-grey"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},420:(e,t,r)=>{"use strict";r.r(t),r.d(t,{NumberCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-number-card",icon:void 0}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},877:(e,t,r)=>{"use strict";r.r(t),r.d(t,{PersonCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-person-card",layout:"vertical",primary_info:"none",secondary_info:"none",icon_type:"entity-picture"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},642:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SensorCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon:"mdi:information",animate:!0,line_color:"green"}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},330:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SwitchCard:()=>o});var i,a=r(976);class o extends a.AbstractCard{constructor(e,t={}){super(e),i.set(this,{type:"custom:mushroom-entity-card",icon:void 0,tap_action:{action:"toggle"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),t)}}i=new WeakMap},695:(e,t,r)=>{"use strict";r.r(t),r.d(t,{VacuumCard:()=>n});var i=r(976);const a=["on_off","start_pause","stop","locate","clean_spot","return_home"];var o;class n extends i.AbstractCard{constructor(e,t={}){super(e),o.set(this,{type:"custom:mushroom-vacuum-card",icon:void 0,icon_animation:!0,commands:[...a],tap_action:{action:"more-info"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,o,"f"),t)}}o=new WeakMap},370:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AbstractChip:()=>o});var i=r(728),a=r(596).R.isCallServiceActionConfig;class o{constructor(){if(this.config={type:"template"},!i.W.isInitialized())throw new Error("The Helper module must be initialized before using this one.")}getChip(){return this.config}setTapActionTarget(e){"tap_action"in this.config&&a(this.config.tap_action)?this.config.tap_action.target=e:i.W.debug&&console.warn(this.constructor.name+" - Target not set: Invalid target or tap action.")}}},660:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ClimateChip:()=>n});var i,a=r(728),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:thermostat",icon_color:"orange",content:a.W.getCountTemplate("climate","ne","off"),tap_action:{action:"none"},hold_action:{action:"navigate",navigation_path:"climates"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},993:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CoverChip:()=>n});var i,a=r(728),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:window-open",icon_color:"cyan",content:a.W.getCountTemplate("cover","eq","open"),tap_action:{action:"none"},hold_action:{action:"navigate",navigation_path:"covers"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},966:(e,t,r)=>{"use strict";r.r(t),r.d(t,{FanChip:()=>n});var i,a=r(728),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:fan",icon_color:"green",content:a.W.getCountTemplate("fan","eq","on"),tap_action:{action:"call-service",service:"fan.turn_off"},hold_action:{action:"navigate",navigation_path:"fans"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},95:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LightChip:()=>n});var i,a=r(728),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:lightbulb-group",icon_color:"amber",content:a.W.getCountTemplate("light","eq","on"),tap_action:{action:"call-service",service:"light.turn_off"},hold_action:{action:"navigate",navigation_path:"lights"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},817:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SwitchChip:()=>n});var i,a=r(728),o=r(370);class n extends o.AbstractChip{constructor(e={}){super(),i.set(this,{type:"template",icon:"mdi:dip-switch",icon_color:"blue",content:a.W.getCountTemplate("switch","eq","on"),tap_action:{action:"call-service",service:"switch.turn_off"},hold_action:{action:"navigate",navigation_path:"switches"}}),this.config=Object.assign(this.config,function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)}(this,i,"f"),e)}}i=new WeakMap},977:(e,t,r)=>{"use strict";r.r(t),r.d(t,{WeatherChip:()=>n});var i,a=r(370),o=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class n extends a.AbstractChip{constructor(e,t={}){super(),i.set(this,{type:"weather",show_temperature:!0,show_conditions:!0}),function(e,t,r,i,a){if("m"===i)throw new TypeError("Private method is not writable");if("a"===i&&!a)throw new TypeError("Private accessor was defined without a setter");if("function"==typeof t?e!==t||!a:!t.has(e))throw new TypeError("Cannot write private member to an object whose class did not declare it");"a"===i?a.call(e,r):a?a.value=r:t.set(e,r)}(this,i,{...o(this,i,"f"),entity:e,...t},"f"),this.config=Object.assign(this.config,o(this,i,"f"),t)}}i=new WeakMap},596:(e,t,r)=>{"use strict";var i;r.d(t,{R:()=>i}),function(e){e.isCallServiceActionConfig=function(e){return e&&"call-service"===e.action&&["action","service"].every((t=>t in e))},e.isCallServiceActionTarget=function(e){return e&&["entity_id","device_id","area_id"].some((t=>t in e))}}(i||(i={}))},1:(e,t,r)=>{"use strict";r.r(t),r.d(t,{AbstractView:()=>o});var i=r(728),a=r(607);class o{constructor(e=""){if(this.domain=e,!i.W.isInitialized())throw new Error("The Helper module must be initialized before using this one.");this.domain=e,this.prefix=this.domain?i.W.getLabelPrefix(this.domain):""}createCard(e,t){return new a.ControllerCard(i.W.toTargetEntities(e),{...this.viewControllerCardConfig(e,t),..."controllerCardOptions"in this.defaultConfig?this.defaultConfig.controllerCardOptions:{}}).createCard()}async createViewCards(e,t){const o=[],n=i.W.strategyOptions.domains[this.domain??"_"].hide_config_entities||i.W.strategyOptions.domains._.hide_config_entities;for(const t of i.W.areas){const s=[],c=i.W.getDeviceEntities(t,this.domain).filter(e),l=i.W.sanitizeClassName(this.domain+"Card"),d=await r(175)(`./${l}`),h=i.W.toTargetEntities(c);for(const e of c){let t=i.W.strategyOptions.card_options?.[e.entity_id],r=i.W.strategyOptions.card_options?.[e.device_id??"null"];t?.hidden||r?.hidden||"config"===e.entity_category&&n||s.push(new d[l](e,t).getCard())}if(s.length){const e="controllerCardOptions"in this.defaultConfig?this.defaultConfig.controllerCardOptions:{};s.unshift(new a.ControllerCard(h,Object.assign({title:t.name},e)).createCard()),o.push({type:"vertical-stack",cards:s})}}if(o.length){const r=i.W.entitiesOfDomain(this.domain).filter(e);o.unshift(this.createCard(r,t))}return o}async getView(){const e=this.domain?i.W.labelsOfDomain(this.domain):[],t=(await Promise.all(e.map((async e=>await this.createViewCards((t=>t.labels.includes(e.label_id)),e.name.replace(this.prefix,"").replace("_"," ")))))).map(((t,r)=>{const a=e[r],o=a.name.replace(this.prefix,"");return{title:o,...this.defaultConfig,icon:i.W.getLabelById(a.label_id)?.icon||this.defaultConfig.icon,path:o.replace(this.prefix,"").toLowerCase(),...i.W.strategyOptions.views[a.name],id:a.name,cards:t}}));return[{...this.defaultConfig,...i.W.strategyOptions.views[this.domain??this.defaultConfig.title?.toLowerCase()],id:this.defaultConfig.id,cards:await this.createViewCards((e=>!this.prefix||!e.labels.some((e=>e.startsWith(this.prefix)))))},...t]}}},578:(e,t,r)=>{"use strict";r.r(t),r.d(t,{BinarySensorView:()=>s});var i,a,o=r(1),n=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class s extends o.AbstractView{constructor(){super(n(i,i,"f",a)),this.defaultConfig={id:n(i,i,"f",a),title:"Binary Sensors",path:"binary-sensors",icon:"mdi:numeric-10",subview:!1,controllerCardOptions:{showControls:!1}},this.viewControllerCardConfig=(e,t="binary sensors")=>({title:`All ${t}`})}}i=s,a={value:"binary_sensor"}},89:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CameraView:()=>c});var i,a,o=r(1),n=r(728),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends o.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Cameras",path:"cameras",icon:"mdi:cctv",subview:!1,controllerCardOptions:{showControls:!1}},this.viewControllerCardConfig=(e,t="cameras")=>({title:`All ${t}`,subtitle:n.W.getCountEntityTemplate(e,"ne","off")+` ${t} on`})}}i=c,a={value:"camera"}},81:(e,t,r)=>{"use strict";r.r(t),r.d(t,{ClimateView:()=>c});var i,a,o=r(728),n=r(1),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends n.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Climates",path:"climates",icon:"mdi:thermostat",subview:!1,controllerCardOptions:{showControls:!1}},this.viewControllerCardConfig=(e,t="climates")=>({title:`All ${t}`,subtitle:o.W.getCountEntityTemplate(e,"ne","off")+` ${t} on`})}}i=c,a={value:"climate"}},136:(e,t,r)=>{"use strict";r.r(t),r.d(t,{CoverView:()=>c});var i,a,o=r(728),n=r(1),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends n.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Covers",path:"covers",icon:"mdi:window-open",subview:!1,controllerCardOptions:{iconOn:"mdi:arrow-up",iconOff:"mdi:arrow-down",onService:"cover.open_cover",offService:"cover.close_cover"}},this.viewControllerCardConfig=(e,t="covers")=>({title:`All ${t}`,subtitle:o.W.getCountEntityTemplate(e,"eq","open")+` ${t} open`})}}i=c,a={value:"cover"}},964:(e,t,r)=>{"use strict";r.r(t),r.d(t,{FanView:()=>c});var i,a,o=r(728),n=r(1),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends n.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Fans",path:"fans",icon:"mdi:fan",subview:!1,controllerCardOptions:{iconOn:"mdi:fan",iconOff:"mdi:fan-off",onService:"fan.turn_on",offService:"fan.turn_off"}},this.viewControllerCardConfig=(e,t="fans")=>({title:`All ${t}`,subtitle:o.W.getCountEntityTemplate(e,"eq","on")+` ${t} on`})}}i=c,a={value:"fan"}},985:(e,t,r)=>{"use strict";r.r(t),r.d(t,{HomeView:()=>d});var i,a,o,n,s=r(728),c=r(1),l=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class d extends c.AbstractView{constructor(){super(),i.add(this),this.defaultConfig={id:"home",title:"Home",icon:"mdi:home-assistant",path:"home",subview:!1},this.viewControllerCardConfig=()=>({})}async createViewCards(){return await Promise.all([l(this,i,"m",a).call(this),l(this,i,"m",o).call(this),l(this,i,"m",n).call(this)]).then((([e,t,r])=>{const i=s.W.strategyOptions,a=[];return e.length&&a.push({type:"custom:mushroom-chips-card",alignment:"center",chips:e}),t.length&&a.push({type:"horizontal-stack",cards:t}),s.W.strategyOptions.home_view.hidden.includes("greeting")||a.push({type:"custom:mushroom-template-card",primary:"{% set time = now().hour %} {% if (time >= 18) %} Good Evening, {{user}}! {% elif (time >= 12) %} Good Afternoon, {{user}}! {% elif (time >= 5) %} Good Morning, {{user}}! {% else %} Hello, {{user}}! {% endif %}",icon:"mdi:hand-wave",icon_color:"orange",tap_action:{action:"none"},double_tap_action:{action:"none"},hold_action:{action:"none"}}),i.quick_access_cards&&a.push(...i.quick_access_cards),a.push({type:"vertical-stack",cards:r}),i.extra_cards&&a.push(...i.extra_cards),a}))}}i=new WeakSet,a=async function(){if(s.W.strategyOptions.home_view.hidden.includes("chips"))return[];const e=[],t=s.W.strategyOptions.chips,i=["light","fan","cover","switch","climate"],a=s.W.areas.map((e=>e.area_id??""));let o;const n=t?.weather_entity??s.W.entities.find((e=>e.entity_id.startsWith("weather.")&&null===e.disabled_by&&null===e.hidden_by))?.entity_id;if(n)try{o=await Promise.resolve().then(r.bind(r,977));const t=new o.WeatherChip(n);e.push(t.getChip())}catch(e){s.W.logError("An error occurred while creating the weather chip!",e)}for(let n of i)if(t?.[`${n}_count`]??1){const t=s.W.sanitizeClassName(n+"Chip");try{o=await r(837)(`./${t}`);const i=new o[t];i.setTapActionTarget({area_id:a}),e.push(i.getChip())}catch(e){s.W.logError(`An error occurred while creating the ${n} chip!`,e)}}return t?.extra_chips&&e.push(...t.extra_chips),e},o=function(){if(s.W.strategyOptions.home_view.hidden.includes("persons"))return[];const e=[];return Promise.resolve().then(r.bind(r,877)).then((t=>{for(const r of s.W.entities.filter((e=>e.entity_id.startsWith("person.")&&null==e.hidden_by&&null==e.disabled_by)))e.push(new t.PersonCard(r).getCard())})),e},n=async function(){if(s.W.strategyOptions.home_view.hidden.includes("areas"))return[];const e=[];let t=[];s.W.strategyOptions.home_view.hidden.includes("areasTitle")||e.push({type:"custom:mushroom-title-card",title:"Areas"});for(const[i,a]of s.W.areas.entries()){let o,n=s.W.strategyOptions.areas[a.area_id]?.type??s.W.strategyOptions.areas._?.type??"default";try{o=await r(175)(`./${n}`)}catch(e){o=await Promise.resolve().then(r.bind(r,396)),s.W.strategyOptions.debug&&"default"!==n&&console.error(e)}if(!s.W.strategyOptions.areas[a.area_id]?.hidden){let e={...s.W.strategyOptions.areas._,...s.W.strategyOptions.areas[a.area_id]};t.push(new o.AreaCard(a,e).getCard())}if(i===s.W.areas.length-1)for(let r=0;r{"use strict";r.r(t),r.d(t,{LawnMowerView:()=>c});var i,a,o=r(728),n=r(1),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends n.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Lawn Mowers",path:"lawn-mowers",icon:"mdi:robot-mower",subview:!1,controllerCardOptions:{iconOn:"mdi:robot-mower",iconOff:"mdi:robot-mower-outline",onService:"lawn_mower.start_mowing",offService:"lawn_mower.dock"}},this.viewControllerCardConfig=(e,t="lawn mowers")=>({title:`All ${t}`,subtitle:o.W.getCountEntityTemplate(e,"ne","off")+` ${t} on`})}}i=c,a={value:"lawn_mower"}},144:(e,t,r)=>{"use strict";r.r(t),r.d(t,{LightView:()=>c});var i,a,o=r(728),n=r(1),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends n.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Lights",path:"lights",icon:"mdi:lightbulb-group",subview:!1,controllerCardOptions:{iconOn:"mdi:lightbulb",iconOff:"mdi:lightbulb-off",onService:"light.turn_on",offService:"light.turn_off"}},this.viewControllerCardConfig=(e,t="lights")=>({title:`All ${t}`,subtitle:o.W.getCountEntityTemplate(e,"eq","on")+` ${t} on`})}}i=c,a={value:"light"}},683:(e,t,r)=>{"use strict";r.r(t),r.d(t,{SwitchView:()=>c});var i,a,o=r(728),n=r(1),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends n.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Switches",path:"switches",icon:"mdi:dip-switch",subview:!1,controllerCardOptions:{iconOn:"mdi:power-plug",iconOff:"mdi:power-plug-off",onService:"switch.turn_on",offService:"switch.turn_off"}},this.viewControllerCardConfig=(e,t="switches")=>({title:`All ${t}`,subtitle:o.W.getCountEntityTemplate(e,"eq","on")+` ${t} on`})}}i=c,a={value:"switch"}},610:(e,t,r)=>{"use strict";r.r(t),r.d(t,{VacuumView:()=>c});var i,a,o=r(728),n=r(1),s=function(e,t,r,i){if("a"===r&&!i)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof t?e!==t||!i:!t.has(e))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===r?i:"a"===r?i.call(e):i?i.value:t.get(e)};class c extends n.AbstractView{constructor(){super(s(i,i,"f",a)),this.defaultConfig={id:s(i,i,"f",a),title:"Vacuums",path:"vacuums",icon:"mdi:robot-vacuum",subview:!1,controllerCardOptions:{iconOn:"mdi:robot-vacuum",iconOff:"mdi:robot-vacuum-off",onService:"vacuum.start",offService:"vacuum.stop"}},this.viewControllerCardConfig=(e,t="vacuums")=>({title:`All ${t}`,subtitle:o.W.getCountEntityTemplate(e,"ne","off")+` ${t} on`})}}i=c,a={value:"vacuum"}},175:(e,t,r)=>{var i={"./AbstractCard":[976],"./AbstractCard.ts":[976],"./AreaCard":[396,179],"./AreaCard.ts":[396,179],"./BinarySensorCard":[418,179],"./BinarySensorCard.ts":[418,179],"./CameraCard":[604,179],"./CameraCard.ts":[604,179],"./ClimateCard":[782,179],"./ClimateCard.ts":[782,179],"./ControllerCard":[607],"./ControllerCard.ts":[607],"./CoverCard":[758,179],"./CoverCard.ts":[758,179],"./FanCard":[287,179],"./FanCard.ts":[287,179],"./HaAreaCard":[387,179],"./HaAreaCard.ts":[387,179],"./LawnMowerCard":[548,179],"./LawnMowerCard.ts":[548,179],"./LightCard":[475,179],"./LightCard.ts":[475,179],"./LockCard":[696,179],"./LockCard.ts":[696,179],"./MediaPlayerCard":[499,179],"./MediaPlayerCard.ts":[499,179],"./MiscellaneousCard":[571,179],"./MiscellaneousCard.ts":[571,179],"./NumberCard":[420,179],"./NumberCard.ts":[420,179],"./PersonCard":[877,179],"./PersonCard.ts":[877,179],"./SensorCard":[642],"./SensorCard.ts":[642],"./SwitchCard":[330,179],"./SwitchCard.ts":[330,179],"./VacuumCard":[695,179],"./VacuumCard.ts":[695,179]};function a(e){if(!r.o(i,e))return Promise.resolve().then((()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=i[e],a=t[0];return Promise.all(t.slice(1).map(r.e)).then((()=>r(a)))}a.keys=()=>Object.keys(i),a.id=175,e.exports=a},837:(e,t,r)=>{var i={"./AbstractChip":[370,179],"./AbstractChip.ts":[370,179],"./ClimateChip":[660,179],"./ClimateChip.ts":[660,179],"./CoverChip":[993,179],"./CoverChip.ts":[993,179],"./FanChip":[966,179],"./FanChip.ts":[966,179],"./LightChip":[95,179],"./LightChip.ts":[95,179],"./SwitchChip":[817,179],"./SwitchChip.ts":[817,179],"./WeatherChip":[977,179],"./WeatherChip.ts":[977,179]};function a(e){if(!r.o(i,e))return Promise.resolve().then((()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=i[e],a=t[0];return r.e(t[1]).then((()=>r(a)))}a.keys=()=>Object.keys(i),a.id=837,e.exports=a},968:(e,t,r)=>{var i={"./AbstractView":[1,179],"./AbstractView.ts":[1,179],"./BinarySensorView":[578,179],"./BinarySensorView.ts":[578,179],"./CameraView":[89,179],"./CameraView.ts":[89,179],"./ClimateView":[81,179],"./ClimateView.ts":[81,179],"./CoverView":[136,179],"./CoverView.ts":[136,179],"./FanView":[964,179],"./FanView.ts":[964,179],"./HomeView":[985,179],"./HomeView.ts":[985,179],"./LawnMowerView":[154,179],"./LawnMowerView.ts":[154,179],"./LightView":[144,179],"./LightView.ts":[144,179],"./SwitchView":[683,179],"./SwitchView.ts":[683,179],"./VacuumView":[610,179],"./VacuumView.ts":[610,179]};function a(e){if(!r.o(i,e))return Promise.resolve().then((()=>{var t=new Error("Cannot find module '"+e+"'");throw t.code="MODULE_NOT_FOUND",t}));var t=i[e],a=t[0];return r.e(t[1]).then((()=>r(a)))}a.keys=()=>Object.keys(i),a.id=968,e.exports=a}},t={};function r(i){var a=t[i];if(void 0!==a)return a.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,r),o.exports}r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},r.d=(e,t)=>{for(var i in t)r.o(t,i)&&!r.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:t[i]})},r.e=()=>Promise.resolve(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},(()=>{"use strict";var e=r(728),t=r(642),i=r(607);class a extends HTMLTemplateElement{static async generateDashboard(t){await e.W.initialize(t);const i=t.config?.views??[];let a,o=[];for(let t of e.W.getViewIds())try{const i=e.W.sanitizeClassName(t+"View");a=await r(968)(`./${i}`),(await(new a[i]).getView()).filter((t=>!e.W.strategyOptions.views[t.id]?.hidden)).filter((e=>e.cards?.length)).forEach((e=>o.push(e)))}catch(r){e.W.logError(`View '${t}' couldn't be loaded!`,r)}e.W.strategyOptions.extra_views&&o.push(...e.W.strategyOptions.extra_views),i.push(...e.W.sortViews(o));for(let t of e.W.areas)t.hidden||i.push({title:t.name,path:t.area_id??t.name,subview:!0,strategy:{type:"custom:mushroom-strategy",options:{area:t}}});return{views:i}}static async generateView(a){const o=e.W.getExposedDomainIds(),n=a.view.strategy?.options?.area??{},s=[...n.extra_cards??[]];let c={area_id:[n.area_id]};for(const a of o){if("default"===a)continue;const o=e.W.sanitizeClassName(a+"Card");let l=[];try{l=await r(175)(`./${o}`).then((r=>{const s=e.W.getDeviceEntities(n,a);let l=e.W.strategyOptions.domains[a??"_"].hide_config_entities||e.W.strategyOptions.domains._.hide_config_entities;"undisclosed"===n.area_id&&(c={entity_id:s.map((e=>e.entity_id))});const d=e.W.getLabelPrefix(a),h=e.W.labelsOfDomain(a),f=h.map((e=>s.filter((t=>t.labels.includes(e.label_id)))));return[s.filter((e=>!e.labels.some((e=>e.startsWith(d))))),...f].flatMap(((s,c)=>{let f=[];if(s.length){const p=h[c-1]?.name?.replace(d,"").replace("_"," ")??e.W.strategyOptions.domains[a].title,w=new i.ControllerCard(e.W.toTargetEntities(s),{...e.W.strategyOptions.domains[a],title:(u=p,u[0].toUpperCase()+u.slice(1))}).createCard();if("sensor"===a){const r=e.W.getStateEntities(n,"sensor"),i=[];for(const a of s){const o=r.find((e=>e.entity_id===a.entity_id));let n=e.W.strategyOptions.card_options?.[a.entity_id],s=e.W.strategyOptions.card_options?.[a.device_id??"null"];n?.hidden||s?.hidden||(o?.attributes.unit_of_measurement&&(n={type:"custom:mini-graph-card",entities:[a.entity_id],...n}),i.push(new t.SensorCard(a,n).getCard()))}return i.length&&(f.push({type:"vertical-stack",cards:i}),f.unshift(w)),f}for(const t of s){let i,a=e.W.strategyOptions.card_options?.[t.entity_id];t.device_id&&(i=e.W.strategyOptions.card_options?.[t.device_id]),a?.hidden||i?.hidden||"config"===t.entity_category&&l||f.push(new r[o](t,a).getCard())}if("binary_sensor"===a){const e=[];for(let t=0;te.area_id===n.area_id)).map((e=>e.id)),a=e.W.entities.filter((e=>{const r=t.includes(e.device_id??"null")||e.area_id===n.area_id,i=null===e.hidden_by&&null===e.disabled_by,a=o.includes(e.entity_id.split(".",1)[0]);return i&&!a&&r}));if(a.length){let t=[];try{t=await Promise.resolve().then(r.bind(r,571)).then((t=>{const r=[new i.ControllerCard(c,e.W.strategyOptions.domains.default).createCard()];for(const i of a){let a=e.W.strategyOptions.card_options?.[i.entity_id],o=e.W.strategyOptions.card_options?.[i.device_id??"null"];a?.hidden||o?.hidden||"config"===i.entity_category&&e.W.strategyOptions.domains._.hide_config_entities||r.push(new t.MiscellaneousCard(i,a).getCard())}return r}))}catch(t){e.W.logError("An error occurred while creating the domain cards!",t)}s.push({type:"vertical-stack",cards:t})}}return{cards:s}}}customElements.define("ll-strategy-mushroom-strategy",a),console.info("%c Mushroom Strategy %c ".concat("v2.1.1"," "),"color: white; background: coral; font-weight: 700;","color: coral; background: white; font-weight: 700;")})()})(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 1dc4cfc8..57cc4fd4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,7 +6,7 @@ "packages": { "": { "name": "mushroom-strategy", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "dependencies": { "deepmerge": "^4" diff --git a/package.json b/package.json index 93884915..16f4eab2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mushroom-strategy", - "version": "2.1.0", + "version": "2.1.1", "description": "Automatically create a dashboard using Mushroom cards", "keywords": [ "strategy", diff --git a/src/Helper.ts b/src/Helper.ts index d5daa53b..46490bbe 100644 --- a/src/Helper.ts +++ b/src/Helper.ts @@ -1,11 +1,15 @@ import {configurationDefaults} from "./configurationDefaults"; -import {HassEntities, HassEntity} from "home-assistant-js-websocket"; +import {HassEntities, HassEntity, HassServiceTarget} from "home-assistant-js-websocket"; import deepmerge from "deepmerge"; import {EntityRegistryEntry} from "./types/homeassistant/data/entity_registry"; import {DeviceRegistryEntry} from "./types/homeassistant/data/device_registry"; import {AreaRegistryEntry} from "./types/homeassistant/data/area_registry"; import {generic} from "./types/strategy/generic"; import StrategyArea = generic.StrategyArea; +import {LabelRegistryEntry} from "./types/homeassistant/data/label_registry"; +import ViewConfig = generic.ViewConfig; +import {FloorRegistryEntry} from "./types/homeassistant/data/floor_registry"; +import {isDefined} from "./utils/object"; /** * Helper Class @@ -37,6 +41,22 @@ class Helper { */ static #areas: StrategyArea[] = []; + /** + * An array of entities from Home Assistant's label registry. + * + * @type {LabelRegistryEntry[]} + * @private + */ + static #labels: LabelRegistryEntry[] = []; + + /** + * An array of entities from Home Assistant's floor registry. + * + * @type {FloorRegistryEntry[]} + * @private + */ + static #floor: any[] = []; + /** * An array of state entities from Home Assistant's Hass object. * @@ -61,6 +81,11 @@ class Helper { */ static #strategyOptions: generic.StrategyConfig; + /** + * map key is floor_id value is level of the floor + * @private + */ + static #floorLevelMap: { [key: string]: number }; /** * Set to true for more verbose information in the console. * @@ -121,6 +146,16 @@ class Helper { return this.#entities; } + /** + * Get the labels from Home Assistant's label registry. + * + * @returns {EntityRegistryEntry[]} + * @static + */ + static get labels(): LabelRegistryEntry[] { + return this.#labels; + } + /** * Get the current debug mode of the mushroom strategy. * @@ -148,10 +183,12 @@ class Helper { // Query the registries of Home Assistant. // noinspection ES6MissingAwait False positive? https://youtrack.jetbrains.com/issue/WEB-63746 - [Helper.#entities, Helper.#devices, Helper.#areas] = await Promise.all([ + [Helper.#entities, Helper.#devices, Helper.#areas, Helper.#labels, Helper.#floor] = await Promise.all([ info.hass.callWS({type: "config/entity_registry/list"}) as Promise, info.hass.callWS({type: "config/device_registry/list"}) as Promise, info.hass.callWS({type: "config/area_registry/list"}) as Promise, + info.hass.callWS({type: "config/label_registry/list"}) as Promise, + info.hass.callWS({type: "config/floor_registry/list"}) as Promise, ]); } catch (e) { Helper.logError("An error occurred while querying Home assistant's registries!", e); @@ -176,18 +213,16 @@ class Helper { return {...area, ...this.#strategyOptions.areas?.[area.area_id]}; }); - // Sort strategy areas by order first and then by name. + this.#floorLevelMap = this.#floor.reduce((acc: { [florName: string]: number }, floor: FloorRegistryEntry) => ({ + ...acc, + [floor.floor_id]: floor.level + }), {}) + + // Sort strategy areas by order first then by floor leven and then by name. this.#areas.sort((a, b) => { - return (a.order ?? Infinity) - (b.order ?? Infinity) || a.name.localeCompare(b.name); + return (a.order ?? Infinity) - (b.order ?? Infinity) || (this.#floorLevelMap[a.floor_id ?? ''] ?? Infinity) - (this.#floorLevelMap[b.floor_id ?? ''] ?? Infinity) || a.name.localeCompare(b.name); }); - // Sort custom and default views of the strategy options by order first and then by title. - this.#strategyOptions.views = Object.fromEntries( - Object.entries(this.#strategyOptions.views).sort(([, a], [, b]) => { - return (a.order ?? Infinity) - (b.order ?? Infinity) || (a.title ?? "undefined").localeCompare(b.title ?? "undefined"); - }), - ); - // Sort custom and default domains of the strategy options by order first and then by title. this.#strategyOptions.domains = Object.fromEntries( Object.entries(this.#strategyOptions.domains).sort(([, a], [, b]) => { @@ -198,6 +233,19 @@ class Helper { this.#initialized = true; } + /** + * sort views according to order or mushroom strategy default + * @param views + */ + static sortViews(views: ViewConfig[]): ViewConfig[] { + const sortedViews = views.filter(item => !Number.isInteger(item.order)); + + views.filter(item => Number.isInteger(item.order)) + .sort((a, b) => (a.order ?? Infinity) - (b.order ?? Infinity)) + .forEach((item) => sortedViews.splice(item.order ?? Infinity, 0, item)); + return sortedViews + } + /** * Get the initialization status of the Helper class. * @@ -233,7 +281,7 @@ class Helper { * * @type {string[]} */ - const states: string[] = []; + const entities: EntityRegistryEntry[] = []; if (!this.isInitialized()) { console.warn("Helper class should be initialized before calling this method!"); @@ -248,17 +296,39 @@ class Helper { }); // Get the entities of which all conditions of the callback function are met. @see areaFilterCallback. - const newStates = this.#entities.filter( + this.#entities.filter( this.#areaFilterCallback, { area: area, domain: domain, areaDeviceIds: areaDeviceIds, - }) - .map((entity) => `states['${entity.entity_id}']`); + }).forEach(entity => entities.push(entity)) - states.push(...newStates); } + return this.getCountEntityTemplate(entities, operator, value); + } + + static getCountEntityTemplate(entities: EntityRegistryEntry[], operator: string, value: string): string { + // noinspection JSMismatchedCollectionQueryUpdate (False positive per 17-04-2023) + /** + * Array of entity state-entries. + * + * Each element contains a template-string which is used to access home assistant's state machine (state object) in + * a template. + * E.g. "states['light.kitchen']" + * + * @type {string[]} + */ + if (!this.isInitialized()) { + console.warn("Helper class should be initialized before calling this method!"); + } + + // Get the ID of the devices which are linked to the given area. + + // Get the entities of which all conditions of the callback function are met. @see areaFilterCallback. + const states = entities + .map((entity) => `states['${entity.entity_id}']`); + return `{% set entities = [${states}] %} {{ entities | selectattr('state','${operator}','${value}') | list | count }}`; } @@ -369,16 +439,52 @@ class Helper { } /** - * Get the ids of the views which aren't set to hidden in the strategy options. + * Get a target of entity IDs for the given domain. + * + * @param {string} domain - The target domain to retrieve entity IDs from. + * @return {EntityRegistryEntry[]} - A target for a service call. + */ + static entitiesOfDomain(domain: string) { + return Helper.entities.filter( + entity => + entity.entity_id.startsWith(domain + ".") + && !entity.hidden_by + && !Helper.strategyOptions.card_options?.[entity.entity_id]?.hidden + ) + }; + + /** + * Get unique labels of domain. Compare name of label for more user flexibility, because the name can be renamed unlike the id. + * + * @param {string} domain - The target domain of entities. + * @return {LabelRegistryEntry[]} - unique labels. + */ + static labelsOfDomain(domain: string): LabelRegistryEntry[] { + const labels: string[] = this.entitiesOfDomain(domain) + .flatMap(entity => entity.labels) + return [...new Set(labels)] + .map(label => this.getLabelById(label)) + .filter(isDefined) + .filter((label) => label.name.startsWith(this.getLabelPrefix(domain))) + } + + + static getLabelById(labelId: string): LabelRegistryEntry | undefined { + return this.#labels.find(label => label.label_id === labelId) + } + + static getLabelPrefix = (domain: string) => `ms_${domain}_` + + /** + * Get the ids of the views. * * @return {string[]} An array of view ids. */ - static getExposedViewIds(): string[] { + static getViewIds(): string[] { if (!this.isInitialized()) { console.warn("Helper class should be initialized before calling this method!"); } - - return this.#getObjectKeysByPropertyValue(this.#strategyOptions.views, "hidden", false); + return Object.keys(this.#strategyOptions.views).filter(viewId => configurationDefaults.views[viewId]); } /** @@ -429,6 +535,19 @@ class Helper { return (entityUnhidden && domainMatches && entityLinked); } + /** + * Get a target of entity IDs for the given domain.) + * + * @param {EntityRegistryEntry[]} entities - List of target entries. + * @return {HassServiceTarget} - A target for a service call. + */ + static toTargetEntities(entities: EntityRegistryEntry[]): HassServiceTarget { + return { + entity_id: entities + .map(entity => entity.entity_id) + }; + } + /** * Get the keys of nested objects by its property value. * diff --git a/src/cards/ControllerCard.ts b/src/cards/ControllerCard.ts index 273d96f7..f478e981 100644 --- a/src/cards/ControllerCard.ts +++ b/src/cards/ControllerCard.ts @@ -11,12 +11,6 @@ import {HassServiceTarget} from "home-assistant-js-websocket"; * @class */ class ControllerCard { - /** - * @type {HassServiceTarget} The target to control the entities of. - * @private - */ - readonly #target: HassServiceTarget; - /** * Default configuration of the card. * @@ -38,8 +32,7 @@ class ControllerCard { * @param {HassServiceTarget} target The target to control the entities of. * @param {cards.ControllerCardOptions} options Controller Card options. */ - constructor(target: HassServiceTarget, options: cards.ControllerCardOptions = {}) { - this.#target = target; + constructor(private target: HassServiceTarget, options: cards.ControllerCardOptions = {}) { this.#defaultConfig = { ...this.#defaultConfig, ...options, @@ -66,25 +59,25 @@ class ControllerCard { cards: [ { type: "custom:mushroom-template-card", - icon: this.#defaultConfig.iconOff, + icon: this.#defaultConfig.iconOn, layout: "vertical", - icon_color: "red", + icon_color: "amber", tap_action: { action: "call-service", - service: this.#defaultConfig.offService, - target: this.#target, + service: this.#defaultConfig.onService, + target: this.target, data: {}, }, }, { type: "custom:mushroom-template-card", - icon: this.#defaultConfig.iconOn, + icon: this.#defaultConfig.iconOff, layout: "vertical", - icon_color: "amber", + icon_color: "red", tap_action: { action: "call-service", - service: this.#defaultConfig.onService, - target: this.#target, + service: this.#defaultConfig.offService, + target: this.target, data: {}, }, }, diff --git a/src/cards/LawnMowerCard.ts b/src/cards/LawnMowerCard.ts new file mode 100644 index 00000000..484ba0a2 --- /dev/null +++ b/src/cards/LawnMowerCard.ts @@ -0,0 +1,46 @@ +import {AbstractCard} from "./AbstractCard"; +import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; +import {LAWN_MOWER_COMMANDS, LawnMowerCardConfig} from "../types/lovelace-mushroom/cards/lawn-mower-card-config"; + +// noinspection JSUnusedGlobalSymbols Class is dynamically imported. +/** + * LawnMower Card Class + * + * Used to create a card for controlling an entity of the vacuum domain. + * + * @class + * @extends AbstractCard + */ +class LawnMowerCard extends AbstractCard { + /** + * Default configuration of the card. + * + * @type {LawnMowerCardConfig} + * @private + */ + #defaultConfig: LawnMowerCardConfig = { + type: "custom:mushroom-vacuum-card", + icon: undefined, + icon_animation: true, + commands: [...LAWN_MOWER_COMMANDS], + tap_action: { + action: "more-info", + } + }; + + /** + * Class constructor. + * + * @param {EntityRegistryEntry} entity The hass entity to create a card for. + * @param {cards.LawnMowerCardOptions} [options={}] Options for the card. + * @throws {Error} If the Helper module isn't initialized. + */ + constructor(entity: EntityRegistryEntry, options: cards.LawnMowerCardOptions = {}) { + super(entity); + + this.config = Object.assign(this.config, this.#defaultConfig, options); + } +} + +export {LawnMowerCard}; diff --git a/src/configurationDefaults.ts b/src/configurationDefaults.ts index d5366b3d..e232d437 100644 --- a/src/configurationDefaults.ts +++ b/src/configurationDefaults.ts @@ -7,6 +7,7 @@ import StrategyDefaults = generic.StrategyDefaults; export const configurationDefaults: StrategyDefaults = { areas: { undisclosed: { + floor_id: null, aliases: [], area_id: "undisclosed", name: "Undisclosed", @@ -100,42 +101,25 @@ export const configurationDefaults: StrategyDefaults = { showControls: true, hidden: false, }, + lawn_mower: { + title: "Lawn Mower", + showControls: true, + hidden: false, + }, }, home_view: { hidden: [], }, views: { - home: { - order: 1, - hidden: false, - }, - light: { - order: 2, - hidden: false, - }, - fan: { - order: 3, - hidden: false, - }, - cover: { - order: 4, - hidden: false, - }, - switch: { - order: 5, - hidden: false, - }, - climate: { - order: 6, - hidden: false, - }, - camera: { - order: 7, - hidden: false, - }, - vacuum: { - order: 8, - hidden: false, - }, + home: {}, + light: {}, + fan: {}, + cover: {}, + switch: {}, + climate: {}, + camera: {}, + vacuum: {}, + lawn_mower: {}, + binary_sensor: {}, } }; diff --git a/src/mushroom-strategy.ts b/src/mushroom-strategy.ts index 21af6c07..ec3b8ab3 100644 --- a/src/mushroom-strategy.ts +++ b/src/mushroom-strategy.ts @@ -7,6 +7,9 @@ import {StackCardConfig} from "./types/homeassistant/lovelace/cards/types"; import {EntityCardConfig} from "./types/lovelace-mushroom/cards/entity-card-config"; import {HassServiceTarget} from "home-assistant-js-websocket"; import StrategyArea = generic.StrategyArea; +import ViewConfig = generic.ViewConfig; +import {EntityRegistryEntry} from "./types/homeassistant/data/entity_registry"; +import {capitalizeFirstLetter} from "./utils/string"; /** * Mushroom Dashboard Strategy.
@@ -39,20 +42,27 @@ class MushroomStrategy extends HTMLTemplateElement { let viewModule; // Create a view for each exposed domain. - for (let viewId of Helper.getExposedViewIds()) { + let tabViews: ViewConfig[] = []; + for (let viewId of Helper.getViewIds()) { try { const viewType = Helper.sanitizeClassName(viewId + "View"); viewModule = await import(`./views/${viewType}`); - const view: LovelaceViewConfig = await new viewModule[viewType](Helper.strategyOptions.views[viewId]).getView(); + (await new viewModule[viewType]().getView()) + .filter((v: ViewConfig) => !Helper.strategyOptions.views[v.id]?.hidden) + .filter((v: ViewConfig) => v.cards?.length) + .forEach((v: ViewConfig) => tabViews.push(v)); - if (view.cards?.length) { - views.push(view); - } } catch (e) { Helper.logError(`View '${viewId}' couldn't be loaded!`, e); } } + // Add custom views. + if (Helper.strategyOptions.extra_views) { + tabViews.push(...Helper.strategyOptions.extra_views); + } + views.push(...Helper.sortViews(tabViews)); + // Create subviews for each area. for (let area of Helper.areas) { if (!area.hidden) { @@ -70,11 +80,6 @@ class MushroomStrategy extends HTMLTemplateElement { } } - // Add custom views. - if (Helper.strategyOptions.extra_views) { - views.push(...Helper.strategyOptions.extra_views); - } - // Return the created views. return { views: views, @@ -107,11 +112,10 @@ class MushroomStrategy extends HTMLTemplateElement { const className = Helper.sanitizeClassName(domain + "Card"); - let domainCards = []; + let allDomainCards: LovelaceCardConfig[] = []; try { - domainCards = await import(`./cards/${className}`).then(cardModule => { - let domainCards = []; + allDomainCards = await import(`./cards/${className}`).then(cardModule => { const entities = Helper.getDeviceEntities(area, domain); let configEntityHidden = Helper.strategyOptions.domains[domain ?? "_"].hide_config_entities @@ -123,103 +127,119 @@ class MushroomStrategy extends HTMLTemplateElement { entity_id: entities.map(entity => entity.entity_id), } } + const labelPrefix = Helper.getLabelPrefix(domain) + const msLabelsOfDomain = Helper.labelsOfDomain(domain); + const entriesGroupedByLabel = msLabelsOfDomain + .map(label => entities + .filter(entity => entity.labels.includes(label.label_id))) + + const labelLessEntities = entities + .filter(entity => !entity.labels + .some(label => label.startsWith(labelPrefix))); + return [labelLessEntities, ...entriesGroupedByLabel] + .flatMap((groupedEntities: EntityRegistryEntry[], index) => { + let domainCards: LovelaceCardConfig[] = []; + + if (groupedEntities.length) { + // Create a Controller card for the current domain. + const title = msLabelsOfDomain[index - 1]?.name?.replace(labelPrefix, "").replace('_', ' ') ?? Helper.strategyOptions.domains[domain].title; + const titleCard = new ControllerCard( + Helper.toTargetEntities(groupedEntities), + { + ...Helper.strategyOptions.domains[domain], + title: capitalizeFirstLetter(title), + }, + ).createCard(); + + if (domain === "sensor") { + // Create a card for each entity-sensor of the current area. + const sensorStates = Helper.getStateEntities(area, "sensor"); + const sensorCards: EntityCardConfig[] = []; + + for (const sensor of groupedEntities) { + // Find the state of the current sensor. + const sensorState = sensorStates.find(state => state.entity_id === sensor.entity_id); + let cardOptions = Helper.strategyOptions.card_options?.[sensor.entity_id]; + let deviceOptions = Helper.strategyOptions.card_options?.[sensor.device_id ?? "null"]; + + if (!cardOptions?.hidden && !deviceOptions?.hidden) { + if (sensorState?.attributes.unit_of_measurement) { + cardOptions = { + ...{ + type: "custom:mini-graph-card", + entities: [sensor.entity_id], + }, + ...cardOptions, + }; + } + + sensorCards.push(new SensorCard(sensor, cardOptions).getCard()); + } + } - if (entities.length) { - // Create a Controller card for the current domain. - const titleCard = new ControllerCard( - target, - Helper.strategyOptions.domains[domain], - ).createCard(); - - if (domain === "sensor") { - // Create a card for each entity-sensor of the current area. - const sensorStates = Helper.getStateEntities(area, "sensor"); - const sensorCards: EntityCardConfig[] = []; - - for (const sensor of entities) { - // Find the state of the current sensor. - const sensorState = sensorStates.find(state => state.entity_id === sensor.entity_id); - let cardOptions = Helper.strategyOptions.card_options?.[sensor.entity_id]; - let deviceOptions = Helper.strategyOptions.card_options?.[sensor.device_id ?? "null"]; - - if (!cardOptions?.hidden && !deviceOptions?.hidden) { - if (sensorState?.attributes.unit_of_measurement) { - cardOptions = { - ...{ - type: "custom:mini-graph-card", - entities: [sensor.entity_id], - }, - ...cardOptions, - }; + if (sensorCards.length) { + domainCards.push({ + type: "vertical-stack", + cards: sensorCards, + }); + + domainCards.unshift(titleCard); } - sensorCards.push(new SensorCard(sensor, cardOptions).getCard()); + return domainCards; } - } - if (sensorCards.length) { - domainCards.push({ - type: "vertical-stack", - cards: sensorCards, - }); + // Create a card for each other domain-entity of the current area. + for (const entity of groupedEntities) { + let deviceOptions; + let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id]; - domainCards.unshift(titleCard); - } - - return domainCards; - } + if (entity.device_id) { + deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id]; + } - // Create a card for each other domain-entity of the current area. - for (const entity of entities) { - let deviceOptions; - let cardOptions = Helper.strategyOptions.card_options?.[entity.entity_id]; + // Don't include the entity if hidden in the strategy options. + if (cardOptions?.hidden || deviceOptions?.hidden) { + continue; + } - if (entity.device_id) { - deviceOptions = Helper.strategyOptions.card_options?.[entity.device_id]; - } + // Don't include the config-entity if hidden in the strategy options. + if (entity.entity_category === "config" && configEntityHidden) { + continue; + } - // Don't include the entity if hidden in the strategy options. - if (cardOptions?.hidden || deviceOptions?.hidden) { - continue; - } + domainCards.push(new cardModule[className](entity, cardOptions).getCard()); + } - // Don't include the config-entity if hidden in the strategy options. - if (entity.entity_category === "config" && configEntityHidden) { - continue; - } + if (domain === "binary_sensor") { + // Horizontally group every two binary sensor cards. + const horizontalCards = []; - domainCards.push(new cardModule[className](entity, cardOptions).getCard()); - } + for (let i = 0; i < domainCards.length; i += 2) { + horizontalCards.push({ + type: "horizontal-stack", + cards: domainCards.slice(i, i + 2), + }); + } - if (domain === "binary_sensor") { - // Horizontally group every two binary sensor cards. - const horizontalCards = []; + domainCards = horizontalCards; + } - for (let i = 0; i < domainCards.length; i += 2) { - horizontalCards.push({ - type: "horizontal-stack", - cards: domainCards.slice(i, i + 2), - }); + if (domainCards.length) { + domainCards.unshift(titleCard); + } } - - domainCards = horizontalCards; - } - - if (domainCards.length) { - domainCards.unshift(titleCard); - } - } - - return domainCards; + return domainCards; + }) }); } catch (e) { Helper.logError("An error occurred while creating the domain cards!", e); } - if (domainCards.length) { + if (allDomainCards.length) { viewCards.push({ type: "vertical-stack", - cards: domainCards, + cards: allDomainCards, }); } } @@ -292,7 +312,7 @@ class MushroomStrategy extends HTMLTemplateElement { customElements.define("ll-strategy-mushroom-strategy", MushroomStrategy); -const version = "v2.1.0"; +const version = "v2.1.1"; console.info( "%c Mushroom Strategy %c ".concat(version, " "), "color: white; background: coral; font-weight: 700;", "color: coral; background: white; font-weight: 700;" diff --git a/src/types/homeassistant/data/area_registry.ts b/src/types/homeassistant/data/area_registry.ts index 2e5d3c1b..4aaaf7e2 100644 --- a/src/types/homeassistant/data/area_registry.ts +++ b/src/types/homeassistant/data/area_registry.ts @@ -2,12 +2,14 @@ * Area Entity. * * @property {string} area_id The id of the area. + * @property {string} floor_id The id of the area. * @property {string} name Name of the area. * @property {string|null} picture URL to a picture that should be used instead of showing the domain icon. * @property {string[]} aliases Array of aliases of the area. */ export interface AreaRegistryEntry { area_id: string; + floor_id: string | null; name: string; picture: string | null; aliases: string[]; diff --git a/src/types/homeassistant/data/entity_registry.ts b/src/types/homeassistant/data/entity_registry.ts index f538a27d..2350f267 100644 --- a/src/types/homeassistant/data/entity_registry.ts +++ b/src/types/homeassistant/data/entity_registry.ts @@ -33,6 +33,7 @@ export interface EntityRegistryDisplayEntry { * @property {string} unique_id * @property {string} [translation_key] * @property {EntityRegistryOptions | null} options + * @property {string[]} labels An array of label_id's */ export interface EntityRegistryEntry { id: string; @@ -51,6 +52,7 @@ export interface EntityRegistryEntry { unique_id: string; translation_key?: string; options: EntityRegistryOptions | null; + labels: string[]; } export interface SensorEntityOptions { diff --git a/src/types/homeassistant/data/floor_registry.ts b/src/types/homeassistant/data/floor_registry.ts new file mode 100644 index 00000000..e1659201 --- /dev/null +++ b/src/types/homeassistant/data/floor_registry.ts @@ -0,0 +1,12 @@ +/** + * Floor Entity. + * + * @property {string} floor_id The id of the floor. + * @property {number} level The label_id of the floor. + * @property {string} name - The name of the label. + * */ +export interface FloorRegistryEntry { + floor_id: string, + level: number, + name: string +} diff --git a/src/types/homeassistant/data/label_registry.ts b/src/types/homeassistant/data/label_registry.ts new file mode 100644 index 00000000..00edf045 --- /dev/null +++ b/src/types/homeassistant/data/label_registry.ts @@ -0,0 +1,16 @@ +/** + * Label Entity. + * + * @property {string | null} color - The color name of the label. + * @property {string | null} description - The description of the label. + * @property {string | null} icon - The icon of the label. + * @property {string} label_id - The ID of the label. Id is given at the creation of the label and can not be changed later. + * @property {string} name - The name of the label. + */ +export interface LabelRegistryEntry { + color: string | null, + description: string | null, + icon: string | null, + label_id: string, + name: string +} diff --git a/src/types/lovelace-mushroom/cards/lawn-mower-card-config.ts b/src/types/lovelace-mushroom/cards/lawn-mower-card-config.ts new file mode 100644 index 00000000..f1d714ee --- /dev/null +++ b/src/types/lovelace-mushroom/cards/lawn-mower-card-config.ts @@ -0,0 +1,28 @@ +import {ActionsSharedConfig} from "../shared/config/actions-config"; +import {LovelaceCardConfig} from "../../homeassistant/data/lovelace"; +import {EntitySharedConfig} from "../shared/config/entity-config"; +import {AppearanceSharedConfig} from "../shared/config/appearance-config"; + +export const LAWN_MOWER_COMMANDS = [ + "on_off", + "start_pause", + "return_home", +] as const; + +export type LawnMowerCommand = (typeof LAWN_MOWER_COMMANDS)[number]; + +/** + * Vacuum Card Config. + * + * @param {boolean} icon_animation Animate the icon when vacuum is cleaning. + * @param {LawnMowerCommand[]} commands List of commands to display (start_pause, return_home). + * + * @see https://github.com/piitaya/lovelace-mushroom/blob/main/docs/cards/vacuum.md + */ +export type LawnMowerCardConfig = LovelaceCardConfig & + EntitySharedConfig & + AppearanceSharedConfig & + ActionsSharedConfig & { + icon_animation?: boolean; + commands?: LawnMowerCommand[]; +}; diff --git a/src/types/strategy/cards.ts b/src/types/strategy/cards.ts index b8582a2c..0eae52c2 100644 --- a/src/types/strategy/cards.ts +++ b/src/types/strategy/cards.ts @@ -16,6 +16,7 @@ import {MediaPlayerCardConfig} from "../lovelace-mushroom/cards/media-player-car import {NumberCardConfig} from "../lovelace-mushroom/cards/number-card-config"; import {PersonCardConfig} from "../lovelace-mushroom/cards/person-card-config"; import {VacuumCardConfig} from "../lovelace-mushroom/cards/vacuum-card-config"; +import {LawnMowerCardConfig} from "../lovelace-mushroom/cards/lawn-mower-card-config"; export namespace cards { /** @@ -58,6 +59,7 @@ export namespace cards { export type PictureEntityCardOptions = Omit; export type TemplateCardOptions = Omit; export type VacuumCardOptions = Omit; + export type LawnMowerCardOptions = Omit; } diff --git a/src/types/strategy/generic.ts b/src/types/strategy/generic.ts index 821d5fcc..c3dad771 100644 --- a/src/types/strategy/generic.ts +++ b/src/types/strategy/generic.ts @@ -27,6 +27,7 @@ export namespace generic { * @property {boolean} [hidden] True if the entity should be hidden from the dashboard. */ export interface ViewConfig extends LovelaceViewConfig { + id: string; hidden?: boolean; order?: number; } @@ -110,7 +111,7 @@ export namespace generic { hidden: HiddenSectionType[] } quick_access_cards?: LovelaceCardConfig[]; - views: { [k: string]: ViewConfig }; + views: { [k: string]: Partial }; } const hiddenSectionList = ["chips", "persons", "greeting", "areas", "areasTitle"] as const; diff --git a/src/types/strategy/views.ts b/src/types/strategy/views.ts index 99190a4c..2ad9e6a2 100644 --- a/src/types/strategy/views.ts +++ b/src/types/strategy/views.ts @@ -8,6 +8,7 @@ export namespace views { * @property {cards.ControllerCardConfig} [controllerCardOptions] Options for the Controller card. */ export interface ViewConfig extends LovelaceViewConfig { + id: string; controllerCardOptions?: cards.ControllerCardOptions; } } diff --git a/src/utils/object.ts b/src/utils/object.ts new file mode 100644 index 00000000..2f02d6e3 --- /dev/null +++ b/src/utils/object.ts @@ -0,0 +1,3 @@ +export function isDefined(obj: T | undefined): obj is T { + return obj !== undefined +} diff --git a/src/utils/string.ts b/src/utils/string.ts new file mode 100644 index 00000000..bc7e2b92 --- /dev/null +++ b/src/utils/string.ts @@ -0,0 +1,3 @@ +export function capitalizeFirstLetter(string: string) { + return string[0].toUpperCase() + string.slice(1); +} diff --git a/src/views/AbstractView.ts b/src/views/AbstractView.ts index 497cdaa9..8c4e4e7f 100644 --- a/src/views/AbstractView.ts +++ b/src/views/AbstractView.ts @@ -1,11 +1,15 @@ import {Helper} from "../Helper"; import {ControllerCard} from "../cards/ControllerCard"; import {StackCardConfig} from "../types/homeassistant/lovelace/cards/types"; -import {LovelaceCardConfig, LovelaceViewConfig} from "../types/homeassistant/data/lovelace"; +import {LovelaceViewConfig} from "../types/homeassistant/data/lovelace"; import {cards} from "../types/strategy/cards"; import {TitleCardConfig} from "../types/lovelace-mushroom/cards/title-card-config"; import {HassServiceTarget} from "home-assistant-js-websocket"; import abstractCardConfig = cards.AbstractCardConfig; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; +import {generic} from "../types/strategy/generic"; +import ViewConfig = generic.ViewConfig; +import {views} from "../types/strategy/views"; /** * Abstract View Class. @@ -16,25 +20,6 @@ import abstractCardConfig = cards.AbstractCardConfig; * @abstract */ abstract class AbstractView { - /** - * Configuration of the view. - * - * @type {LovelaceViewConfig} - */ - config: LovelaceViewConfig = { - icon: "mdi:view-dashboard", - subview: false, - }; - - /** - * A card to switch all entities in the view. - * - * @type {StackCardConfig} - */ - viewControllerCard: StackCardConfig = { - cards: [], - type: "", - }; /** * The domain of which we operate the devices. @@ -42,7 +27,11 @@ abstract class AbstractView { * @private * @readonly */ - readonly #domain?: string; + protected readonly prefix: string; + + protected abstract defaultConfig: views.ViewConfig + + protected abstract viewControllerCardConfig(entities: EntityRegistryEntry[], groupName?: string): cards.ControllerCardOptions; /** * Class constructor. @@ -52,45 +41,48 @@ abstract class AbstractView { * @throws {Error} If trying to instantiate this class. * @throws {Error} If the Helper module isn't initialized. */ - protected constructor(domain: string = "") { + protected constructor(protected readonly domain: string = '') { if (!Helper.isInitialized()) { throw new Error("The Helper module must be initialized before using this one."); } - - if (domain) { - this.#domain = domain; - } + this.domain = domain; + this.prefix = this.domain ? Helper.getLabelPrefix(this.domain) : '' } + createCard(entities: EntityRegistryEntry[], groupName?: string): StackCardConfig { + return new ControllerCard( + Helper.toTargetEntities(entities), + { + ...this.viewControllerCardConfig(entities, groupName), + ...("controllerCardOptions" in this.defaultConfig ? this.defaultConfig.controllerCardOptions : {}) as cards.ControllerCardConfig, + }).createCard(); + }; + /** * Create the cards to include in the view. * * @return {Promise<(StackCardConfig | TitleCardConfig)[]>} An array of card objects. */ - async createViewCards(): Promise<(StackCardConfig | TitleCardConfig)[]> { - const viewCards: LovelaceCardConfig[] = []; + async createViewCards(labelFilter: (entity: EntityRegistryEntry) => boolean, groupName?: string): Promise<(StackCardConfig | TitleCardConfig)[]> { + const viewCards: StackCardConfig[] = []; const configEntityHidden = - Helper.strategyOptions.domains[this.#domain ?? "_"].hide_config_entities - || Helper.strategyOptions.domains["_"].hide_config_entities; + Helper.strategyOptions.domains[this.domain ?? "_"].hide_config_entities + || Helper.strategyOptions.domains["_"].hide_config_entities; // Create cards for each area. for (const area of Helper.areas) { const areaCards: abstractCardConfig[] = []; - const entities = Helper.getDeviceEntities(area, this.#domain ?? ""); - const className = Helper.sanitizeClassName(this.#domain + "Card"); + const entities = Helper.getDeviceEntities(area, this.domain).filter(labelFilter); + const className = Helper.sanitizeClassName(this.domain + "Card"); const cardModule = await import(`../cards/${className}`); // Set the target for controller cards to the current area. - let target: HassServiceTarget = { - area_id: [area.area_id], - }; + const target: HassServiceTarget = Helper.toTargetEntities(entities); // Set the target for controller cards to entities without an area. - if (area.area_id === "undisclosed") { - target = { - entity_id: entities.map(entity => entity.entity_id), - } - } + /*if (area.area_id === "undisclosed") { + target = Helper.toTargetEntities(entities); + }*/ // Create a card for each domain-entity of the current area. for (const entity of entities) { @@ -110,7 +102,7 @@ abstract class AbstractView { // Vertical stack the area cards if it has entities. if (areaCards.length) { - const titleCardOptions = ("controllerCardOptions" in this.config) ? this.config.controllerCardOptions : {}; + const titleCardOptions = ("controllerCardOptions" in this.defaultConfig) ? this.defaultConfig.controllerCardOptions : {}; // Create and insert a Controller card. areaCards.unshift(new ControllerCard(target, Object.assign({title: area.name}, titleCardOptions)).createCard()); @@ -124,41 +116,47 @@ abstract class AbstractView { // Add a Controller Card for all the entities in the view. if (viewCards.length) { - viewCards.unshift(this.viewControllerCard); + const entities = Helper.entitiesOfDomain(this.domain).filter(labelFilter); + viewCards.unshift(this.createCard(entities, groupName)); } return viewCards; } /** - * Get a view object. + * Get a view array of domain. * * The view includes the cards which are created by method createViewCards(). * - * @returns {Promise} The view object. + * @returns {Promise} The view arrays of domain. */ - async getView(): Promise { - return { - ...this.config, - cards: await this.createViewCards(), - }; - } + async getView(): Promise<(ViewConfig)[]> { + const msLabelsOfDomain = this.domain ? Helper.labelsOfDomain(this.domain) : []; + const views = (await Promise.all(msLabelsOfDomain + .map(async label => await this.createViewCards(entity => entity.labels.includes(label.label_id), label.name.replace(this.prefix, '').replace('_', ' '),)))) + .map((cards, index) => { + const label = msLabelsOfDomain[index]; + const title = label.name.replace(this.prefix, ""); + return { + title, + ...this.defaultConfig, + icon: Helper.getLabelById(label.label_id)?.icon || this.defaultConfig.icon, + path: title.replace(this.prefix, "").toLowerCase(), + ...Helper.strategyOptions.views[label.name], + id: label.name, // as long as the label id can not be seen in the UI use the name as id. this id need to map to the view id in the lovelace config + cards, + } + }); - /** - * Get a target of entity IDs for the given domain. - * - * @param {string} domain - The target domain to retrieve entity IDs from. - * @return {HassServiceTarget} - A target for a service call. - */ - targetDomain(domain: string): HassServiceTarget { - return { - entity_id: Helper.entities.filter( - entity => - entity.entity_id.startsWith(domain + ".") - && !entity.hidden_by - && !Helper.strategyOptions.card_options?.[entity.entity_id]?.hidden - ).map(entity => entity.entity_id), - }; + const mainView = ({ + ...this.defaultConfig, + ...Helper.strategyOptions.views[this.domain ?? this.defaultConfig.title?.toLowerCase()], + id: this.defaultConfig.id, // prevent to override by strategyOptions + cards: await this.createViewCards(entity => !this.prefix || !entity.labels.some(label => label.startsWith(this.prefix))), + }); + + + return [mainView, ...views]; } } diff --git a/src/views/BinarySensorView.ts b/src/views/BinarySensorView.ts new file mode 100644 index 00000000..6ec0a007 --- /dev/null +++ b/src/views/BinarySensorView.ts @@ -0,0 +1,63 @@ +import {AbstractView} from "./AbstractView"; +import {views} from "../types/strategy/views"; +import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; + +// noinspection JSUnusedGlobalSymbols Class is dynamically imported. +/** + * Cover View Class. + * + * Used to create a view for entities of the cover domain. + * + * @class CoverView + * @extends AbstractView + */ +class BinarySensorView extends AbstractView { + /** + * Domain of the view's entities. + * + * @type {string} + * @static + * @private + */ + static #domain: string = "binary_sensor"; + + /** + * Default configuration of the view. + * + * @type {views.ViewConfig} + * @protected + */ + defaultConfig: views.ViewConfig = { + id: BinarySensorView.#domain, + title: "Binary Sensors", + path: "binary-sensors", + icon: "mdi:numeric-10", + subview: false, + controllerCardOptions: { + showControls: false, + }, + }; + + /** + * Get default configuration of the view's Controller card. + * + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} + */ + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = "binary sensors"): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + /*subtitle: Helper.getCountEntityTemplate(entities, "eq", "open") + ` ${groupName} open`,*/ + }); + + /** + * Class constructor. + */ + constructor() { + super(BinarySensorView.#domain); + } +} + +export {BinarySensorView}; diff --git a/src/views/CameraView.ts b/src/views/CameraView.ts index 949052fa..53d73a52 100644 --- a/src/views/CameraView.ts +++ b/src/views/CameraView.ts @@ -1,8 +1,8 @@ -import {ControllerCard} from "../cards/ControllerCard"; import {AbstractView} from "./AbstractView"; import {views} from "../types/strategy/views"; import {cards} from "../types/strategy/cards"; import {Helper} from "../Helper"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. /** @@ -27,9 +27,10 @@ class CameraView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig: views.ViewConfig = { + id: CameraView.#domain, title: "Cameras", path: "cameras", icon: "mdi:cctv", @@ -43,30 +44,18 @@ class CameraView extends AbstractView { * Default configuration of the view's Controller card. * * @type {cards.ControllerCardOptions} - * @private + * @protected */ - #viewControllerCardConfig: cards.ControllerCardOptions = { - title: "All Cameras", - subtitle: Helper.getCountTemplate(CameraView.#domain, "ne", "off") + " cameras on", - }; + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = 'cameras'): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "ne", "off") + ` ${groupName} on`, + }); /** * Class constructor. - * - * @param {views.ViewConfig} [options={}] Options for the view. */ - constructor(options: views.ViewConfig = {}) { + constructor() { super(CameraView.#domain); - - this.config = Object.assign(this.config, this.#defaultConfig, options); - - // Create a Controller card to switch all entities of the domain. - this.viewControllerCard = new ControllerCard( - {}, - { - ...this.#viewControllerCardConfig, - ...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig, - }).createCard(); } } diff --git a/src/views/ClimateView.ts b/src/views/ClimateView.ts index 1e0a476f..eaf4cf9b 100644 --- a/src/views/ClimateView.ts +++ b/src/views/ClimateView.ts @@ -1,8 +1,8 @@ import {Helper} from "../Helper"; -import {ControllerCard} from "../cards/ControllerCard"; import {AbstractView} from "./AbstractView"; import {views} from "../types/strategy/views"; import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. /** @@ -27,9 +27,10 @@ class ClimateView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig = { + id: ClimateView.#domain, title: "Climates", path: "climates", icon: "mdi:thermostat", @@ -40,33 +41,23 @@ class ClimateView extends AbstractView { }; /** - * Default configuration of the view's Controller card. + * Get default configuration of the view's Controller card. * - * @type {cards.ControllerCardOptions} - * @private + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} */ - #viewControllerCardConfig: cards.ControllerCardOptions = { - title: "All Climates", - subtitle: Helper.getCountTemplate(ClimateView.#domain, "ne", "off") + " climates on", - }; + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = 'climates'): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "ne", "off") + ` ${groupName} on`, + }); /** * Class constructor. - * - * @param {views.ViewConfig} [options={}] Options for the view. */ - constructor(options: views.ViewConfig = {}) { + constructor() { super(ClimateView.#domain); - - this.config = Object.assign(this.config, this.#defaultConfig, options); - - // Create a Controller card to switch all entities of the domain. - this.viewControllerCard = new ControllerCard( - this.targetDomain(ClimateView.#domain), - { - ...this.#viewControllerCardConfig, - ...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig, - }).createCard(); } } diff --git a/src/views/CoverView.ts b/src/views/CoverView.ts index 23c1f1aa..3825ea13 100644 --- a/src/views/CoverView.ts +++ b/src/views/CoverView.ts @@ -1,8 +1,8 @@ import {Helper} from "../Helper"; -import {ControllerCard} from "../cards/ControllerCard"; import {AbstractView} from "./AbstractView"; import {views} from "../types/strategy/views"; import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. /** @@ -27,9 +27,10 @@ class CoverView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig: views.ViewConfig = { + id: CoverView.#domain, title: "Covers", path: "covers", icon: "mdi:window-open", @@ -43,33 +44,23 @@ class CoverView extends AbstractView { }; /** - * Default configuration of the view's Controller card. + * Get default configuration of the view's Controller card. * - * @type {cards.ControllerCardOptions} - * @private + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} */ - #viewControllerCardConfig: cards.ControllerCardOptions = { - title: "All Covers", - subtitle: Helper.getCountTemplate(CoverView.#domain, "eq", "open") + " covers open", - }; + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = "covers"): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "eq", "open") + ` ${groupName} open`, + }); /** * Class constructor. - * - * @param {views.ViewConfig} [options={}] Options for the view. */ - constructor(options: views.ViewConfig = {}) { + constructor() { super(CoverView.#domain); - - this.config = Object.assign(this.config, this.#defaultConfig, options); - - // Create a Controller card to switch all entities of the domain. - this.viewControllerCard = new ControllerCard( - this.targetDomain(CoverView.#domain), - { - ...this.#viewControllerCardConfig, - ...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig, - }).createCard(); } } diff --git a/src/views/FanView.ts b/src/views/FanView.ts index e1699b09..5a3fb9a4 100644 --- a/src/views/FanView.ts +++ b/src/views/FanView.ts @@ -1,8 +1,8 @@ import {Helper} from "../Helper"; -import {ControllerCard} from "../cards/ControllerCard"; import {AbstractView} from "./AbstractView"; import {views} from "../types/strategy/views"; import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. /** @@ -27,9 +27,10 @@ class FanView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig: views.ViewConfig = { + id: FanView.#domain, title: "Fans", path: "fans", icon: "mdi:fan", @@ -43,33 +44,23 @@ class FanView extends AbstractView { }; /** - * Default configuration of the view's Controller card. + * Get default configuration of the view's Controller card. * - * @type {cards.ControllerCardOptions} - * @private + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} */ - #viewControllerCardConfig: cards.ControllerCardOptions = { - title: "All Fans", - subtitle: Helper.getCountTemplate(FanView.#domain, "eq", "on") + " fans on", - }; + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = 'fans'): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "eq", "on") + ` ${groupName} on`, + }); /** * Class constructor. - * - * @param {views.ViewConfig} [options={}] Options for the view. */ - constructor(options: views.ViewConfig = {}) { + constructor() { super(FanView.#domain); - - this.config = Object.assign(this.config, this.#defaultConfig, options); - - // Create a Controller card to switch all entities of the domain. - this.viewControllerCard = new ControllerCard( - this.targetDomain(FanView.#domain), - { - ...this.#viewControllerCardConfig, - ...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig, - }).createCard(); } } diff --git a/src/views/HomeView.ts b/src/views/HomeView.ts index 54b53f9a..89fe8623 100644 --- a/src/views/HomeView.ts +++ b/src/views/HomeView.ts @@ -8,6 +8,7 @@ import {TemplateCardConfig} from "../types/lovelace-mushroom/cards/template-card import {ActionConfig} from "../types/homeassistant/data/lovelace"; import {TitleCardConfig} from "../types/lovelace-mushroom/cards/title-card-config"; import {PersonCardConfig} from "../types/lovelace-mushroom/cards/person-card-config"; +import {cards} from "../types/strategy/cards"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. @@ -24,9 +25,10 @@ class HomeView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig= { + id: "home", title: "Home", icon: "mdi:home-assistant", path: "home", @@ -34,14 +36,17 @@ class HomeView extends AbstractView { }; /** - * Class constructor. + * Get default configuration of the view's Controller card. * - * @param {views.ViewConfig} [options={}] Options for the view. + * @return {cards.ControllerCardOptions} */ - constructor(options: views.ViewConfig = {}) { - super(); + viewControllerCardConfig = (): cards.ControllerCardOptions => ({}); - this.config = Object.assign(this.config, this.#defaultConfig, options); + /** + * Class constructor. + */ + constructor() { + super(); } /** @@ -57,7 +62,7 @@ class HomeView extends AbstractView { this.#createAreaSection(), ]).then(([chips, personCards, areaCards]) => { const options = Helper.strategyOptions; - const homeViewCards = []; + const homeViewCards: (StackCardConfig | TemplateCardConfig | ChipsCardConfig)[] = []; if (chips.length) { // TODO: Create the Chip card at this.#createChips() diff --git a/src/views/LawnMowerView.ts b/src/views/LawnMowerView.ts new file mode 100644 index 00000000..da9d9c62 --- /dev/null +++ b/src/views/LawnMowerView.ts @@ -0,0 +1,67 @@ +import {Helper} from "../Helper"; +import {AbstractView} from "./AbstractView"; +import {views} from "../types/strategy/views"; +import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; + +// noinspection JSUnusedGlobalSymbols Class is dynamically imported. +/** + * Lawn Mower View Class. + * + * Used to create a view for entities of the lawn_mower domain. + * + * @class LawnMowerView + * @extends AbstractView + */ +class LawnMowerView extends AbstractView { + /** + * Domain of the view's entities. + * + * @type {string} + * @static + * @private + */ + static #domain: string = "lawn_mower"; + + /** + * Default configuration of the view. + * + * @type {views.ViewConfig} + * @protected + */ + defaultConfig: views.ViewConfig = { + id: LawnMowerView.#domain, + title: "Lawn Mowers", + path: "lawn-mowers", + icon: "mdi:robot-mower", + subview: false, + controllerCardOptions: { + iconOn: "mdi:robot-mower", + iconOff: "mdi:robot-mower-outline", + onService: "lawn_mower.start_mowing", + offService: "lawn_mower.dock", + }, + }; + + /** + * Get default configuration of the view's Controller card. + * + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} + */ + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = 'lawn mowers'): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "ne", "off") + ` ${groupName} on`, + }); + + /** + * Class constructor. + */ + constructor() { + super(LawnMowerView.#domain); + } +} + +export {LawnMowerView}; diff --git a/src/views/LightView.ts b/src/views/LightView.ts index 75a2b153..36ea814a 100644 --- a/src/views/LightView.ts +++ b/src/views/LightView.ts @@ -1,8 +1,8 @@ import {Helper} from "../Helper"; -import {ControllerCard} from "../cards/ControllerCard"; import {AbstractView} from "./AbstractView"; import {views} from "../types/strategy/views"; import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. /** @@ -27,9 +27,10 @@ class LightView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig: views.ViewConfig = { + id: LightView.#domain, title: "Lights", path: "lights", icon: "mdi:lightbulb-group", @@ -43,33 +44,23 @@ class LightView extends AbstractView { }; /** - * Default configuration of the view's Controller card. + * Get default configuration of the view's Controller card. * - * @type {cards.ControllerCardOptions} - * @private + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} */ - #viewControllerCardConfig: cards.ControllerCardOptions = { - title: "All Lights", - subtitle: Helper.getCountTemplate(LightView.#domain, "eq", "on") + " lights on", - }; + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = 'lights'): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "eq", "on") + ` ${groupName} on`, + }); /** * Class constructor. - * - * @param {views.ViewConfig} [options={}] Options for the view. */ - constructor(options: views.ViewConfig = {}) { + constructor() { super(LightView.#domain); - - this.config = Object.assign(this.config, this.#defaultConfig, options); - - // Create a Controller card to switch all entities of the domain. - this.viewControllerCard = new ControllerCard( - this.targetDomain(LightView.#domain), - { - ...this.#viewControllerCardConfig, - ...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig, - }).createCard(); } } diff --git a/src/views/SwitchView.ts b/src/views/SwitchView.ts index 8244dab0..07993f51 100644 --- a/src/views/SwitchView.ts +++ b/src/views/SwitchView.ts @@ -1,8 +1,8 @@ import {Helper} from "../Helper"; -import {ControllerCard} from "../cards/ControllerCard"; import {AbstractView} from "./AbstractView"; import {views} from "../types/strategy/views"; import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. /** @@ -27,9 +27,10 @@ class SwitchView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig: views.ViewConfig = { + id: SwitchView.#domain, title: "Switches", path: "switches", icon: "mdi:dip-switch", @@ -43,33 +44,23 @@ class SwitchView extends AbstractView { }; /** - * Default configuration of the view's Controller card. + * Get default configuration of the view's Controller card. * - * @type {cards.ControllerCardOptions} - * @private + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} */ - #viewControllerCardConfig: cards.ControllerCardOptions = { - title: "All Switches", - subtitle: Helper.getCountTemplate(SwitchView.#domain, "eq", "on") + " switches on", - }; + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = 'switches'): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "eq", "on") + ` ${groupName} on`, + }); /** * Class constructor. - * - * @param {views.ViewConfig} [options={}] Options for the view. */ - constructor(options: views.ViewConfig = {}) { + constructor() { super(SwitchView.#domain); - - this.config = Object.assign(this.config, this.#defaultConfig, options); - - // Create a Controller card to switch all entities of the domain. - this.viewControllerCard = new ControllerCard( - this.targetDomain(SwitchView.#domain), - { - ...this.#viewControllerCardConfig, - ...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig, - }).createCard(); } } diff --git a/src/views/VacuumView.ts b/src/views/VacuumView.ts index 55b4ae1c..1735fb13 100644 --- a/src/views/VacuumView.ts +++ b/src/views/VacuumView.ts @@ -1,8 +1,8 @@ import {Helper} from "../Helper"; -import {ControllerCard} from "../cards/ControllerCard"; import {AbstractView} from "./AbstractView"; import {views} from "../types/strategy/views"; import {cards} from "../types/strategy/cards"; +import {EntityRegistryEntry} from "../types/homeassistant/data/entity_registry"; // noinspection JSUnusedGlobalSymbols Class is dynamically imported. /** @@ -27,9 +27,10 @@ class VacuumView extends AbstractView { * Default configuration of the view. * * @type {views.ViewConfig} - * @private + * @protected */ - #defaultConfig: views.ViewConfig = { + defaultConfig: views.ViewConfig = { + id: VacuumView.#domain, title: "Vacuums", path: "vacuums", icon: "mdi:robot-vacuum", @@ -43,33 +44,23 @@ class VacuumView extends AbstractView { }; /** - * Default configuration of the view's Controller card. + * Get default configuration of the view's Controller card. * - * @type {cards.ControllerCardOptions} - * @private + * @param {EntityRegistryEntry[]} [entities] relevant entities for this card + * @param {string?} groupName can be used to define alternative domain name for card + * + * @return {cards.ControllerCardOptions} */ - #viewControllerCardConfig: cards.ControllerCardOptions = { - title: "All Vacuums", - subtitle: Helper.getCountTemplate(VacuumView.#domain, "ne", "off") + " vacuums on", - }; + viewControllerCardConfig = (entities: EntityRegistryEntry[], groupName: string = 'vacuums'): cards.ControllerCardOptions => ({ + title: `All ${groupName}`, + subtitle: Helper.getCountEntityTemplate(entities, "ne", "off") + ` ${groupName} on`, + }); /** * Class constructor. - * - * @param {views.ViewConfig} [options={}] Options for the view. */ - constructor(options: views.ViewConfig = {}) { + constructor() { super(VacuumView.#domain); - - this.config = Object.assign(this.config, this.#defaultConfig, options); - - // Create a Controller card to switch all entities of the domain. - this.viewControllerCard = new ControllerCard( - this.targetDomain(VacuumView.#domain), - { - ...this.#viewControllerCardConfig, - ...("controllerCardOptions" in this.config ? this.config.controllerCardOptions : {}) as cards.ControllerCardConfig, - }).createCard(); } }