Total overhaul!

This commit is contained in:
2019-06-13 22:45:59 +02:00
parent 46be70ee49
commit 1d29e2365f
14 changed files with 611 additions and 436 deletions

35
src/climate-controller.js Normal file
View File

@@ -0,0 +1,35 @@
import {Controller} from "./controller.js";
export class ClimateController extends Controller {
get _value() {
return this.stateObj.attributes.temperature;
}
set _value(value) {
this._hass.callService("climate", "set_temperature", {
entity_id: this.stateObj.entity_id,
temperature: value,
});
}
get string() {
if (this.stateObj.attributes.operation_mode === "off")
return this._hass.localize("state.climate.off");
return `${this.value} ${this._hass.config.unit_system.temperature}`;
}
get isOff() {
return this.stateObj.attributes.operation_mode === "off";
}
get _min() {
return this.stateObj.attributes.min_temp;
}
get _max() {
return this.stateObj.attributes.max_temp;
}
get _step() {
return 1;
}
}

59
src/controller.js Normal file
View File

@@ -0,0 +1,59 @@
export class Controller {
constructor(config) {
this._config = config;
}
set hass(hass) {
this._hass = hass;
this.stateObj = this._config.entity in hass.states ? hass.states[this._config.entity] : null;
}
get value() {
if(this._value)
return Math.round(this._value/this.step)*this.step;
return 0;
}
set value(value) {
if (value !== this.value)
this._value = value;
}
get string() {
return `${this.value}`;
}
get hidden() {
return false;
}
get hasSlider() {
return true;
}
get hasToggle() {
return true;
}
get isOff() {
return this.value === 0;
}
get min() {
if (this._config.min !== undefined)
return this._config.min;
if (this._min !== undefined)
return this._min;
return 0;
}
get max() {
if (this._config.max !== undefined)
return this._config.max;
if (this._max !== undefined)
return this._max;
return 100;
}
get step() {
if (this._config.step !== undefined)
return this._config.step;
if (this._step !== undefined)
return this._step;
return 5;
}
}

40
src/cover-controller.js Normal file
View File

@@ -0,0 +1,40 @@
import {Controller} from "./controller.js";
export class CoverController extends Controller {
get _value() {
return this.stateObj.state === "open"
? this.stateObj.attributes.current_position
: 0;
}
set _value(value) {
this._hass.callService("cover", "set_cover_position", {
entity_id: this.stateObj.entity_id,
position: value,
});
}
get string() {
if (!this.hasSlider)
return "";
if (this.stateObj.state === "closed")
return this._hass.localize("state.cover.closed");
return `${this.value} %`
}
get hasToggle() {
return false;
}
get hasSlider() {
if ("current_position" in this.stateObj.attributes) return true;
if (("supported_features" in this.stateObj.attributes) &&
(this.stateObj.attributes.supported_features & 4)) return true;
return false;
}
get _step() {
return 10;
}
}

43
src/fan-controller.js Normal file
View File

@@ -0,0 +1,43 @@
import {Controller} from "./controller.js";
export class FanController extends Controller {
get _value() {
return (this.stateObj.state !== "off")
? this.stateObj.attributes.speed_list.indexOf(this.stateObj.attributes.speed)
: 0;
}
set _value(value) {
if (value in this.stateObj.attributes.speed_list) {
this._hass.callService("fan", "turn_on", {
entity_id: this.stateObj.entity_id,
speed: this.stateObj.attributes.speed_list[value],
});
} else {
this._hass.callService("fan", "turn_off", {
entity_id: this.stateObj.entity_id,
});
}
}
get string() {
if (this.stateObj.state === "off")
return this._hass.localize("state.default.off");
return this.stateObj.attributes.speed;
}
get hasSlider() {
if ("speed" in this.stateObj.attributes) return true;
return false;
}
get _max() {
return this.stateObj.attributes.speed_list.length -1;
}
get _step() {
return 1;
}
}

View File

@@ -0,0 +1,44 @@
import {Controller} from "./controller.js";
export class InputNumberController extends Controller {
get _value() {
return this.stateObj.state;
}
set _value(value) {
this._hass.callService("input_number", "set_value", {
entity_id: this.stateObj.entity_id,
value: value,
});
}
get string() {
return `${Math.round(this.stateObj.state)}`
}
get isOff() {
return false;
}
get hasToggle() {
return false;
}
get hasSlider() {
return this.stateObj.attributes.mode === "slider";
}
get _min() {
return this.stateObj.attributes.min;
}
get _max() {
return this.stateObj.attributes.max;
}
get _step() {
return this.stateObj.attributes.step;
}
}

View File

