Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for script domain #11

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
8f5701c
Bold the entity name
lizsugar Apr 9, 2022
458d26a
make slider track more pronounced
lizsugar Apr 14, 2022
3a9b803
name and todo updates
lizsugar Apr 14, 2022
2737e93
Change card name to be more obvious
lizsugar Apr 17, 2022
37729ca
Add support for automation domain
lizsugar Apr 17, 2022
3615e5f
source and built files allowing automations as switch cards
lizsugar Apr 18, 2022
746beb5
initial support for relative slider positioning
lizsugar Apr 21, 2022
41a9819
built file with support relative slider positioning
lizsugar Apr 21, 2022
ffb8570
Resolved issue with card bounds cutting off relative slider movement
lizsugar Apr 21, 2022
1ecdb3e
Add support for automation domain
lizsugar Apr 17, 2022
ce8d556
source and built files allowing automations as switch cards
lizsugar Apr 18, 2022
cf160ef
initial support for relative slider positioning
lizsugar Apr 21, 2022
1244ea4
built file with support relative slider positioning
lizsugar Apr 21, 2022
25659c8
Resolved issue with card bounds cutting off relative slider movement
lizsugar Apr 21, 2022
5ff5b1f
Initial yaml config and handling for tap_action on slider
lizsugar Apr 21, 2022
a9c6fb7
Change how I'm handling the slider tap action
lizsugar Apr 22, 2022
b9dbd83
create proper automation controller
lizsugar Apr 22, 2022
1c5c315
initial attribute yaml config
lizsugar Apr 23, 2022
0755edb
Resolve "semantic error TS2515: Non-abstract class '__' does not impl…
lizsugar Apr 25, 2022
b9b7aea
Merge branch 'relative_slider_positioning' into main
lizsugar Apr 25, 2022
88e3567
Merge branch 'automation_domain_support' into main
lizsugar Apr 25, 2022
fadd518
Merge pull request #2 from rohankapoorcom/animate-speedless-fans-upst…
lizsugar Apr 25, 2022
1af7253
create dist file after latest merges
lizsugar Apr 25, 2022
12246fd
Display user-defined attribute
lizsugar Apr 26, 2022
89bf928
minor visual changes and default attribute options by domain
lizsugar Apr 26, 2022
db64daa
Merge branch 'visual_changes' into main
lizsugar Apr 26, 2022
5005eb7
fork specific readme updates
lizsugar Apr 26, 2022
57817d5
initial controller for input-number domain
lizsugar Apr 26, 2022
3a6f885
Add input_number domain to slider-button-card
lizsugar Apr 27, 2022
528b672
Merge branch 'input_number' into main
lizsugar Apr 27, 2022
80355ec
resolve issue with sliders that are not 0 to 100
lizsugar Apr 27, 2022
bc7c570
Merge branch 'relative_slider_positioning' into main
lizsugar Apr 27, 2022
c068172
State and attribute are now displyed on a single line
lizsugar Apr 29, 2022
dea525d
Merge branch 'visual_changes' into main
lizsugar Apr 29, 2022
a2bbbbc
build after merge
lizsugar Apr 29, 2022
83590d9
Added en language/gui toggle for slider tap_action
lizsugar Apr 29, 2022
e69ace2
Merge branch 'slider_tap_action' into main
lizsugar Apr 30, 2022
4c1c701
Add support for sensor domain and binary_sensor domain
lizsugar May 3, 2022
c8b86d7
Merge branch 'sensor_cards' into main
lizsugar May 3, 2022
7f3e715
fix missing configs on BINARY_SENSOR and SENSOR
lizsugar May 6, 2022
8c57bf8
initial script domain support
lizsugar May 13, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@

A button card with integrated slider for `light, switch, fan, cover, input_boolean, media_player, climate, lock` entities.

