Skip to content

Commit

Permalink
version 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Falk Nielsen committed Jun 20, 2024
1 parent ddbc39d commit d5e59be
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 63 deletions.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# v2.0.0
## Breaking changes
- The following attributes have been renamed to better align with home assistant:
- iconOff -> icon_off
- onState -> on_state
- navigate -> action (also supports all normal actions now)

## New Features
- There's now an editor!
- It's now possibile to customize the main icon color, as well as individual entity icon colors using color values or templates

# v1.0.0
First public release
35 changes: 18 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,73 +42,74 @@ Room Card is available in [HACS][hacs] (Home Assistant Community Store).
```
## Configuration variables
Currently the lovelace editor is not supported, but you can use `yaml`:.
The editor is supported from version 2.0.0, but if you want to use `yaml`, heres the properties:

| Name | Type | Default | Description |
| :-------------------- | :-------------- | :---------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| `icon` | string | Required | Icon to render. |
| `name` | string | Required | Name of the room to render. |
| `icon` | string | Required | Icon to render. |
| `icon_color` | string | Optional | The color of the room icon. May contain [templates](https://www.home-assistant.io/docs/configuration/) |
| `secondary` | string | Optional | Secondary info to render. May contain [templates](https://www.home-assistant.io/docs/configuration/templating/). |
| `navigate` | string | Optional | Path to navigate to on press. |
| `entities` | list | Optional | Room state entities, supports two types: entity and [templates](https://www.home-assistant.io/docs/configuration/templating/). |

| `entities` | list | Optional | Room state entities |
<br>

Room State Entity
| Name | Type | Default | Description |
| :-------------------- | :-------------- | :---------- | :---------------------------------------------------------------------------------------------------------------------------------- |
| `type` | enum | Required | Use `entity` or `template` |
| `icon` | string | Required | Icon to render. |
| `iconOff` | string | Optional | Icon to render when state is off, if not set the icon will not be changed. |
| `icon_off` | string | Optional | Icon to render when state is off, if not set the icon will not be changed. |
| `entity` | string | Required | Required if type is `entity`, the state from this will be used. |
| `onState` | string | Required | Required if type is `entity`, the state that will be considered as on. |
| `on_state` | string | Required | Required if type is `entity`, the state that will be considered as on. |
| `condition` | string | Required | Required if type is `template`. Supports template values, return any value for on state, and empty for off. |
| `color_on` | string | Optional | The color for entitie icons when on. May contain [templates](https://www.home-assistant.io/docs/configuration/) |
| `color_off` | string | Optional | The color for entitie icons when off. May contain [templates](https://www.home-assistant.io/docs/configuration/) |



### Example
### YAML Example
```yaml
type: custom:room-card
icon: mdi:home-outline
icon_color: "#333333"
name: Living Room
secondary: '{{states("sensor.living_room_temperature")}} °C'
navigate: /dashboard/living-room
entities:
- type: entity
entity: climate.climate_living_room
icon: mdi:heat-wave
onState: heating
on_state: heating
- type: template
icon: mdi:ceiling-light
iconOff: mdi:ceiling-light-outline
icon_off: mdi:ceiling-light-outline
condition: >-
{% set lights_on = expand(area_entities('Living Room')) |
selectattr('domain','eq','light') | selectattr('state','eq','on') | list |
count %}{% if lights_on > 0 %}true{% endif %}
- type: entity
entity: binary_sensor.living_room_presence_presence
onState: 'on'
on_state: 'on'
icon: mdi:motion-sensor
iconOff: mdi:motion-sensor-off
icon_off: mdi:motion-sensor-off
- type: entity
entity: media_player.living_room
onState: playing
on_state: playing
icon: mdi:speaker
iconOff: mdi:speaker-off
icon_off: mdi:speaker-off
- type: entity
entity: media_player.living_room_tv
onState: playing
on_state: playing
icon: mdi:television-classic
iconOff: mdi:television-classic-off
icon_off: mdi:television-classic-off
```


### Theme customization

Room Card works without theme but you can add a theme like [Noctis](https://github.com/aFFekopp/noctis). If you want more information about themes, check out the official [Home Assistant documentation about themes][home-assitant-theme-docs].


<!-- Badges -->
[hacs-url]: https://github.com/hacs/integration
[hacs-badge]: https://img.shields.io/badge/hacs-default-orange.svg?style=flat-square
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hass-room-card",
"version": "1.0.0",
"version": "2.0.0",
"description": "",
"main": "src",
"scripts": {
Expand Down
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
import "./room-card-editor.js"
import "./room-card.js"
201 changes: 201 additions & 0 deletions src/room-card-editor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
import { LitElement, html } from "lit-element"

class RoomCardEditor extends LitElement {
setConfig(config) {
this._config = config
this._currentTab = 0
}

static get properties() {
return {
hass: { attribute: false },
_config: { state: true },
}
}

_deleteStateEntity(idx) {
if (!this._config) return

this._config.entities.splice(idx, 1);
this.dispatchEvent(
new CustomEvent("config-changed", { detail: { config: this._config } })
)
}

_moveStateEntity(idx, pos) {
if (!this._config) return;

[this._config.entities[idx], this._config.entities[idx + pos]] = [this._config.entities[idx + pos], this._config.entities[idx]];
this.dispatchEvent(
new CustomEvent("config-changed", { detail: { config: this._config } })
)
}

_addEntityState() {
if (!this._config) return;

this._config.entities.push({ type: "template" })
this.dispatchEvent(
new CustomEvent("config-changed", { detail: { config: this._config } })
)
}

_valueChanged(ev) {
if (!this._config || !this.hass) {
return
}

const event = new CustomEvent("config-changed", {
detail: { config: ev.detail.value },
bubbles: true,
composed: true,
})
this.dispatchEvent(event)
}

_valueChangedEntity(entity, ev) {
if (!this._config || !this.hass) {
return
}

const entities = [...this._config.entities]
entities[entity] = ev.detail.value
this._config.entities = entities

const event = new CustomEvent("config-changed", {
detail: { config: this._config },
bubbles: true,
composed: true,
})
this.dispatchEvent(event)
}

_getEntitySchema(item) {
let baseSchema = [
{name: "type", label: "State Type", selector: { select: { multiple: false, mode: "dropdown", options: [
{label: "Entity", value: "entity"},
{label: "Template", value: "template"},
]}}},
{
type: "grid",
name: "",
schema: [
{name: "icon", label: "Icon On", required: true, selector: { icon: {} }, context: { icon_entity: "entity" } },
{name: "icon_off", label: "Icon Off", selector: { icon: {} }, context: { icon_entity: "entity" } },
],
},
{
type: "grid",
name: "",
schema: [
{name: "color_on", label: "Color On", selector: { text: {} }},
{name: "color_off", label: "Color Off", selector: { text: {} }},
],
}
]
const templateSchema = [
{name: "condition", label: "Template Condition", required: true, selector: { template: {} }},
]

const entitySchema = [
{name: "entity", label: "Entity", required: true, selector: { entity: {} }},
{name: "on_state", label: "On State", required: true, selector: { text: {} }},
]

if (item.type === "template") {
baseSchema.push(...templateSchema)
}

if (item.type === "entity") {
baseSchema.push(...entitySchema)
}

const shouldExpand = item.type == "template" && item.condition == undefined || item.type == "entity" && item.entity == undefined
return [
{type: "expandable", expanded: shouldExpand , name: "", title: `State: ${item.type}`, schema: baseSchema}
]
}

_renderEntities() {
if (this._config.entities === undefined) {
this._config.entities = []
}

return html`
${this._config.entities?.map(
(entity, entity_idx) => html`
<div class="box">
<div class="toolbar">
<mwc-icon-button
.disabled=${entity_idx === 0}
@click=${() => this._moveStateEntity(entity_idx, -1)}
>
<ha-icon .icon=${"mdi:arrow-up"}></ha-icon>
</mwc-icon-button>
<mwc-icon-button
.disabled=${entity_idx === this._config.entities.length - 1}
@click=${() => this._moveStateEntity(entity_idx, 1)}
>
<ha-icon .icon=${"mdi:arrow-down"}></ha-icon>
</mwc-icon-button>
<mwc-icon-button
@click=${() => this._deleteStateEntity(entity_idx)}
>
<ha-icon .icon=${"mdi:close"}></ha-icon>
</mwc-icon-button>
<ha-form
.hass=${this.hass}
.schema=${this._getEntitySchema(entity)}
.data=${entity}
.computeLabel=${(s) => s.label ?? s.name}
@value-changed=${(ev) => this._valueChangedEntity(entity_idx, ev)}
></ha-form>
</div>
</div>
`
)}
`
}

// The render() function of a LitElement returns the HTML of your card, and any time one or the
// properties defined above are updated, the correct parts of the rendered html are magically
// replaced with the new values. Check https://lit.dev for more info.
render() {
return html`
<ha-form
.hass=${this.hass}
.data=${this._config}
.schema=${[
{name: "name", label: "Name", required: true, selector: { text: {} }},
{name: "icon", label: "Icon", required: true, selector: { icon: {} }, context: { icon_entity: "entity" } },
{name: "action", label: "Tap Action", selector: { "ui-action": {} }},
{name: "icon_color", label: "Icon Color", selector: { template: {} }},
{name: "secondary", label: "Secondary Info", selector: { template: {} }},
]}
.computeLabel=${(s) => s.label ?? s.name}
@value-changed=${this._valueChanged}
></ha-form>
<div style="display: flex;justify-content: space-between; margin-top: 20px;">
<p>States</p>
<mwc-button style="margin-top: 5px;" @click=${this._addEntityState}>
<ha-icon .icon=${"mdi:plus"}></ha-icon>Add State
</mwc-button>
</div>
${this._renderEntities()}
`
}
}

customElements.define("room-card-editor", RoomCardEditor)

window.customCards = window.customCards || []
window.customCards.push({
type: "room-card",
name: "Room Card",
preview: false, // Optional - defaults to false
description: "Display the state of a room at a glance", // Optional
documentationURL: "https://github.com/patrickfnielsen/hass-room-card", // Adds a help link in the frontend card editor
});
Loading

0 comments on commit d5e59be

Please sign in to comment.