mirror of
https://github.com/DigiLive/mushroom-strategy.git
synced 2025-09-26 13:10:54 +02:00
Add UI editor
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/node_modules/
|
/node_modules/
|
||||||
# Don't add directory /dist/ to .gitignore, as it is used by the build script
|
# Don't add directory /dist/ to .gitignore, as it is used by the build script
|
||||||
|
dist/mushroom-strategy.js
|
||||||
|
57
package-lock.json
generated
57
package-lock.json
generated
@@ -6,10 +6,11 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "mushroom-strategy",
|
"name": "mushroom-strategy",
|
||||||
"version": "2.3.4",
|
"version": "2.3.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deepmerge": "^4"
|
"deepmerge": "^4",
|
||||||
|
"lit": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.7.0",
|
||||||
@@ -408,6 +409,21 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@lit-labs/ssr-dom-shim": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.4.0.tgz",
|
||||||
|
"integrity": "sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
|
"node_modules/@lit/reactive-element": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit-labs/ssr-dom-shim": "^1.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@nodelib/fs.scandir": {
|
"node_modules/@nodelib/fs.scandir": {
|
||||||
"version": "2.1.5",
|
"version": "2.1.5",
|
||||||
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
|
||||||
@@ -569,6 +585,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/trusted-types": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/unist": {
|
"node_modules/@types/unist": {
|
||||||
"version": "2.0.11",
|
"version": "2.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
|
||||||
@@ -2755,6 +2777,37 @@
|
|||||||
"uc.micro": "^2.0.0"
|
"uc.micro": "^2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/lit": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit/reactive-element": "^2.1.0",
|
||||||
|
"lit-element": "^4.2.0",
|
||||||
|
"lit-html": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-element": {
|
||||||
|
"version": "4.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.1.tgz",
|
||||||
|
"integrity": "sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@lit-labs/ssr-dom-shim": "^1.4.0",
|
||||||
|
"@lit/reactive-element": "^2.1.0",
|
||||||
|
"lit-html": "^3.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/lit-html": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==",
|
||||||
|
"license": "BSD-3-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/trusted-types": "^2.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/loader-runner": {
|
"node_modules/loader-runner": {
|
||||||
"version": "4.3.0",
|
"version": "4.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||||
|
@@ -27,7 +27,8 @@
|
|||||||
"url": "https://github.com/DigiLive/mushroom-strategy"
|
"url": "https://github.com/DigiLive/mushroom-strategy"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"deepmerge": "^4"
|
"deepmerge": "^4",
|
||||||
|
"lit": "^3.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/semver": "^7.7.0",
|
"@types/semver": "^7.7.0",
|
||||||
|
428
src/editor/hui-mushroom-strategy-editor.ts
Normal file
428
src/editor/hui-mushroom-strategy-editor.ts
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
import { LitElement, html, css, TemplateResult } from 'lit';
|
||||||
|
import { customElement, property, state } from 'lit/decorators.js';
|
||||||
|
import { fireEvent } from '../utilities/fire-event';
|
||||||
|
import { StrategyConfig } from '../types/strategy/strategy-generics';
|
||||||
|
|
||||||
|
export interface LovelaceStrategyEditor extends HTMLElement {
|
||||||
|
hass?: any;
|
||||||
|
config?: any;
|
||||||
|
setConfig(config: any): void;
|
||||||
|
configChanged?: (config: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
@customElement("hui-mushroom-strategy-editor")
|
||||||
|
export class HuiMushroomStrategyEditor
|
||||||
|
extends LitElement
|
||||||
|
implements LovelaceStrategyEditor
|
||||||
|
{
|
||||||
|
@property({ attribute: false }) public hass!: any;
|
||||||
|
@state() private _config?: any;
|
||||||
|
@state() private _selectedConfigArea?: string;
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return css`
|
||||||
|
.card-config {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section {
|
||||||
|
border: 1px solid var(--divider-color);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-header {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row label {
|
||||||
|
width: 200px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-switch {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-textfield {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-select {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 16px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public setConfig(config: any): void {
|
||||||
|
console.log('Editor setConfig called with:', config);
|
||||||
|
this._config = config || { type: 'custom:mushroom-strategy', options: {} };
|
||||||
|
}
|
||||||
|
|
||||||
|
public configChanged(config: any): void {
|
||||||
|
console.log('Editor configChanged called with:', config);
|
||||||
|
this._config = config || { type: 'custom:mushroom-strategy', options: {} };
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _valueChanged(ev: CustomEvent): void {
|
||||||
|
if (!this._config) return;
|
||||||
|
|
||||||
|
const target = ev.target as any;
|
||||||
|
const configPath = target.configPath;
|
||||||
|
const value = target.value;
|
||||||
|
|
||||||
|
if (!configPath) return;
|
||||||
|
|
||||||
|
const newConfig = JSON.parse(JSON.stringify(this._config));
|
||||||
|
|
||||||
|
// Ensure the options structure exists (flat structure for strategies)
|
||||||
|
if (!newConfig.options) {
|
||||||
|
newConfig.options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate to the correct path and set the value
|
||||||
|
let current = newConfig.options;
|
||||||
|
const pathParts = configPath.split('.');
|
||||||
|
|
||||||
|
for (let i = 0; i < pathParts.length - 1; i++) {
|
||||||
|
if (!current[pathParts[i]]) {
|
||||||
|
current[pathParts[i]] = {};
|
||||||
|
}
|
||||||
|
current = current[pathParts[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
current[pathParts[pathParts.length - 1]] = value;
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: newConfig });
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this._config) {
|
||||||
|
// Initialize with default config if none provided
|
||||||
|
this._config = {
|
||||||
|
type: 'custom:mushroom-strategy',
|
||||||
|
options: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// For strategies, options are directly under the config
|
||||||
|
const options = this._config.options || {};
|
||||||
|
|
||||||
|
console.log('Editor rendering with config:', this._config);
|
||||||
|
console.log('Options:', options);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="card-config">
|
||||||
|
<div style="margin-bottom: 16px; padding: 8px; background: var(--secondary-background-color); border-radius: 4px;">
|
||||||
|
<strong>Mushroom Strategy Configuration</strong>
|
||||||
|
</div>
|
||||||
|
${this._renderGeneralSection(options)}
|
||||||
|
${this._renderHomeViewSection(options)}
|
||||||
|
${this._renderChipsSection(options)}
|
||||||
|
${this._renderAreasSection(options)}
|
||||||
|
${this._renderDomainsSection(options)}
|
||||||
|
${this._renderViewsSection(options)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderGeneralSection(options: StrategyConfig): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">General Settings</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Debug Mode</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${options.debug || false}
|
||||||
|
.configPath=${"debug"}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderHomeViewSection(options: StrategyConfig): TemplateResult {
|
||||||
|
const homeView = options.home_view || { hidden: [], stack_count: { _: 2 } };
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">Home View Settings</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Default Stack Count</label>
|
||||||
|
<ha-textfield
|
||||||
|
type="number"
|
||||||
|
.value=${homeView.stack_count?._ || 2}
|
||||||
|
.configPath=${"home_view.stack_count._"}
|
||||||
|
@input=${this._valueChanged}
|
||||||
|
></ha-textfield>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${this._renderHiddenSections(homeView.hidden || [])}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderHiddenSections(hidden: string[]): TemplateResult {
|
||||||
|
const sections = ['areas', 'areasTitle', 'chips', 'greeting', 'persons'];
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
<label>Hidden Sections:</label>
|
||||||
|
${sections.map(section => html`
|
||||||
|
<div class="form-row">
|
||||||
|
<label>${section}</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${hidden.includes(section)}
|
||||||
|
.configPath=${"home_view.hidden"}
|
||||||
|
.section=${section}
|
||||||
|
@change=${this._toggleHiddenSection}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleHiddenSection(ev: CustomEvent): void {
|
||||||
|
const target = ev.target as any;
|
||||||
|
const section = target.section;
|
||||||
|
const isChecked = target.checked;
|
||||||
|
|
||||||
|
if (!this._config) return;
|
||||||
|
|
||||||
|
const newConfig = JSON.parse(JSON.stringify(this._config));
|
||||||
|
|
||||||
|
// Ensure the options structure exists (flat structure for strategies)
|
||||||
|
if (!newConfig.options) {
|
||||||
|
newConfig.options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const hidden = newConfig.options.home_view?.hidden || [];
|
||||||
|
|
||||||
|
if (isChecked && !hidden.includes(section)) {
|
||||||
|
hidden.push(section);
|
||||||
|
} else if (!isChecked) {
|
||||||
|
const index = hidden.indexOf(section);
|
||||||
|
if (index > -1) {
|
||||||
|
hidden.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newConfig.options.home_view) {
|
||||||
|
newConfig.options.home_view = {};
|
||||||
|
}
|
||||||
|
newConfig.options.home_view.hidden = hidden;
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: newConfig });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderChipsSection(options: StrategyConfig): TemplateResult {
|
||||||
|
const chips = options.chips || {};
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">Chips Configuration</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Weather Entity</label>
|
||||||
|
<ha-textfield
|
||||||
|
.value=${chips.weather_entity || 'auto'}
|
||||||
|
.configPath=${"chips.weather_entity"}
|
||||||
|
@input=${this._valueChanged}
|
||||||
|
></ha-textfield>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Light Count</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${chips.light_count !== false}
|
||||||
|
.configPath=${"chips.light_count"}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Fan Count</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${chips.fan_count !== false}
|
||||||
|
.configPath=${"chips.fan_count"}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Cover Count</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${chips.cover_count !== false}
|
||||||
|
.configPath=${"chips.cover_count"}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Switch Count</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${chips.switch_count !== false}
|
||||||
|
.configPath=${"chips.switch_count"}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Climate Count</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${chips.climate_count !== false}
|
||||||
|
.configPath=${"chips.climate_count"}
|
||||||
|
@change=${this._valueChanged}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderAreasSection(options: StrategyConfig): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">Areas Configuration</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<label>Default Area Card Type</label>
|
||||||
|
<ha-select
|
||||||
|
.value=${options.areas?._ || 'AreaCard'}
|
||||||
|
.configPath=${"areas._.type"}
|
||||||
|
@selected=${this._valueChanged}
|
||||||
|
>
|
||||||
|
<mwc-list-item value="AreaCard">Mushroom Area Card</mwc-list-item>
|
||||||
|
<mwc-list-item value="HaAreaCard">Home Assistant Area Card</mwc-list-item>
|
||||||
|
</ha-select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderDomainsSection(options: StrategyConfig): TemplateResult {
|
||||||
|
const domains = options.domains || {};
|
||||||
|
const supportedDomains = ['light', 'switch', 'fan', 'cover', 'climate', 'lock', 'camera', 'vacuum', 'scene'] as const;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">Domain Configuration</div>
|
||||||
|
|
||||||
|
${supportedDomains.map(domain => html`
|
||||||
|
<div class="form-row">
|
||||||
|
<label>${domain.charAt(0).toUpperCase() + domain.slice(1)}</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${!domains[domain]?.hidden}
|
||||||
|
.configPath=${"domains." + domain + ".hidden"}
|
||||||
|
@change=${this._toggleDomainHidden}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleDomainHidden(ev: CustomEvent): void {
|
||||||
|
const target = ev.target as any;
|
||||||
|
const configPath = target.configPath;
|
||||||
|
const isChecked = target.checked;
|
||||||
|
|
||||||
|
if (!this._config) return;
|
||||||
|
|
||||||
|
const newConfig = JSON.parse(JSON.stringify(this._config));
|
||||||
|
|
||||||
|
// Ensure the options structure exists (flat structure for strategies)
|
||||||
|
if (!newConfig.options) {
|
||||||
|
newConfig.options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate to the correct path and set the hidden value (opposite of checked)
|
||||||
|
let current = newConfig.options;
|
||||||
|
const pathParts = configPath.split('.');
|
||||||
|
|
||||||
|
for (let i = 0; i < pathParts.length - 1; i++) {
|
||||||
|
if (!current[pathParts[i]]) {
|
||||||
|
current[pathParts[i]] = {};
|
||||||
|
}
|
||||||
|
current = current[pathParts[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
current[pathParts[pathParts.length - 1]] = !isChecked;
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: newConfig });
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderViewsSection(options: StrategyConfig): TemplateResult {
|
||||||
|
const views = options.views || {};
|
||||||
|
const supportedViews = ['home', 'light', 'switch', 'fan', 'cover', 'climate', 'lock', 'camera', 'vacuum', 'scene'] as const;
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<div class="section">
|
||||||
|
<div class="section-header">Views Configuration</div>
|
||||||
|
|
||||||
|
${supportedViews.map(view => html`
|
||||||
|
<div class="form-row">
|
||||||
|
<label>${view.charAt(0).toUpperCase() + view.slice(1)} View</label>
|
||||||
|
<ha-switch
|
||||||
|
.checked=${!views[view]?.hidden}
|
||||||
|
.configPath=${"views." + view + ".hidden"}
|
||||||
|
@change=${this._toggleViewHidden}
|
||||||
|
></ha-switch>
|
||||||
|
</div>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _toggleViewHidden(ev: CustomEvent): void {
|
||||||
|
const target = ev.target as any;
|
||||||
|
const configPath = target.configPath;
|
||||||
|
const isChecked = target.checked;
|
||||||
|
|
||||||
|
if (!this._config) return;
|
||||||
|
|
||||||
|
const newConfig = JSON.parse(JSON.stringify(this._config));
|
||||||
|
|
||||||
|
// Ensure the options structure exists (flat structure for strategies)
|
||||||
|
if (!newConfig.options) {
|
||||||
|
newConfig.options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigate to the correct path and set the hidden value (opposite of checked)
|
||||||
|
let current = newConfig.options;
|
||||||
|
const pathParts = configPath.split('.');
|
||||||
|
|
||||||
|
for (let i = 0; i < pathParts.length - 1; i++) {
|
||||||
|
if (!current[pathParts[i]]) {
|
||||||
|
current[pathParts[i]] = {};
|
||||||
|
}
|
||||||
|
current = current[pathParts[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
current[pathParts[pathParts.length - 1]] = !isChecked;
|
||||||
|
|
||||||
|
fireEvent(this, "config-changed", { config: newConfig });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure custom element is registered
|
||||||
|
if (!customElements.get("hui-mushroom-strategy-editor")) {
|
||||||
|
customElements.define("hui-mushroom-strategy-editor", HuiMushroomStrategyEditor);
|
||||||
|
}
|
@@ -1,6 +1,7 @@
|
|||||||
import { HassServiceTarget } from 'home-assistant-js-websocket';
|
import { HassServiceTarget } from 'home-assistant-js-websocket';
|
||||||
import HeaderCard from './cards/HeaderCard';
|
import HeaderCard from './cards/HeaderCard';
|
||||||
import SensorCard from './cards/SensorCard';
|
import SensorCard from './cards/SensorCard';
|
||||||
|
import './editor/hui-mushroom-strategy-editor';
|
||||||
import { Registry } from './Registry';
|
import { Registry } from './Registry';
|
||||||
import { LovelaceCardConfig } from './types/homeassistant/data/lovelace/config/card';
|
import { LovelaceCardConfig } from './types/homeassistant/data/lovelace/config/card';
|
||||||
import { LovelaceConfig } from './types/homeassistant/data/lovelace/config/types';
|
import { LovelaceConfig } from './types/homeassistant/data/lovelace/config/types';
|
||||||
@@ -268,8 +269,20 @@ class MushroomStrategy extends HTMLTemplateElement {
|
|||||||
logMessage(lvlError, 'Error while handling persistent notifications for Mushroom Strategy', e);
|
logMessage(lvlError, 'Error while handling persistent notifications for Mushroom Strategy', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getConfigElement() {
|
||||||
|
return document.createElement("hui-mushroom-strategy-editor");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static getStubConfig() {
|
||||||
|
return {
|
||||||
|
type: 'custom:mushroom-strategy',
|
||||||
|
options: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register the strategy
|
||||||
customElements.define('ll-strategy-mushroom-strategy', MushroomStrategy);
|
customElements.define('ll-strategy-mushroom-strategy', MushroomStrategy);
|
||||||
|
|
||||||
const STRATEGY_VERSION = 'v2.3.5';
|
const STRATEGY_VERSION = 'v2.3.5';
|
||||||
@@ -278,3 +291,15 @@ console.info(
|
|||||||
'color: white; background: coral; font-weight: 700;',
|
'color: white; background: coral; font-weight: 700;',
|
||||||
'color: coral; background: white; font-weight: 700;'
|
'color: coral; background: white; font-weight: 700;'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Debug: Test editor registration
|
||||||
|
setTimeout(() => {
|
||||||
|
const strategy = customElements.get('ll-strategy-mushroom-strategy');
|
||||||
|
const editor = customElements.get('hui-mushroom-strategy-editor');
|
||||||
|
console.log('Mushroom Strategy Debug:', {
|
||||||
|
strategyRegistered: !!strategy,
|
||||||
|
editorRegistered: !!editor,
|
||||||
|
hasGetConfigElement: !!(strategy as any)?.getConfigElement,
|
||||||
|
hasGetStubConfig: !!(strategy as any)?.getStubConfig
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
21
src/utilities/fire-event.ts
Normal file
21
src/utilities/fire-event.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
export const fireEvent = (
|
||||||
|
node: HTMLElement,
|
||||||
|
type: string,
|
||||||
|
detail: any,
|
||||||
|
options?: {
|
||||||
|
bubbles?: boolean;
|
||||||
|
cancelable?: boolean;
|
||||||
|
composed?: boolean;
|
||||||
|
}
|
||||||
|
): Event => {
|
||||||
|
options = options || {};
|
||||||
|
detail = detail === null || detail === undefined ? {} : detail;
|
||||||
|
const event = new CustomEvent(type, {
|
||||||
|
bubbles: options.bubbles === undefined ? true : options.bubbles,
|
||||||
|
cancelable: Boolean(options.cancelable),
|
||||||
|
composed: options.composed === undefined ? true : options.composed,
|
||||||
|
detail,
|
||||||
|
});
|
||||||
|
node.dispatchEvent(event);
|
||||||
|
return event;
|
||||||
|
};
|
@@ -15,13 +15,16 @@
|
|||||||
// 4. Interop and Imports
|
// 4. Interop and Imports
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
// 5. Type Checking and Strictness
|
// 5. Experimental Features
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
// 6. Type Checking and Strictness
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"noImplicitThis": true,
|
"noImplicitThis": true,
|
||||||
"noImplicitReturns": true,
|
"noImplicitReturns": true,
|
||||||
"strictNullChecks": true,
|
"strictNullChecks": true,
|
||||||
// 6. Project Structure and Build Integrity
|
// 7. Project Structure and Build Integrity
|
||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"skipLibCheck": true
|
"skipLibCheck": true
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user