Fork maintained by [@lizsugar](https://www.github.com/lizsugar) and has the following modifications:

- Automation entities supported
- Slider moves based on relative position, not absolute
- Option to show attribute

Also includes fan speed modifications from [@rohankapoorcom](https://github.com/rohankapoorcom).

## TODO:
- [ ] Add a tap action on the card independent of slider enabled/disabled

![Preview][preview]
![Preview 2][preview-2]

Expand Down
7,811 changes: 7,665 additions & 146 deletions dist/slider-button-card.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion hacs.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "Slider Button Card",
"name": "Slider Button Card (Liz Sugar mods)",
"render_readme": true,
"filename": "slider-button-card.js",
"domains": ["light", "fan", "cover", "switch", "input_boolean", "media_player"]
Expand Down
39 changes: 39 additions & 0 deletions src/controllers/automation-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { STATES_OFF } from 'custom-card-helpers';
import { Controller } from './controller';

export class AutomationController extends Controller {
_min = 0;
_max = 1;
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get _value(): number {
return !STATES_OFF.includes(this.stateObj.state)
? 1
: 0;
}

set _value(value) {
const service = value > 0 ? 'turn_on' : 'turn_off';
this._hass.callService('automation', service, {
// eslint-disable-next-line @typescript-eslint/camelcase
entity_id: this.stateObj.entity_id
});
}

get _step(): number {
return 1;
}

get label(): string {
if (this.percentage > 0) {
return this._hass.localize('component.automation.state._.on');
}
return this._hass.localize('component.automation.state._.off');
}

}
25 changes: 25 additions & 0 deletions src/controllers/binary-sensor-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { STATES_OFF } from 'custom-card-helpers';
import { Controller } from './controller';

export class BinarySensorController extends Controller {
_min = 0;
_max = 1;
_targetValue;
_invert = false;
_step = 1;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get _value(): number {
return !STATES_OFF.includes(this.stateObj.state)
? 1
: 0;
}

get label(): string {
return this.stateObj.state;
}

}
4 changes: 4 additions & 0 deletions src/controllers/climate-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { Controller } from './controller';
export class ClimateController extends Controller {
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get _value(): number {
return this.stateObj.attributes.temperature;
Expand Down
71 changes: 67 additions & 4 deletions src/controllers/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ export abstract class Controller {
_sliderPrevColor = '';

abstract _value?: number;
abstract _originalValue?: number;
abstract _originalValueLock?: boolean;
abstract _clickPosition?: number;
abstract _clickPositionLock?: boolean;
abstract _targetValue?: number;
abstract _min?: number;
abstract _max?: number;
Expand Down Expand Up @@ -67,6 +71,58 @@ export abstract class Controller {
}
}

set originalValue(value: number) {
this._originalValue = value;
}

get originalValue(): number {
//return this.originalValue;
if (this._originalValue === 0) {
return 0;
}
if (this._originalValue) {
return Math.round(this._originalValue / this.step) * this.step;
}
return 0;
}

get originalValueLock(): boolean {
if (this._originalValueLock == true) {
return true;
}
return false;
}

set originalValueLock(lock: boolean) {
this._originalValueLock = lock;
}

set clickPosition(value: number) {
this._clickPosition = value;
}

get clickPosition(): number {
//return this.clickPosition;
if (this._clickPosition === 0) {
return 0;
}
if (this._clickPosition) {
return Math.round(this._clickPosition / this.step) * this.step;
}
return 0;
}

get clickPositionLock(): boolean {
if (this._clickPositionLock == true) {
return true;
}
return false;
}

set clickPositionLock(lock: boolean) {
this._clickPositionLock = lock;
}

get targetValue(): number {
if (this._targetValue === 0) {
return 0;
Expand All @@ -91,6 +147,13 @@ export abstract class Controller {
return `${this.targetValue}`;
}

get attributeLabel(): string {
if (this._config.attribute) {
return this.stateObj.attributes[this._config.attribute];
}
return '';
}

get hidden(): boolean {
return false;
}
Expand All @@ -99,8 +162,8 @@ export abstract class Controller {
return true;
}

get hasToggle(): boolean {
return this._config.slider?.toggle_on_click ?? false;
get disableSliding(): boolean {
return this._config.slider?.disable_sliding ?? false;
}

get toggleValue(): number {
Expand All @@ -120,7 +183,7 @@ export abstract class Controller {
}

get isSliderDisabled(): boolean {
return this.isUnavailable ? this.isUnavailable : this.hasToggle;
return this.isUnavailable ? this.isUnavailable : this.disableSliding;
}

get min(): number {
Expand Down Expand Up @@ -236,7 +299,7 @@ export abstract class Controller {
moveSlider(event: any, {left, top, width, height}): number {
let percentage = this.calcMovementPercentage(event, {left, top, width, height});
percentage = this.applyStep(percentage);
percentage = normalize(percentage, 0, 100);
//percentage = normalize(percentage, 0, 100);
if (!this.isValuePercentage) {
percentage = percentageToValue(percentage, this.min, this.max);
}
Expand Down
4 changes: 4 additions & 0 deletions src/controllers/cover-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export class CoverController extends Controller {
_min = 0;
_targetValue;
_invert = true;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get attribute(): string {
if (this._config.slider?.attribute?.length && this.allowedAttributes.includes(this._config.slider?.attribute)) {
Expand Down
4 changes: 4 additions & 0 deletions src/controllers/fan-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export class FanController extends Controller {
_min = 0;
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get _value(): number {
return this.isUnavailable || STATES_OFF.includes(this.state)
Expand Down
10 changes: 10 additions & 0 deletions src/controllers/get-controller.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import { computeDomain } from 'custom-card-helpers';
import { Domain, SliderButtonCardConfig } from '../types';
import { AutomationController } from './automation-controller';
import { ClimateController } from './climate-controller';
import { Controller } from './controller';
import { CoverController } from './cover-controller';
import { FanController } from './fan-controller';
import { InputBooleanController } from './input-boolean-controller';
import { InputNumberController } from './input-number-controller';
import { LightController } from './light-controller';
import { LockController } from './lock-controller';
import { MediaController } from './media-controller';
import { SwitchController } from './switch-controller';
import { SensorController } from './sensor-controller';
import { BinarySensorController } from './binary-sensor-controller';
import { ScriptController } from './script-controller';

export class ControllerFactory {
static getInstance(config: SliderButtonCardConfig): Controller {
Expand All @@ -17,11 +22,16 @@ export class ControllerFactory {
[Domain.LIGHT]: LightController,
[Domain.FAN]: FanController,
[Domain.SWITCH]: SwitchController,
[Domain.AUTOMATION]: AutomationController,
[Domain.COVER]: CoverController,
[Domain.INPUT_BOOLEAN]: InputBooleanController,
[Domain.INPUT_NUMBER]: InputNumberController,
[Domain.MEDIA_PLAYER]: MediaController,
[Domain.CLIMATE]: ClimateController,
[Domain.LOCK]: LockController,
[Domain.SENSOR]: SensorController,
[Domain.BINARY_SENSOR]: BinarySensorController,
[Domain.SCRIPT]: ScriptController,
};
if (typeof mapping[domain] === 'undefined') {
throw new Error(`Unsupported entity type: ${domain}`)
Expand Down
4 changes: 4 additions & 0 deletions src/controllers/input-boolean-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export class InputBooleanController extends Controller {
_max = 1;
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get _value(): number {
return !STATES_OFF.includes(this.stateObj.state)
Expand Down
64 changes: 64 additions & 0 deletions src/controllers/input-number-controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Controller } from './controller';
import { normalize, percentageToValue, toPercentage } from '../utils';
import { SliderConfig } from '../types';

export class InputNumberController extends Controller {
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;
// _min;
// _max;

get _value(): number {
return this.stateObj.state;
}

set _value(value) {
//value = percentageToValue(value, this._min, this._max);
this._hass.callService('input_number', 'set_value', {
// eslint-disable-next-line @typescript-eslint/camelcase
entity_id: this.stateObj.entity_id,
value: value,
});
}

get _min(): number {
return this.stateObj.attributes.min;
}

get _max(): number {
return this.stateObj.attributes.max;
}

// get _targetValue(): number {
// return this._value;
// }

// set _targetValue(value: number) {
// if (value !== this.targetValue) {
// if (value > this._min) {
// value = this._min;
// }
// if (value > this._max) {
// value = this._max;
// }
// this._targetValue = value;
// }
// }

get isValuePercentage(): boolean {
return false;
}

get _step(): number {
return this.stateObj.attributes.step;
}

get label(): string {
return this.stateObj.attributes.unit_of_measurement ? `${this.targetValue} ${this.stateObj.attributes.unit_of_measurement}` : `${this.targetValue}`;
}

}
6 changes: 5 additions & 1 deletion src/controllers/light-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ export class LightController extends Controller {
_step = 1;
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get attribute(): string {
const attr = this._config.slider?.attribute as LightAttributes;
Expand Down Expand Up @@ -210,7 +214,7 @@ export class LightController extends Controller {
if (supported.length === 1 && supported[0] === LightAttributes.ON_OFF) {
return true;
}
return this._config.slider?.toggle_on_click ?? false;
return this._config.slider?.disable_sliding ?? false;
}

get hasSlider(): boolean {
Expand Down
4 changes: 4 additions & 0 deletions src/controllers/lock-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ export class LockController extends Controller {
_max = 1;
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get _value(): number {
return !STATES_OFF.includes(this.stateObj.state)
Expand Down
4 changes: 4 additions & 0 deletions src/controllers/media-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ export class MediaController extends Controller {
_step = 1;
_targetValue;
_invert = false;
_clickPosition;
_clickPositionLock;
_originalValue;
_originalValueLock;

get _value(): number {
return this.isUnavailable || this.stateObj?.attributes?.is_volume_muted
Expand Down
Loading