Skip to content

Commit

Permalink
Add on off toggle and more info (#270)
Browse files Browse the repository at this point in the history
* Update container config to meet new HA config

* Enable source maps in dev build

* Add off state toggle and more info on touch hold and right click

* Fix build errors

* Fix power button clikable area

* Adjust power toggle colorw hile heating/cooling

Co-authored-by: Miklos Szanyi <miklos.szanyi@gmil.com>
  • Loading branch information
swingerman and Miklos Szanyi committed Oct 4, 2021
1 parent 5b547a2 commit a123d3b
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 15 deletions.
3 changes: 3 additions & 0 deletions .devcontainer/configuration.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
default_config:
lovelace:
mode: yaml
resources:
- url: http://127.0.0.1:5000/thermostat-dark-card.js
type: module
demo:
climate:
- platform: generic_thermostat
Expand Down
3 changes: 0 additions & 3 deletions .devcontainer/ui-lovelace.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
resources:
- url: http://127.0.0.1:5000/thermostat-dark-card.js
type: module
views:
- cards:
- type: custom:thermostat-dark-card
Expand Down
2 changes: 1 addition & 1 deletion rollup.config.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default {
},
plugins: [
resolve(),
typescript(),
typescript({ sourceMap: true }),
json(),
babel({
exclude: "node_modules/**",
Expand Down
1 change: 1 addition & 0 deletions src/const.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const HVAC_HEATING = 'heating';
export const HVAC_COOLING = 'cooling';
export const HVAC_IDLE = 'idle';
export const HVAC_OFF = 'off';

export const PRESET_ECO = 'eco';
export const PRESET_AWAY = 'away';
Expand Down
42 changes: 37 additions & 5 deletions src/thermostat-dark-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,18 @@ import {
PropertyValues,
internalProperty,
} from 'lit-element';
import { HomeAssistant, hasConfigOrEntityChanged, LovelaceCardEditor, getLovelace } from 'custom-card-helpers'; // This is a community maintained npm module with common helper functions/types
import {
HomeAssistant,
hasConfigOrEntityChanged,
LovelaceCardEditor,
getLovelace
} from 'custom-card-helpers'; // This is a community maintained npm module with common helper functions/types

import './editor';
import { ThermostatUserInterface } from './user-interface';

import type { ThermostatDarkCardConfig } from './types';
import { HVAC_HEATING, HVAC_COOLING, HVAC_IDLE, GREEN_LEAF_MODES } from './const';
import { HVAC_HEATING, HVAC_COOLING, HVAC_IDLE, HVAC_OFF, GREEN_LEAF_MODES, } from './const';
import { localize } from './localize/localize';

// This puts your card into the UI card picker dialog
Expand All @@ -29,7 +34,7 @@ import { localize } from './localize/localize';

@customElement('thermostat-dark-card')
export class ThermostatDarkCard extends ThermostatUserInterface {
@property({ attribute: false }) private _hass!: HomeAssistant;
@property({ attribute: false }) public _hass!: HomeAssistant;

public static async getConfigElement(): Promise<LovelaceCardEditor> {
return document.createElement('thermostat-dark-card-editor');
Expand All @@ -55,10 +60,9 @@ export class ThermostatDarkCard extends ThermostatUserInterface {
} else hvacState = entity.attributes['hvac_action'] || entity.state;

if (![HVAC_IDLE, HVAC_HEATING, HVAC_COOLING].includes(hvacState)) {
hvacState = config.hvac.states[hvacState] || HVAC_IDLE
hvacState = config.hvac.states[hvacState] || HVAC_OFF
}


let awayState = entity.attributes[config.away.attribute];
//let awayState = 'on';
if (config.away.sensor.sensor) {
Expand Down Expand Up @@ -159,6 +163,12 @@ export class ThermostatDarkCard extends ThermostatUserInterface {
${this.container}`;
}

private _handleHold(): void {
//const config = this._config;
//if (!config) return;
//handleClick(this, this._hass!, this._evalActions(config, 'hold_action'), true, false);
}

private _controlSetPoints(): void {
if (this.dual) {
if (
Expand Down Expand Up @@ -201,13 +211,31 @@ export class ThermostatDarkCard extends ThermostatUserInterface {
--thermostat-path-active-color: rgba(255, 255, 255, 0.8);
--thermostat-path-active-color-large: rgba(255, 255, 255, 1);
--thermostat-text-color: white;
--thermostat-toggle-color: grey;
--thermostat-toggle-off-color: darkgrey;
--thermostat-toggle-on-color: lightgrey;
}
.dial.has-thermo .dial__ico__leaf {
visibility: hidden;
}
.dial.hide-toggle .dial__ico__power {
display: none;
}
.dial .dial__shape {
transition: fill 0.5s;
}
.dial__ico__power{
fill: var(--thermostat-toggle-color);
cursor: pointer;
pointer-events: bounding-box;
}
.dial.dial--state--off .dial__ico__power{
fill: var(--thermostat-toggle-off-color);
}
.dial.dial--state--heating .dial__ico__power,
.dial.dial--state--cooling .dial__ico__power{
fill: var(--thermostat-toggle-on-color);
}
.dial__ico__leaf {
fill: #13eb13;
opacity: 0;
Expand Down Expand Up @@ -237,6 +265,7 @@ export class ThermostatDarkCard extends ThermostatUserInterface {
transition: opacity 0.5s;
}
.dial__temperatureControl {
display: none;
fill: white;
opacity: 0;
transition: opacity 0.2s;
Expand All @@ -247,6 +276,9 @@ export class ThermostatDarkCard extends ThermostatUserInterface {
.dial--edit .dial__editableIndicator {
opacity: 1;
}
.dial--edit .dial__temperatureControl {
display: block;
}
.dial--state--off .dial__shape {
fill: var(--thermostat-off-fill);
}
Expand Down
77 changes: 71 additions & 6 deletions src/user-interface.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable @typescript-eslint/camelcase */
import { LitElement, internalProperty } from 'lit-element';
import { HVAC_HEATING, HVAC_COOLING, HVAC_IDLE } from './const';
import { HomeAssistant, fireEvent } from 'custom-card-helpers';
import { HVAC_HEATING, HVAC_COOLING, HVAC_IDLE, HVAC_OFF } from './const';
class SvgUtil {
// Rotate a cartesian point about given origin by X degrees
static rotatePoint(point, angle, origin): Array<number> {
Expand Down Expand Up @@ -111,12 +112,17 @@ export class ThermostatUserInterface extends LitElement {
@internalProperty() private _ticks!: Array<SVGElement>;
@internalProperty() private _controls!: Array<SVGElement>;
@internalProperty() private _root!: SVGElement;
@internalProperty() private _toggle!: SVGElement;
@internalProperty() private minValue!: number;
@internalProperty() private maxValue!: number;
@internalProperty() private _timeoutHandler!: number;
@internalProperty() private _hvacState!: string;
@internalProperty() private _away!: boolean;
@internalProperty() private _savedOptions: any;
@internalProperty()
public _hass!: HomeAssistant;

private _touchTimeout: any;

public get hvacState(): string {
return this._hvacState;
Expand Down Expand Up @@ -173,6 +179,7 @@ export class ThermostatUserInterface extends LitElement {
config.chevron_size = Number(config.chevron_size);
config.pending = Number(config.pending);
config.idle_zone = Number(config.idle_zone);
this._touchTimeout = 0;
this._config = config; // need certain options for updates
this._ticks = []; // need for dynamic tick updates
this._controls = []; // need for managing highlight and clicks
Expand All @@ -183,11 +190,13 @@ export class ThermostatUserInterface extends LitElement {
if (config.name) this._container.appendChild(this._buildTitle(config.name));
this._container.appendChild(style);
const root = this._buildCore(config.diameter);
const toggle = this._buildPowerIcon(config.radius);
root.appendChild(this._buildDial(config.radius));
root.appendChild(this._buildTicks(config.numTicks));
root.appendChild(this._buildRing(config.radius));
root.appendChild(this._buildLeaf(config.radius));
root.appendChild(this._buildThermoIcon(config.radius));
root.appendChild(toggle);
root.appendChild(this._buildDialSlot(1));
root.appendChild(this._buildDialSlot(2));
root.appendChild(this._buildDialSlot(3));
Expand All @@ -205,11 +214,48 @@ export class ThermostatUserInterface extends LitElement {

this._container.appendChild(root);
this._root = root;
this._toggle = toggle;
this._buildControls(config.radius);
if (this._savedOptions) {
this.updateState(this._savedOptions);
}

this._root.addEventListener('click', () => this._enableControls());
this._root.addEventListener('touchstart', (e) => this._handleTouchStart(e, this));
this._root.addEventListener('touchend', () => this._handleTouchEnd());
this._root.addEventListener('touchcancel', (e) => this._handleTouchCancel(e));
this._root.addEventListener('contextmenu', (e) => this._handleMoreInfo(e, this));
this._toggle.addEventListener('click', (e) => this._handleToggle(e))
}

private _handleTouchCancel(e: TouchEvent): void {
e.preventDefault();
window.clearTimeout(this._touchTimeout);
}

private _handleTouchStart(e: TouchEvent, t: ThermostatUserInterface): void {
this._touchTimeout = setTimeout(
this._handleMoreInfo, 2*1000, e, t
)
}

private _handleTouchEnd(): void {
window.clearTimeout(this._touchTimeout);
}

private _handleMoreInfo(e: MouseEvent, t: ThermostatUserInterface): void {
if (e) e.preventDefault();
fireEvent(t, "hass-more-info", {
entityId: t._config!.entity,
});
}

private _handleToggle(e: MouseEvent) {
e.stopPropagation();
const serviceCall = this._hvacState !== HVAC_OFF ? "turn_off" : "turn_on";
this._hass!.callService("climate", serviceCall, {
entity_id: this._config!.entity
});
}

_configDial(): void {
Expand Down Expand Up @@ -415,24 +461,26 @@ export class ThermostatUserInterface extends LitElement {
if (this._timeoutHandler) clearTimeout(this._timeoutHandler);
this._updateEdit(true);
this._updateClass('has-thermo', true);
this._updateClass('hide-toggle', true);
this._updateText('target', this.temperature.target);
this._updateText('low', this.temperature.low);
this._updateText('high', this.temperature.high);
this._timeoutHandler = window.setTimeout(() => {
this._updateText('ambient', this._ambient);
this._updateEdit(false);
this._updateClass('has-thermo', false);
this._updateClass('hide-toggle', false);
this._inControl = false;
this._updateClass('in_control', this._inControl);
config.control();
}, config.pending * 1000);
}

_toggle(): boolean {
const config = this._config;
config.toggle();
return false;
}
// _toggle(): boolean {
// const config = this._config;
// config.toggle();
// return false;
// }

_updateClass(className, flag): void {
this.setSvgClass(this._root, className, flag);
Expand Down Expand Up @@ -663,6 +711,23 @@ export class ThermostatUserInterface extends LitElement {
});
}

private _buildPowerIcon(radius: number): SVGElement {
const width = 24;
const scale = 2.3;
const scaledWidth = width * scale;
const powerDef = 'M16.56,5.44L15.11,6.89C16.84,7.94 18,9.83 18,12A6,6 0 0,1 12,18A6,6 0 0,1 6,12C6,9.83 7.16,7.94 8.88,6.88L7.44,5.44C5.36,6.88 4,9.28 4,12A8,8 0 0,0 12,20A8,8 0 0,0 20,12C20,9.28 18.64,6.88 16.56,5.44M13,3H11V13H13';
const translate = [radius - (scaledWidth / 2), radius * 1.6];
const color = this._hvacState == HVAC_OFF ? 'grey' : 'white'
return this, this.createSVGElement(
'path', {
class: 'dial__ico__power',
fill: color,
d: powerDef,
transform: 'translate('+ translate[0] +',' + translate[1] +') scale('+ scale + ')',
}
)
}

private _buildDialSlot(index: number): SVGElement {
return this.createSVGElement('text', {
class: 'dial__lbl dial__lbl--ring',
Expand Down

0 comments on commit a123d3b

Please sign in to comment.