@@ -0,0 +1,41 @@
import {Controller} from "./controller.js";
export class InputSelectController extends Controller {
get _value() {
return this.stateObj.attributes.options.indexOf(this.stateObj.state);
}
set _value(value) {
if (value in this.stateObj.attributes.options)
this._hass.callService("input_select", "select_option", {
entity_id: this.stateObj.entity_id,
option: this.stateObj.attributes.options[value],
});
}
get string() {
return this.stateObj.state;
}
get isOff() {
return false;
}
get hasToggle() {
return false;
}
get hasSlider() {
return this.stateObj.attributes.options && this.stateObj.attributes.options.length > 0
}
get _max() {
return this.stateObj.attributes.options.length - 1;
}
get _step() {
return 1;
}
}

37
src/light-controller.js Normal file
View File

@@ -0,0 +1,37 @@
import {Controller} from "./controller.js";
export class LightController extends Controller {
get _value() {
return (this.stateObj.state === "on")
? Math.ceil(this.stateObj.attributes.brightness*100.0/255)
: 0;
}
set _value(value) {
value = Math.ceil(value/100.0*255);
if (value) {
this._hass.callService("light", "turn_on", {
entity_id: this.stateObj.entity_id,
brightness: value,
});
} else {
this._hass.callService("light", "turn_off", {
entity_id: this.stateObj.entity_id,
});
}
}
get string() {
if (this.stateObj.state === "off")
return this._hass.localize("state.default.off");
return `${this.value} %`;
}
get hasSlider() {
if ("brightness" in this.stateObj.attributes) return true;
if (("supported_features" in this.stateObj.attributes) &&
(this.stateObj.attributes.supported_features & 1)) return true;
return false;
}
}

119
src/main.js Normal file
View File

@@ -0,0 +1,119 @@
import {LitElement, html, css} from "/card-tools/lit-element.js";
import { Controller } from "./controller.js";
import { LightController } from "./light-controller.js";
import { MediaPlayerController } from "./media-player-controller.js";
import { ClimateController } from "./climate-controller.js";
import { CoverController } from "./cover-controller.js";
import { FanController } from "./fan-controller.js";
import { InputNumberController } from "./input-number-controller.js";
import { InputSelectController } from "./input-select-controller.js";
const controllers = {
light: LightController,
media_player: MediaPlayerController,
climate: ClimateController,
cover: CoverController,
fan: FanController,
input_number: InputNumberController,
input_select: InputSelectController,
};
class SliderEntityRow extends LitElement {
static get properties() {
return {
hass: {},
};
}
setConfig(config) {
this._config = config;
const domain = config.entity.split('.')[0];
const ctrlClass = controllers[domain];
if(!ctrlClass)
throw new Error(`Unsupported entity type: ${domain}`);
this.ctrl = new ctrlClass(config);
}
render() {
const c = this.ctrl;
c.hass = this.hass;
const slider = html`
<ha-slider
.min=${c.min}
.max=${c.max}
.step=${c.step}
.value=${c.value}
pin
@change=${(ev) => c.value = this.shadowRoot.querySelector("ha-slider").value}
class=${this._config.full_row ? "full" : ""}
></ha-slider>
`;
const toggle = html`
<ha-entity-toggle
.stateObj=${this.hass.states[this._config.entity]}
.hass=${this.hass}
></ha-entity-toggle>
`;
const content = html`
<div class="wrapper" @click=${(ev) => ev.stopPropagation()}>
${(c.stateObj.state === "unavailable")
? html`
<span class="state">
unavailable
</span>
`
: html`
${((this._config.hide_when_off && c.isOff)
|| !c.hasSlider)
? ""
: slider }
${(this._config.hide_state || this._config.toggle)
? ""
: html`
<span class="state">
${c.string}
</span>
`}
${this._config.toggle
&& c.hasToggle
? toggle
: ""}
`}
</div>
`;
if (this._config.full_row)
return content;
return html`
<hui-generic-entity-row
.hass=${this.hass}
.config=${this._config}
> ${content} </hui-generic-entity-row>
`;
}
static get styles() {
return css`
.wrapper {
display: flex;
align-items: center;
height: 40px;
}
.state {
min-width: 45px;
text-align: end;
}
ha-entity-toggle {
margin-left: 8px;
}
ha-slider.full {
width: 100%;
}
`;
}
}
customElements.define('slider-entity-row', SliderEntityRow);

View File

@@ -0,0 +1,34 @@
import {Controller} from "./controller.js";
export class MediaPlayerController extends Controller {
get _value() {
return (this.stateObj.is_volume_muted === "on")
? 0
: Math.ceil(this.stateObj.attributes.volume_level*100.0);
}
set _value(value) {
value = value/100.0;
this._hass.callService("media_player", "volume_set", {
entity_id: this.stateObj.entity_id,
volume_level: value,
});
}
get isOff() {
return this.stateObj.state === "off";
}
get string() {
if (this.stateObj.attributes.is_volume_muted)
return "-";
return !!this.stateObj.attributes.volume_level
? `${this.value} %`
: this._hass.localize("state.media_player.off");
}
get hasToggle() {
return false;
}
}