tap_action instead of action, deprecation of color_off and style in state support (#84)

* Support for style definition in state

* Update documentation

* Deprecate color_off and action replaced by tap_action

* Fix documentation

* Fixing documentation

* Fix #71

* Add support for navigation

* Fix documentation

* Fix #71 - better solution

* Default color when off is var(--disabled-text-color)

* location action example

* Fix documentation

* Some bugfix

* Fix documentation

* Disable click when not needed

* Linting and some code fix requested

* Some fix

* renamed location to navigate

* Missing break in switch case

* remove inexisting variable reference

* Fixing button size and font to match default

* Fixing button size on firefox

* Use default entity icon if undefined and add show_icon, Fix #53

* Fix documentation

* Replace default colors to match hass default

* Updating documentation

* Fix isClickable

* Some refactoring

* Blink animation support
This commit is contained in:
Jérôme W 2019-04-18 01:03:57 +02:00 committed by Ian Richardson
parent 416fb899a9
commit 11b87a5a50
3 changed files with 464 additions and 188 deletions

219
README.md
View File

@ -16,43 +16,75 @@ Lovelace Button card for your entities.
## Features
- works with any toggleable entity
- 3 actions on tap `toggle`, `more_info` and `service`
- 3 actions on tap `none`, `toggle`, `more-info` and `call-service`
- state display (optional)
- custom color for `on` and `off` state (optional)
- custom state definition with customizable color (optional)
- custom color (optional), or based on light rgb value
- custom state definition with customizable color, icon and style (optional)
- custom size (optional)
- custom icon (optional)
- custom css style (optional)
- automatic color for light (optional)
- custom default color for lights (when color cannot be determined) (optional)
- 2 color types
- `icon` : apply color settings to the icon only
- `card` : apply color settings to the card only
- automatic font color if color_type is set to `card`
- support unit of measurement
- blank card and label card (for organization)
- [blink](#blink) animation support
- support for [custom_updater](https://github.com/custom-components/custom_updater)
## Options
## Configuration
### Main Options
| Name | Type | Default | Supported options | Description |
| ---------- | ------- | ---------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| type | string | **Required** | `custom:button-card` | Type of the card |
| entity | string | **Required** | `switch.ac` | entity_id |
| icon | string | optional | `mdi:air-conditioner` \| `attribute` | Icon to display in place of the state. Will be overriden by the icon defined defined in a state (if present). If you use keywork `attribute` it will fetch the icon configured on the entity (overrides all icons defined). |
| color_type | string | `icon` | `icon` \| `card` \| `blank-card` \| `label-card` | Color either the background of the card or the icon inside the card. Setting this to `card` enable automatic `font` and `icon` color. This allows the text/icon to be readable even if the background color is bright/dark. Additional color-type options `blank-card` and `label-card` can be used for organisation (see examples). |
| color | string | `var(--primary-text-color)` | `auto` \| `rgb(28, 128, 199)` | Color of the icon/card when state is `on`. `auto` sets the color based on the color of a light. |
| color_off | string | `var(--disabled-text-color)` | `rgb(28, 128, 199)` | Color of the icon/card when state is `off`. |
| size | string | `40%` | `20px` | Size of the icon. Can be percentage or pixel |
| action | string | `toggle` | `toggle` \| `more_info` \| `service` | Define the type of action |
| service | Object | optional | See [example section](#Examples) | Service to call and service data when action is set to `service` |
| name | string | optional | `Air conditioner` | Define an optional text to show below the icon |
| show_state | boolean | `false` | `true` \| `false` | Show the state on the card. defaults to false if not set |
| style | object | optional | `- text-transform: none` | Define a list of css attribute and their value to apply to the card |
| state | list | optional | See [state example section](#Configuration-with-states) | State to use for the color of the button. Multiple states can be defined |
| Name | Type | Default | Supported options | Description |
| ------------ | ----------- | ------------ | ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | string | **Required** | `custom:button-card` | Type of the card |
| `entity` | string | **Required** | `switch.ac` | entity_id |
| `icon` | string | `attribute` | `mdi:air-conditioner` \| `attribute` | Icon to display in place of the state. Will be overriden by the icon defined in a state (if present). If you use keyword `attribute` it will fetch the icon configured on the entity. |
| `color_type` | string | `icon` | `icon` \| `card` \| `blank-card` \| `label-card` | Color either the background of the card or the icon inside the card. Setting this to `card` enable automatic `font` and `icon` color. This allows the text/icon to be readable even if the background color is bright/dark. Additional color-type options `blank-card` and `label-card` can be used for organisation (see examples). |
| `color` | string | optional | `auto` \| `rgb(28, 128, 199)` | Color of the icon/card. `auto` sets the color based on the color of a light. By default, if the entity state is `off`, the color will be `var(--paper-item-icon-active-color)`, for `on` it will be `var(--paper-item-icon-color)` and for any other state it will be `var(--primary-text-color)`. You can redefine each colors using `state` |
| `size` | string | `40%` | `20px` | Size of the icon. Can be percentage or pixel |
| `tap_action` | object | optional | See [Service](#Action) | Define the type of action, if undefined, toggle will be used. |
| `name` | string | optional | `Air conditioner` | Define an optional text to show below the icon |
| `show_state` | boolean | `false` | `true` \| `false` | Show the state on the card. defaults to false if not set |
| `show_icon` | boolean | `true` | `true` \| `false` | Wether to show the icon or not. Unless redefined in `icon`, uses the default entity icon from hass |
| `style` | object list | optional | `- text-transform: none` | Define a list of css attribute and their value to apply to the card |
| `state` | object list | optional | See [State](#State) | State to use for the color, icon and style of the button. Multiple states can be defined |
## Installaion
### Action
| Name | Type | Default | Supported options | Description |
| ----------------- | ------ | -------- | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `action` | string | `toggle` | `more-info`, `toggle`, `call-service`, `none`, `navigate` | Action to perform |
| `navigation_path` | string | none | Eg: `/lovelace/0/` | Path to navigate to (e.g. `/lovelace/0/`) when action defined as navigate |
| `service` | string | none | Any service | Service to call (e.g. `media_player.media_play_pause`) when `action` defined as `call-service` |
| `service_data` | object | none | Any service data | Service data to include (e.g. `entity_id: media_player.bedroom`) when `action` defined as `call-service` |
### State
| Name | Type | Default | Supported options | Description |
| ---------- | ------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `operator` | string | `==` | See [Available Operators](#Available-operators) | The operator used to compare the current state against the `value` |
| `value` | string/number | **required** (unless operator is `default`) | If your entity is a sensor with numbers, use a number directly, else use a string | The value which will be compared against the current state of the entity |
| `icon` | string | optional | `mdi:battery`, `attribute` | The icon to display for this state. If `attribute` is used, the icon of the entity will be used |
| `color` | string | `var(--primary-text-color)` | Any color, eg: `rgb(28, 128, 199)` or `blue` | The color of the icon (if `color_type: icon`) or the background (if `color_type: card`) |
| `style` | string | optional | Any CSS style. If nothing is specified, the main style is used unless undefined. If you want to override the default style of the main part of the config, use `style: []` | Define a list of css attribute and their value to apply to the card |
### Available operators
| Operator | `value` example | Description |
| :-------: | --------------- | -------------------------------------------------------------------------------------------------------- |
| `<` | `5` | Current state is inferior to `value` |
| `<=` | `4` | Current state is inferior or equal to `value` |
| `==` | `42` or `'on'` | **This is the default if no operator is specified.** Current state is equal (`==` javascript) to `value` |
| `>=` | `32` | Current state is superior or equal to `value` |
| `>` | `12` | Current state is superior to `value` |
| `!=` | `'normal'` | Current state is not equal (`!=` javascript) to `value` |
| `regex` | `'^norm.*$'` | `value` regex applied to current state does match |
| `default` | N/A | If nothing matches, this is used |
## Installation
### Manual Installation
1. Download the [button-card](https://raw.githubusercontent.com/custom-cards/button-card/master/button-card.js)
@ -84,10 +116,7 @@ resources:
## Examples
More examples in [here](./examples)
Show a button for the air conditioner (blue when on):
Show a button for the air conditioner (blue when on, `var(--disabled-text-color)` when off):
![ac](examples/ac.png)
@ -97,6 +126,18 @@ Show a button for the air conditioner (blue when on):
icon: mdi:air-conditioner
color: rgb(28, 128, 199)
```
Redefine the color when the state if off to red:
```yaml
- type: "custom:button-card"
entity: switch.ac
icon: mdi:air-conditioner
color: rgb(28, 128, 199)
state:
- value: 'off'
color: rgb(255, 0, 0)
```
---------
Show an ON/OFF button for the home_lights group:
@ -121,7 +162,8 @@ Light entity with custom icon and "more info" pop-in:
entity: light.living_room_lights
icon: mdi:sofa
color: auto
action: more_info
tap_action:
action: more-info
```
@ -138,8 +180,8 @@ Light card with card color type, name, and automatic color:
icon: mdi:home
color: auto
color_type: card
default_color: rgb(255, 233, 155)
action: more_info
tap_action:
action: more-info
name: Home
style:
- font-size: 12px
@ -168,21 +210,19 @@ Horizontal stack with :
color_type: card
color: rgb(223, 255, 97)
icon: mdi:volume-plus
action: service
service:
domain: media_player
action: volume_up
data:
tap_action:
action: service
service: media_player.volume_up
service_data:
entity_id: media_player.livimg_room_speaker
- type: "custom:button-card"
color_type: card
color: rgb(223, 255, 97)
icon: mdi:volume-minus
action: service
service:
domain: media_player
action: volume_down
data:
tap_action:
action: service
service: media_player.volume_down
service_data:
entity_id: media_player.livimg_room_speaker
- type: "custom:button-card"
color_type: blank-card
@ -247,55 +287,76 @@ Input select card with select next service and custom color and icon for states.
![cube](examples/cube.png)
#### Default behavior
If you don't specify any operator, `==` will be used to match the current state against the `value`
```yaml
- type: "custom:button-card"
entity: input_select.cube_mode
icon: mdi:cube
action: service
show_state: true
state:
- value: 'sleeping'
color: var(--disabled-text-color)
icon: mdi:cube-outline
- value: 'media'
color: rgb(5, 147, 255)
- value: 'light'
color: rgb(189, 255, 5)
- type: "custom:button-card"
entity: input_select.cube_mode
icon: mdi:cube
tap_action:
action: toggle
show_state: true
state:
- value: 'sleeping'
color: var(--disabled-text-color)
icon: mdi:cube-outline
- value: 'media'
color: rgb(5, 147, 255)
- value: 'light'
color: rgb(189, 255, 5)
```
#### With Operator on state
The definition order matters, the first item to match will be the one selected.
```yaml
- type: "custom:button-card"
entity: sensor.temperature
show_state: true
state:
- value: 15
operator: '<='
color: blue
icon: mdi:thermometer-minus
- value: 25
operator: '>='
color: red
icon: mdi:thermometer-plus
- operator: 'default' # used if nothing matches
color: yellow
icon: mdi: thermometer
- type: "custom:button-card"
entity: sensor.temperature
show_state: true
state:
- value: 15
operator: '<='
color: blue
icon: mdi:thermometer-minus
- value: 25
operator: '>='
color: red
icon: mdi:thermometer-plus
- operator: 'default' # used if nothing matches
color: yellow
icon: mdi: thermometer
style:
- opacity: 0.5
```
Available operators:
| Operator | `value` example | Description |
| :-------: | --------------- | ----------------------------------------------- |
| `<` | `5` | State is inferior to `value` |
| `<=` | `4` | State is inferior or equal to `value` |
| `==` | `42` | State is equal (`==` javascript) to `value` |
| `>=` | `32` | State is superior or equal to `value` |
| `>` | `12` | State is superior to `value` |
| `!=` | `normal` | State is not equal (`!=` javascript) to `value` |
| `regex` | `'^norm.*$'` | `value` regex applied to State does match |
| `default` | N/A | If nothing matches, this is used |
#### `tap_action` Location
For example, you can swith panel with the `location` action:
```yaml
- type: "custom:button-card"
color_type: label-card
icon: mdi:home
name: Go To Home
tap_action:
action: location
navigation_path: /lovelace/0
```
#### blink
You can make the whole button blink:
![blink-animation](examples/blink-animation.gif)
```yaml
- type: "custom:button-card"
color_type: card
entity: binary_sensor.intruder
name: Intruder Alert
state:
- value: 'on'
color: red
icon: mdi:alert
style:
- animation: blink 2s ease infinite
- operator: default
color: green
icon: mdi:shield-check
```
## Credits

View File

@ -1,3 +1,107 @@
/**
* Return the icon to be used for a domain.
*
* Optionally pass in a state to influence the domain icon.
*/
export const DEFAULT_DOMAIN_ICON = "hass:bookmark";
const fixedIcons = {
alert: "hass:alert",
automation: "hass:playlist-play",
calendar: "hass:calendar",
camera: "hass:video",
climate: "hass:thermostat",
configurator: "hass:settings",
conversation: "hass:text-to-speech",
device_tracker: "hass:account",
fan: "hass:fan",
group: "hass:google-circles-communities",
history_graph: "hass:chart-line",
homeassistant: "hass:home-assistant",
homekit: "hass:home-automation",
image_processing: "hass:image-filter-frames",
input_boolean: "hass:drawing",
input_datetime: "hass:calendar-clock",
input_number: "hass:ray-vertex",
input_select: "hass:format-list-bulleted",
input_text: "hass:textbox",
light: "hass:lightbulb",
mailbox: "hass:mailbox",
notify: "hass:comment-alert",
person: "hass:account",
plant: "hass:flower",
proximity: "hass:apple-safari",
remote: "hass:remote",
scene: "hass:google-pages",
script: "hass:file-document",
sensor: "hass:eye",
simple_alarm: "hass:bell",
sun: "hass:white-balance-sunny",
switch: "hass:flash",
timer: "hass:timer",
updater: "hass:cloud-upload",
vacuum: "hass:robot-vacuum",
water_heater: "hass:thermometer",
weblink: "hass:open-in-new",
};
export default function domainIcon(domain, state) {
if (domain in fixedIcons) {
return fixedIcons[domain];
}
switch (domain) {
case "alarm_control_panel":
switch (state) {
case "armed_home":
return "hass:bell-plus";
case "armed_night":
return "hass:bell-sleep";
case "disarmed":
return "hass:bell-outline";
case "triggered":
return "hass:bell-ring";
default:
return "hass:bell";
}
case "binary_sensor":
return state && state === "off"
? "hass:radiobox-blank"
: "hass:checkbox-marked-circle";
case "cover":
return state === "closed" ? "hass:window-closed" : "hass:window-open";
case "lock":
return state && state === "unlocked" ? "hass:lock-open" : "hass:lock";
case "media_player":
return state && state !== "off" && state !== "idle"
? "hass:cast-connected"
: "hass:cast";
case "zwave":
switch (state) {
case "dead":
return "hass:emoticon-dead";
case "sleeping":
return "hass:sleep";
case "initializing":
return "hass:timer-sand";
default:
return "hass:z-wave";
}
default:
// tslint:disable-next-line
console.warn(
"Unable to find icon for domain " + domain + " (" + state + ")"
);
return DEFAULT_DOMAIN_ICON;
}
}
((LitElement, ButtonBase) => {
var html = LitElement.prototype.html;
var css = LitElement.prototype.css;
@ -37,33 +141,54 @@
margin: auto;
text-align: center;
}
button-card-button div {
padding: 4%;
button-card-button.disabled {
pointer-events: none;
cursor: default;
}
button-card-button div.main {
padding: 4% 0px;
text-transform: none;
font-weight: 400;
font-size: 1.2rem;
align-items: center;
text-align: center;
letter-spacing: normal;
width: 100%;
}
@keyframes blink{
0%{opacity:0;}
50%{opacity:1;}
100%{opacity:0;}
}
`;
}
render() {
const state = this.__hass.states[this.config.entity];
const configState = this.testConfigState(state, this.config)
switch (this.config.color_type) {
case 'blank-card':
return this.blankCardColoredHtml(state, this.config);
return this.blankCardColoredHtml(state, this.config, configState);
case 'label-card':
return this.labelCardColoredHtml(state, this.config);
return this.labelCardColoredHtml(state, this.config, configState);
case 'card':
return this.cardColoredHtml(state, this.config);
return this.cardColoredHtml(state, this.config, configState);
case 'icon':
default:
return this.iconColoredHtml(state, this.config);
return this.iconColoredHtml(state, this.config, configState);
}
}
getFontColorBasedOnBackgroundColor(backgroundColor) {
const parsedRgbColor = backgroundColor.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
const parsedBackgroundColor = parsedRgbColor ? parsedRgbColor : this.hexToRgb(backgroundColor.substring(1));
let parsedBackgroundColor = null;
if (backgroundColor.substring(0, 3) === "var") {
let rgb = getComputedStyle(this.parentNode).getPropertyValue(backgroundColor.substring(4).slice(0, -1)).trim();
parsedBackgroundColor = this.hexToRgb(rgb.substring(1));
} else {
const parsedRgbColor = backgroundColor.match(/^rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)$/i);
parsedBackgroundColor = parsedRgbColor ? parsedRgbColor : this.hexToRgb(backgroundColor.substring(1));
}
let fontColor = ''; // don't override by default
if (parsedBackgroundColor) {
// Counting the perceptive luminance - human eye favors green color...
@ -111,7 +236,7 @@
def = elt;
}
} else {
return (elt.value === state.state)
return (elt.value == state.state)
}
})
if (!retval)
@ -121,119 +246,195 @@
return retval;
}
buildCssColorAttribute(state, config) {
let color = config.color;
if (state) {
let configState = this.testConfigState(state, config);
if (configState) {
color = configState.color ? configState.color : config.color_off;
if (configState.color === 'auto') {
color = state.attributes.rgb_color ? `rgb(${state.attributes.rgb_color.join(',')})` : configState.default_color;
getDefaultColorForState(state) {
switch (state.state) {
case 'on':
return this.config.color_on;
case 'off':
return this.config.color_off;
default:
return this.config.default_color;
}
}
buildCssColorAttribute(state, config, configState) {
let colorValue = null;
let color = null;
if (configState && configState.color) {
colorValue = configState.color;
} else {
if (config.color != 'auto' && state && state.state == 'off') {
colorValue = config.color_off;
} else {
colorValue = config.color;
}
}
if (colorValue == 'auto') {
if (state) {
if (state.attributes.rgb_color) {
color = `rgb(${state.attributes.rgb_color.join(',')})`
} else {
color = this.getDefaultColorForState(state)
}
} else {
if (config.color === 'auto') {
color = state.attributes.rgb_color ? `rgb(${state.attributes.rgb_color.join(',')})` : config.default_color;
color = config.default_color;
}
} else {
if (!colorValue) {
if (state) {
color = this.getDefaultColorForState(state)
} else {
color = config.default_color;
}
color = state.state === 'on' ? color : config.color_off;
} else {
color = colorValue;
}
}
return color;
}
buildIcon(state, config) {
let iconOff = config.icon;
if (config.icon == 'attribute') {
if (state) {
const icon = state.attributes.icon;
return icon;
}
return iconOff;
}
let configState = this.testConfigState(state, config);
buildIcon(state, config, configState) {
let iconValue = null;
let icon = null;
if (configState && configState.icon) {
const icon = configState.icon;
return icon;
iconValue = configState.icon;
} else {
iconValue = config.icon;
}
return iconOff;
if (iconValue == 'attribute') {
if (state) {
icon = state.attributes.icon ?
state.attributes.icon :
domainIcon(state.entity_id.split('.', 2)[0], state.state);
} else {
icon = undefined;
}
} else {
icon = iconValue;
}
return icon;
}
blankCardColoredHtml(state, config) {
buildStyle(state, config, configState) {
let cardStyle = '';
let styleArray = undefined;
if (state) {
if (configState && configState.style) {
styleArray = configState.style;
} else if (config.style) {
styleArray = config.style;
}
} else {
if (config.style)
styleArray = config.style;
}
if (styleArray) {
styleArray.forEach((cssObject) => {
const attribute = Object.keys(cssObject)[0];
const value = cssObject[attribute];
cardStyle += `${attribute}: ${value};\n`;
});
}
return cardStyle;
}
isClickable(state, config) {
let clickable = true;
if (config.tap_action.action == 'toggle') {
if (state) {
switch (state.entity_id.split('.', 2)[0]) {
case 'sensor':
case 'binary_sensor':
clickable = false
break;
default:
clickable = true
break;
}
} else {
clickable = false
}
} else {
clickable = !(config.tap_action.action == 'none')
}
return clickable;
}
blankCardColoredHtml(state, config, configState) {
const color = this.buildCssColorAttribute(state, config);
const fontColor = this.getFontColorBasedOnBackgroundColor(color);
return html`
<ha-card style="color: ${fontColor}; background-color: ${color}; ${config.card_style}" on-tap="${ev => this._toggle(state, config)}">
<ha-card class="disabled" style="color: ${fontColor}; background-color: ${color}; position: relative;">
</ha-card>
`;
}
labelCardColoredHtml(state, config) {
const color = this.buildCssColorAttribute(state, config);
labelCardColoredHtml(state, config, configState) {
const color = this.buildCssColorAttribute(state, config, configState);
const fontColor = this.getFontColorBasedOnBackgroundColor(color);
const icon = this.buildIcon(state, config, configState);
const style = this.buildStyle(state, config, configState);
return html`
<ha-card style="color: ${fontColor};">
<button-card-button noink style="background-color: ${color}">
<div style="${config.card_style}">
${config.icon ? html`<ha-icon style="width: ${config.size}; height: ${config.size};" icon="${config.icon}"></ha-icon>` : ''}
${config.name ? html`<span>${config.name}</span>` : ''}
</div>
<ha-card style="color: ${fontColor}; position: relative;" @tap="${ev => this._handleTap(state, config)}">
<button-card-button class="${this.isClickable(state, config) ? '' : "disabled"}" style="background-color: ${color}">
<div class="main" style="${style}">
${config.show_icon && icon ? html`<ha-icon style="width: ${config.size}; height: auto;" icon="${icon}"></ha-icon>` : ''}
${config.name ? html`<div>${config.name}</div>` : ''}
</div>
</button-card-button>
</ha-card>
`;
}
cardColoredHtml(state, config) {
const color = this.buildCssColorAttribute(state, config);
cardColoredHtml(state, config, configState) {
const color = this.buildCssColorAttribute(state, config, configState);
const fontColor = this.getFontColorBasedOnBackgroundColor(color);
const icon = this.buildIcon(state, config);
const icon = this.buildIcon(state, config, configState);
const style = this.buildStyle(state, config, configState);
return html`
<ha-card style="color: ${fontColor};" @tap="${ev => this._toggle(state, config)}">
<button-card-button style="background-color: ${color}; ${config.card_style}">
<div style="${config.card_style}">
${config.icon || icon ? html`<ha-icon style="width: ${config.size}; height: ${config.size};" icon="${icon}"></ha-icon>` : ''}
${config.name ? html`<span>${config.name}</span>` : ''}
${config.show_state ? html`<span>${state.state} ${state.attributes.unit_of_measurement ? state.attributes.unit_of_measurement : ''}</span>` : ''}
</div>
<ha-card style="color: ${fontColor}; position: relative;" @tap="${ev => this._handleTap(state, config)}">
<button-card-button class="${this.isClickable(state, config) ? '' : "disabled"}" style="background-color: ${color};">
<div class="main" style="${style}">
${config.show_icon && icon ? html`<ha-icon style="width: ${config.size}; height: auto;" icon="${icon}"></ha-icon>` : ''}
${config.name ? html`<div>${config.name}</div>` : ''}
${config.show_state ? html`<div>${state.state} ${state.attributes.unit_of_measurement ? state.attributes.unit_of_measurement : ''}</div>` : ''}
</div>
</button-card-button>
</ha-card>
`;
}
iconColoredHtml(state, config) {
const color = this.buildCssColorAttribute(state, config);
const icon = this.buildIcon(state, config);
iconColoredHtml(state, config, configState) {
const color = this.buildCssColorAttribute(state, config, configState);
const icon = this.buildIcon(state, config, configState);
const style = this.buildStyle(state, config, configState);
return html`
<ha-card @tap="${ev => this._toggle(state, config)}">
<button-card-button style="${config.card_style}">
<div style="${config.card_style}">
${config.icon || icon ? html`<ha-icon style="color: ${color}; width: ${config.size}; height: ${config.size};" icon="${icon}"></ha-icon>` : ''}
${config.name ? html`<div>${config.name}</div>` : ''}
${config.show_state ? html`<div>${state.state} ${state.attributes.unit_of_measurement ? state.attributes.unit_of_measurement : ''}</div>` : ''}
</div>
<ha-card style="position: relative;" @tap="${ev => this._handleTap(state, config)}">
<button-card-button class="${this.isClickable(state, config) ? '' : "disabled"}">
<div class="main" style="${style}">
${config.show_icon && icon ? html`<ha-icon style="color: ${color}; width: ${config.size}; height: auto;" icon="${icon}"></ha-icon>` : ''}
${config.name ? html`<div>${config.name}</div>` : ''}
${config.show_state ? html`<div>${state.state} ${state.attributes.unit_of_measurement ? state.attributes.unit_of_measurement : ''}</div>` : ''}
</div>
</button-card-button>
</ha-card>
`;
}
setConfig(config) {
// if (!config.entity) {
// throw new Error('You need to define entity');
// }
this.config = { ...config };
this.config.color = config.color ? config.color : 'var(--primary-text-color)';
this.config.size = config.size ? config.size : '40%';
let cardStyle = '';
if (config.style) {
config.style.forEach((cssObject) => {
const attribute = Object.keys(cssObject)[0];
const value = cssObject[attribute];
cardStyle += `${attribute}: ${value};\n`;
});
}
this.config.color_type = config.color_type ? config.color_type : 'icon';
this.config.color_off = config.color_off ? config.color_off : 'var(--disabled-text-color)';
this.config.default_color = config.default_color ? config.default_color : 'var(--primary-text-color)';
this.config.card_style = cardStyle;
this.config.name = config.name ? config.name : '';
this.config = {
tap_action: { action: "toggle" },
size: '40%',
color_type: 'icon',
default_color: 'var(--primary-text-color)',
name: '',
icon: 'attribute',
show_icon: true,
...config
};
this.config.color_off = 'var(--paper-item-icon-color)';
this.config.color_on = 'var(--paper-item-icon-active-color)';
}
// The height of your card. Home Assistant uses this to automatically
@ -242,34 +443,48 @@
return 3;
}
_toggle(state, config) {
switch (config.action) {
case 'toggle':
this.hass.callService('homeassistant', 'toggle', {
entity_id: state.entity_id,
});
break;
case 'more_info': {
const node = this.shadowRoot;
const options = {};
const detail = { entityId: state.entity_id };
const event = new Event('hass-more-info', {
bubbles: options.bubbles === undefined ? true : options.bubbles,
cancelable: Boolean(options.cancelable),
composed: options.composed === undefined ? true : options.composed,
});
event.detail = detail;
node.dispatchEvent(event);
return event;
_handleTap(state, config) {
if (config.tap_action) {
let event;
switch (config.tap_action.action) {
case 'none':
break;
case 'more-info':
case 'more_info':
event = new Event('hass-more-info', {
bubbles: true,
cancelable: false,
composed: true,
});
event.detail = { entityId: state.entity_id };
this.shadowRoot.dispatchEvent(event);
break;
case 'navigate':
if (!config.tap_action.navigation_path) break;
history.pushState(null, "", config.tap_action.navigation_path);
event = new Event('location-changed', {
bubbles: true,
cancelable: false,
composed: true,
});
event.detail = {};
this.shadowRoot.dispatchEvent(event);
break;
case 'service':
case 'call-service':
if (!config.tap_action.service) {
return;
}
const [domain, service] = config.tap_action.service.split('.', 2);
this.hass.callService(domain, service, config.tap_action.service_data);
break;
case 'toggle':
default:
this.hass.callService('homeassistant', 'toggle', {
entity_id: state.entity_id,
});
break;
}
case 'service':
this.hass.callService(config.service.domain, config.service.action, config.service.data);
break;
default:
this.hass.callService('homeassistant', 'toggle', {
entity_id: state.entity_id,
});
break;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB