This commit is contained in:
Martin Bauer 2020-05-03 14:41:27 +00:00
parent feb05f63df
commit d3c7e18d38
11 changed files with 1093 additions and 54 deletions

View File

@ -1,28 +1,6 @@
{
"data": {
"config": {
"resources": [
{
"type": "js",
"url": "/local/custom_ui/state-card-custom-cover.js"
},
{
"type": "js",
"url": "/local/custom_ui/toggle-lock-entity-row.js"
},
{
"type": "module",
"url": "/local/custom_ui/mini-graph-card-bundle.js?v=0.4.3"
},
{
"type": "module",
"url": "/local/custom_ui/mini-media-player-bundle.js?v=1.2.0"
},
{
"type": "js",
"url": "/local/custom_ui/room-glance-card.js"
}
],
"views": [
{
"cards": [
@ -179,10 +157,6 @@
},
{
"entities": [
{
"entity": "switch.mobile_steckdose_schalter_1",
"name": "Stern Eingang"
},
{
"entity": "switch.mobile_steckdose_schalter_2",
"name": "Balkon"
@ -192,6 +166,16 @@
"show_header_toggle": true,
"title": "Weihnachten",
"type": "entities"
},
{
"entities": [
"switch.klingel_innentur",
"switch.klingen_aussentur",
"switch.klingel_oben"
],
"icon": "mdi:bell",
"title": "Klingel",
"type": "entities"
}
],
"icon": "mdi:sofa",
@ -247,25 +231,25 @@
"name": "Gang"
},
{
"entity": "group.light_living_area",
"entity": "group.living_area",
"icon": "mdi:sofa",
"name": "Wohnzimmer/Esszimmer"
},
{
"entity": "group.light_office_martin",
"entity": "group.office_martin",
"icon": "mdi:monitor-multiple",
"name": "Martins B\u00fcro"
},
{
"entity": "switch.mobile_steckdose_schalter_1",
"icon": "mdi:palm-tree",
"name": "Palmenlicht"
},
{
"entity": "group.outside",
"icon": "mdi:flower",
"name": "Drau\u00dfen"
},
{
"entity": "switch.mobile_steckdose_schalter_1",
"icon": "mdi:pine-tree",
"name": "Stern Eingang"
},
{
"entity": "switch.mobile_steckdose_schalter_2",
"name": "Balkon"
@ -301,7 +285,7 @@
},
{
"icon": "mdi:heart",
"id": "Kuschelmusik",
"id": "Bar Classics",
"type": "music"
},
{
@ -388,6 +372,7 @@
},
{
"entity": "sensor.martin_handy_alarm_sensor",
"format": "relative",
"name": "Wecker"
}
],
@ -411,17 +396,17 @@
{
"color": "#f07a1d",
"name": "Orange",
"scene": "scene.arbeitszimmer_martin_orange"
"scene": "scene.arbeitszimmer_orange"
},
{
"color": "#2eacd7",
"name": "Blau",
"scene": "scene.arbeitszimmer_martin_blau_grun"
"scene": "scene.arbeitszimmer_blau_grun"
},
{
"color": "#fff",
"name": "Hell",
"scene": "scene.arbeitszimmer_martin_hell"
"scene": "scene.arbeitszimmer_hell"
}
],
"type": "custom:room-glance-card"
@ -458,6 +443,11 @@
{
"entity": "sensor.arbeitszimmer_martin_bewegungsmelder_helligkeit",
"name": "Helligkeit"
},
{
"entity": "switch.mobile_steckdose_schalter_1",
"icon": "mdi:palm-tree",
"name": "Palmenlicht"
}
],
"show_header_toggle": false,
@ -504,9 +494,9 @@
},
{
"entities": [
"switch.bewegungsmelder_west_led",
"switch.bewegungsmelder_ost_led",
"switch.bewegungsmelder_mitte_led"
"light.bewegungsmelder_west_led",
"light.bewegungsmelder_ost_led",
"light.bewegungsmelder_mitte_led"
],
"title": "Bewegungsmelder LEDs",
"type": "entities"
@ -719,6 +709,17 @@
"show_header_toggle": false,
"title": "Temperatur",
"type": "entities"
},
{
"entities": [
"binary_sensor.rauchmelder_gang_oben_sensor",
"binary_sensor.rauchmelder_gang_sensor",
"binary_sensor.rauchmelder_schlafzimmer_sensor",
"binary_sensor.rauchmelder_wohnzimmer_sensor"
],
"show_header_toggle": false,
"title": "Rauchmelder",
"type": "entities"
}
],
"icon": "mdi:more",

View File

@ -0,0 +1,116 @@
{
"data": {
"config": {
"title": "Admin",
"views": [
{
"badges": [],
"cards": [
{
"entities": [
{
"entities": [
{
"icon": "mdi:twitter-retweet",
"tap_action": {
"action": "call-service",
"confirmation": "Wirklich neu starten?",
"service": "sysdweb.restart",
"service_data": {
"hostname": "kitchenpi.fritz.box",
"service_name": "lircd"
}
}
}
],
"entity": "binary_sensor.sysdweb_kitchenpi_lircd",
"icon": "mdi:remote",
"name": "lirc",
"secondary_info": "last-changed",
"type": "custom:multiple-entity-row"
},
{
"entities": [
{
"icon": "mdi:twitter-retweet",
"tap_action": {
"action": "call-service",
"confirmation": "Wirklich neu starten?",
"service": "sysdweb.restart",
"service_data": {
"hostname": "kitchenpi.fritz.box",
"service_name": "squeezelite"
}
}
}
],
"entity": "binary_sensor.sysdweb_kitchenpi_squeezelite",
"icon": "mdi:music",
"name": "squeezelite",
"secondary_info": "last-changed",
"type": "custom:multiple-entity-row"
},
{
"entities": [
{
"icon": "mdi:twitter-retweet",
"tap_action": {
"action": "call-service",
"confirmation": "Wirklich neu starten?",
"service": "sysdweb.restart",
"service_data": {
"hostname": "kitchenpi.fritz.box",
"service_name": "shairport-sync"
}
}
}
],
"entity": "binary_sensor.sysdweb_kitchenpi_shairport_sync",
"icon": "mdi:tablet",
"name": "shairport-sync",
"secondary_info": "last-changed",
"type": "custom:multiple-entity-row"
},
{
"entities": [
{
"icon": "mdi:twitter-retweet",
"tap_action": {
"action": "call-service",
"confirmation": "Wirklich neu starten?",
"service": "sysdweb.restart",
"service_data": {
"hostname": "kitchenpi.fritz.box",
"service_name": "dht22_sensing"
}
}
}
],
"entity": "binary_sensor.sysdweb_kitchenpi_dht22_sensing",
"icon": "mdi:thermometer",
"name": "dht22_sensing",
"secondary_info": "last-changed",
"type": "custom:multiple-entity-row"
}
],
"icon": "mdi:raspberry-pi",
"show_header_toggle": false,
"title": "KitchenPi",
"type": "entities"
}
],
"icon": "mdi:raspberry-pi",
"path": "services",
"title": "Services",
"visible": [
{
"user": "2ffcb10d3db745b396789364fdef72ec"
}
]
}
]
}
},
"key": "lovelace.lovelace_admin",
"version": 1
}

View File

@ -0,0 +1,502 @@
{
"data": {
"config": {
"title": "",
"views": [
{
"badges": [],
"cards": [
{
"aspect_ratio": "16x9",
"entities": [
"group.living_area"
],
"image": "/local/img/living_area_16_9.jpg",
"scenes": [
{
"color": "#f07a1d",
"name": "Orange",
"scene": "scene.wohnbereich_orange"
},
{
"color": "#f2e616",
"name": "Hell",
"scene": "scene.wohnbereich_hell"
},
{
"color": "#0050ff",
"name": "Blau",
"scene": "scene.wohnbereich_blau_grun"
},
{
"color": "#ffffff",
"icon": "mdi:weather-night",
"name": "Kuscheln",
"scene": "scene.wohnbereich_kuscheln"
}
],
"type": "custom:room-glance-card"
},
{
"entities": [
{
"entity": "light.wohnzimmer_deckenlampe",
"name": "Wohnzimmer Decke"
},
{
"entity": "light.esszimmer_deckenlampe_west",
"name": "Esstisch"
},
{
"entity": "light.esszimmer_deckenlampe_mitte",
"name": "Esszimmer Mitte"
},
{
"entity": "light.kuche_deckenlampe",
"name": "K\u00fcche"
},
{
"type": "divider"
},
{
"entity": "cover.wohnzimmer_fenster_rollo",
"name": "Wohnzimmer Fenster",
"type": "custom:state-card-custom-cover"
},
{
"entity": "cover.wohnzimmer_terrassentur_rollo",
"name": "Wohnzimmer T\u00fcr",
"type": "custom:state-card-custom-cover"
},
{
"entity": "cover.kuche_fenster_rollo",
"name": "K\u00fcche",
"type": "custom:state-card-custom-cover"
},
{
"entity": "cover.esszimmer_fenster_rollo",
"name": "Esszimmer",
"type": "custom:state-card-custom-cover"
},
{
"type": "divider"
},
{
"entity": "light.gang_licht",
"name": "Gang Licht"
},
{
"entity": "light.gang_bogen",
"name": "Gang Bogen"
}
],
"show_header_toggle": false,
"type": "entities"
},
{
"entity": "media_player.kitchenpi",
"hide": {
"power_state": false,
"shuffle": false,
"source": true,
"volume": false
},
"idle_view": {
"when_idle": true,
"when_paused": true,
"when_standby": true
},
"shortcuts": {
"columns": 4,
"label": "Playlists",
"list": [
{
"icon": "mdi:alpha-r",
"id": "http://dg-br-http-dus-dtag-cdn.cast.addradio.de/br/b5aktuell/live/mp3/128/stream.mp3?ar-distributor=f0a1",
"name": "B5",
"type": "music"
},
{
"icon": "mdi:alpha-b",
"id": "spotify:playlist:37i9dQZF1DX6G7arXBXa3A",
"name": "BigBand",
"type": "music"
},
{
"icon": "mdi:alpha-j",
"id": "spotify:playlist:37i9dQZF1DX4wta20PHgwo",
"name": "LateNightJazz",
"type": "music"
}
]
},
"type": "custom:mini-media-player"
}
],
"icon": "mdi:sofa",
"title": "Wohnbereich"
},
{
"badges": [],
"cards": [
{
"aspect_ratio": "16x9",
"entities": [
"group.bedroom"
],
"image": "/local/img/bedroom.jpg",
"scenes": [
{
"color": "#eee",
"icon": "mdi:shoe-print",
"name": "Bettlicht Dunkel",
"scene": "scene.schlafzimmer_bettlicht_dunkel"
},
{
"color": "#f07a1d",
"name": "Orange",
"scene": "scene.schlafzimmer_orange"
},
{
"color": "#f35421",
"name": "Rot",
"scene": "scene.schlafzimmer_rot"
},
{
"color": "#fdef56",
"name": "Hell",
"scene": "scene.schlafzimmer_ganz_hell"
},
{
"color": "#2eacd7",
"name": "Blau",
"scene": "scene.schlafzimmer_blau"
}
],
"type": "custom:room-glance-card"
},
{
"artwork": "cover",
"entity": "media_player.bedroompi",
"hide": {
"power_state": false,
"shuffle": false,
"source": true,
"volume": false
},
"icon": "mdi:music",
"shortcuts": {
"buttons": [
{
"icon": "mdi:sleep",
"id": "Good Night",
"type": "music"
},
{
"icon": "mdi:power-sleep",
"id": "Good Night Long",
"type": "music"
},
{
"icon": "mdi:heart",
"id": "Bar Classics",
"type": "music"
},
{
"icon": "mdi:weather-lightning-rainy",
"id": "spotify:playlist:37i9dQZF1DXbcPC6Vvqudd",
"type": "music"
}
],
"columns": 4,
"label": "Playlists",
"list": [
{
"icon": "mdi:waves",
"id": "spotify:playlist:37i9dQZF1DX9if5QDLdzCa",
"name": "Wellen",
"type": "music"
},
{
"icon": "mdi:library-music-outline",
"id": "spotify:playlist:37i9dQZF1DX7heGeZ10YDi",
"name": "Different Sleeping List",
"type": "music"
},
{
"icon": "mdi:saxophone",
"id": "spotify:playlist:37i9dQZF1DX4wta20PHgwo",
"name": "Late NightJazz",
"type": "music"
},
{
"icon": "mdi:account-heart",
"id": "spotify:playlist:2AmjhSAm6iI0qrXXhQyHgk",
"name": "Romantic Jazz",
"type": "music"
},
{
"icon": "mdi:violin",
"id": "spotify:playlist:37i9dQZF1DX8Dd9bxD1WYH",
"name": "Klassik zum Entspannen",
"type": "music"
}
]
},
"type": "custom:mini-media-player"
},
{
"entities": [
{
"entity": "light.schlafzimmer_deckenlampe",
"name": "Deckenlampe"
},
{
"entity": "light.schlafzimmer_fluter",
"name": "Fluter"
},
{
"entity": "light.bett_martin",
"icon": "mdi:sleep",
"name": "Bett Martin"
},
{
"entity": "light.bett_rebecca",
"icon": "mdi:sleep",
"name": "Bett Rebecca"
},
{
"entity": "light.schlafzimmer_schrank",
"icon": "mdi:trello",
"name": "Schrank"
},
{
"entity": "cover.schlafzimmer_rollo_gross",
"name": "Rollo Gro\u00df",
"type": "custom:state-card-custom-cover"
},
{
"entity": "cover.schlafzimmer_rollo_klein",
"name": "Rollo Klein",
"type": "custom:state-card-custom-cover"
},
{
"entity": "automation.wecker_licht_an",
"name": "Martin Wecker Licht an"
},
{
"entity": "sensor.martin_handy_alarm_sensor",
"format": "relative",
"name": "Wecker"
}
],
"show_header_toggle": false,
"type": "entities"
},
{
"card": {
"title": "Lichter die noch an sind",
"type": "entities"
},
"entities": [
{
"entity": "group.hallway",
"icon": "mdi:door-closed",
"name": "Gang"
},
{
"entity": "group.living_area",
"icon": "mdi:sofa",
"name": "Wohnzimmer/Esszimmer"
},
{
"entity": "group.office_martin",
"icon": "mdi:monitor-multiple",
"name": "Martins B\u00fcro"
},
{
"entity": "switch.mobile_steckdose_schalter_1",
"icon": "mdi:palm-tree",
"name": "Palmenlicht"
},
{
"entity": "group.outside",
"icon": "mdi:flower",
"name": "Drau\u00dfen"
},
{
"entity": "switch.mobile_steckdose_schalter_2",
"name": "Balkon"
}
],
"show_empty": false,
"state_filter": [
"on"
],
"type": "entity-filter"
}
],
"icon": "mdi:bed-empty",
"path": "schlafzimmer",
"title": "Schlafzimmer"
},
{
"badges": [],
"cards": [
{
"aspect_ratio": "16x9",
"entities": [
"group.office_martin"
],
"image": "/local/img/office_martin_16_9.jpg",
"name": "Arbeitszimmer",
"scenes": [
{
"color": "#f07a1d",
"name": "Orange",
"scene": "scene.arbeitszimmer_orange"
},
{
"color": "#2eacd7",
"name": "Blau",
"scene": "scene.arbeitszimmer_blau_grun"
},
{
"color": "#fff",
"name": "Hell",
"scene": "scene.arbeitszimmer_hell"
}
],
"type": "custom:room-glance-card"
},
{
"entities": [
{
"entity": "light.arbeitszimmer_deckenlampe",
"name": "Deckenlampe"
},
{
"entity": "light.arbeitszimmer_martin",
"name": "Fluter"
},
{
"entity": "light.arbeitszimmer_martin_oben",
"name": "Stehlampe Oben"
},
{
"entity": "light.arbeitszimmer_martin_unten",
"name": "Stehlampe Unten"
},
{
"entity": "cover.arbeitszimmer_martin_rollo",
"name": "Rollo",
"type": "custom:state-card-custom-cover"
},
{
"entity": "binary_sensor.arbeitszimmer_martin_bewegungsmelder_bewegung",
"format": "relative",
"name": "Bewegung",
"secondary_info": "last-changed"
}
],
"show_header_toggle": false,
"type": "entities"
}
],
"icon": "mdi:monitor-multiple",
"path": "arbeitszimmer",
"title": "Arbeitszimmer"
},
{
"badges": [],
"cards": [
{
"aspect_ratio": "16x9",
"entities": [
"group.bedroom"
],
"image": "/local/img/kinderzimmer.jpg",
"scenes": [
{
"color": "#ffffff",
"icon": "mdi:weather-night",
"name": "Wickel nachts",
"scene": "scene.kinderzimmer_wickeln_nachts"
},
{
"color": "#f07a1d",
"name": "Orange",
"scene": "scene.schlafzimmer_orange"
},
{
"color": "#fdef56",
"name": "Hell",
"scene": "scene.schlafzimmer_ganz_hell"
}
],
"type": "custom:room-glance-card"
},
{
"entities": [
{
"entity": "light.kinderzimmer_deckenlampe",
"name": "Deckenlampe"
},
{
"entity": "light.kinderzimmer_fluter",
"name": "Fluter"
},
{
"entity": "cover.arbeitszimmer_rebecca_rollo",
"name": "Rollo Klein",
"type": "custom:state-card-custom-cover"
}
],
"show_header_toggle": false,
"type": "entities"
}
],
"icon": "mdi:star-face",
"panel": false,
"path": "kinderzimmer",
"title": "Kinderzimmer"
},
{
"badges": [],
"cards": [
{
"aspect_ratio": "16x9",
"entities": [
"group.bedroom"
],
"image": "/local/img/bathroom2.jpg",
"scenes": [
{
"color": "#f07a1d",
"name": "Normal",
"scene": "scene.schlafzimmer_orange"
},
{
"color": "#f35421",
"name": "Baden",
"scene": "scene.schlafzimmer_rot"
},
{
"color": "#fdef56",
"name": "Hell",
"scene": "scene.schlafzimmer_ganz_hell"
}
],
"type": "custom:room-glance-card"
}
],
"icon": "mdi:ship-wheel",
"path": "bad-and-co",
"title": "Bad & Co"
}
]
}
},
"key": "lovelace.lovelace_rebecca",
"version": 1
}

View File

@ -0,0 +1,25 @@
{
"data": {
"items": [
{
"icon": "mdi:account",
"id": "lovelace_rebecca",
"mode": "storage",
"require_admin": false,
"show_in_sidebar": true,
"title": "Rebecca",
"url_path": "lovelace-rebecca"
},
{
"id": "lovelace_admin",
"mode": "storage",
"require_admin": true,
"show_in_sidebar": true,
"title": "Admin",
"url_path": "lovelace-admin"
}
]
},
"key": "lovelace_dashboards",
"version": 1
}

View File

@ -0,0 +1,38 @@
{
"data": {
"items": [
{
"id": "2dd231ecf4fa46d1b9ba696f28da7731",
"type": "js",
"url": "/local/custom_ui/state-card-custom-cover.js"
},
{
"id": "526c1c7115cd4bb396cf02a83b884365",
"type": "js",
"url": "/local/custom_ui/toggle-lock-entity-row.js"
},
{
"id": "7a6c43fa1538436da6459843fd217667",
"type": "module",
"url": "/local/custom_ui/mini-graph-card-bundle.js?v=0.4.3"
},
{
"id": "3527ae66772544dfb09c5246ba4882c0",
"type": "module",
"url": "/local/custom_ui/mini-media-player-bundle.js?v=1.5.1"
},
{
"id": "55ef5e325ab444d19e15a1183a97fb35",
"type": "js",
"url": "/local/custom_ui/room-glance-card.js?v=1.0"
},
{
"id": "02ac97946bb44092a5f073752a8b6deb",
"type": "module",
"url": "/local/custom_ui/multiple-entity-row.js?v=3.1.1"
}
]
},
"key": "lovelace_resources",
"version": 1
}

View File

@ -9,7 +9,7 @@ yaml = YAML()
def get_config():
return {
'bedroom': {
'ir_host': 'bedroompi',
'ir_host': 'bedroompi.fritz.box',
'media_player': 'media_player.bedroompi',
'group': 'group.bedroom',
@ -73,7 +73,7 @@ def get_config():
}
},
'living_area': {
'ir_host': 'kitchenpi',
'ir_host': 'kitchenpi.fritz.box',
'media_player': 'media_player.kitchenpi',
'group': 'group.living_area',
@ -97,9 +97,8 @@ def get_config():
}
},
'first_floor_dining_room': {
'ir_host': 'esszimmerradio',
'ir_host': 'esszimmerradio.fritz.box',
'media_player': 'media_player.esszimmer',
'group': 'first_floor',
'mapping': {
'btn_1': '[radio] Bayern 1',
'btn_2': '[radio] Bayern 2',
@ -114,7 +113,25 @@ def get_config():
'btn_0': '[playlist] Weihnachten mit den Wiener Sängerknaben',
'key_numeric_pound': '[playlist] SammlungGeorg',
}
},
'first_floor_living_room': {
'ir_host': 'musikserverwohnzimmeroben.fritz.box',
'media_player': 'media_player.wohnzimmer',
'mapping': {
'btn_1': '[radio] Bayern 1',
'btn_2': '[radio] Bayern 2',
'btn_3': '[radio] BR Heimat',
'btn_4': '[radio] Bayern+',
'btn_5': '[radio] B 5 Aktuell',
'btn_6': '[radio] BR-Klassik',
'btn_7': '[playlist] Gesammelte Weihnachtslieder',
'btn_8': '[playlist] Harmonic Brass Christmas',
'btn_9': '[playlist] German Brass Christmas',
'key_numeric_star': '[playlist] Weihnachten mit den Wiener Sängerknaben',
'btn_0': '[playlist] Weihnachten mit den Wiener Sängerknaben',
'key_numeric_pound': '[playlist] SammlungGeorg',
}
},
}
@ -131,8 +148,6 @@ def split_description(d):
def automation_from_config(ir_description):
ir_host = ir_description['ir_host']
media_player = ir_description['media_player']
group = ir_description['group']
result = []
for key, description in ir_description['mapping'].items():
@ -143,14 +158,17 @@ def automation_from_config(ir_description):
elif isinstance(description, str):
function, value = split_description(description)
if function == 'playlist':
media_player = ir_description['media_player']
action = service('media_player.play_media', media_player,
media_content_id=value, media_content_type='music')
elif function == 'radio':
media_player = ir_description['media_player']
action = service('media_player.play_media', media_player,
media_content_id=value, media_content_type='channel')
elif function == 'scene':
action = service('scene.turn_on', 'scene.' + value)
elif function == 'timed_light_off':
group = ir_description['group']
action = service('light.turn_off', group, transition=str(60 * int(value)))
else:
raise ValueError("Invalid prefix " + function)
@ -277,10 +295,15 @@ def default_music_controls(device_group, ir_host):
def create_rules(folder):
for name, data in get_config().items():
rules = automation_from_config(data)
rules += default_light_controls(data['group'], data['ir_host'])
rules += default_music_controls(data['media_player'], data['ir_host'])
rules = []
rules += automation_from_config(data)
if 'group' in data:
rules += default_shutter_controls(data['group'], data['ir_host'])
rules += default_light_controls(data['group'], data['ir_host'])
if 'media_player' in data:
rules += default_music_controls(data['media_player'], data['ir_host'])
file_name = os.path.join(folder, "ir_" + name + '.yaml')
with open(file_name, 'w') as f:
f.write("# Dont' edit manually! this is generated!\n\n")

View File

@ -57,7 +57,7 @@ class SispmctlHost:
async def update_state(self):
url = f"http://{self.hostname}:{self.port}"
async with self._session.get(url, timeout=20) as resp:
async with self._session.get(url, timeout=3) as resp:
if resp.status == 200:
result = await resp.json()
await self.notify_switches(result)

View File

@ -57,7 +57,13 @@ def find_local_radio_url_by_name(search_name):
if search_name.startswith('http'):
return search_name
for radioId, radio in find_local_radio_url_by_name.stations.items():
# first try: find a station that starts with the search expression
for radio_id, radio in find_local_radio_url_by_name.stations.items():
if radio['name'].lower().startswith(search_name):
return radio['url']
# second try: containment is enough
for radio_id, radio in find_local_radio_url_by_name.stations.items():
if search_name in radio['name'].lower():
return radio['url']

11
scenes/kinderzimmer.yaml Normal file
View File

@ -0,0 +1,11 @@
- name: Kinderzimmer Wickeln nachts
id: kinderzimmer_wickeln_nachts
entities:
light.kinderzimmer_fluter:
state: true
brightness: 1
xy_color:
- 0.608
- 0.371
light.kinderzimmer_deckenlampe:
state: false

View File

@ -0,0 +1,317 @@
((LitElement) => {
const html = LitElement.prototype.html;
const css = LitElement.prototype.css;
class MultipleEntityRow extends LitElement {
static get properties() {
return {
_hass: {},
_config: {},
state: {},
}
}
static get styles() {
return css`
:host {
display: flex;
align-items: center;
}
.flex {
flex: 1;
margin-left: 16px;
display: flex;
justify-content: space-between;
align-items: center;
min-width: 0;
}
.info {
flex: 1 0 60px;
cursor: pointer;
}
.info, .info > * {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.flex ::slotted(*) {
margin-left: 8px;
min-width: 0;
}
.flex ::slotted([slot="secondary"]) {
margin-left: 0;
}
.secondary, ha-relative-time {
display: block;
color: var(--secondary-text-color);
}
state-badge {
flex: 0 0 40px;
cursor: pointer;
}
.entity {
margin-right: 16px;
text-align: center;
cursor: pointer;
}
.entity span {
font-size: 10px;
color: var(--secondary-text-color);
}
.entity:last-of-type {
margin-right: 0;
}
.state {
min-width: 45px;
}
.toggle {
margin-left: 8px;
}
.icon-small {
width: auto;
}`;
}
render() {
return html`
<state-badge
.stateObj="${this.state.stateObj}"
.overrideIcon="${this._config.icon}"
.stateColor="${this._config.state_color}"
@click="${this.onRowClick}">
</state-badge>
<div class="flex">
<div class="info" @click="${this.onRowClick}">
${this.state.name}
<div class="secondary">${this.renderSecondaryInfo()}</div>
</div>
${this.state.entities.map(entity => this.renderEntity(entity))}
${this.state.value ? html`
<div class="state entity" @click="${this.onStateClick}">
${this.stateHeader && html`<span>${this.stateHeader}</span>`}
${this.renderMainState()}
</div>` : null}
</div>`;
}
renderMainState() {
if (this.state.toggle) return html`<div class="toggle">${this.renderToggle(this.state.stateObj)}</div>`;
else if (this._config.format) return this.renderTimestamp(this.state.value, this._config.format);
else return html`<div>${this.state.value}</div>`;
}
renderSecondaryInfo() {
return this.lastChanged
? html`<ha-relative-time datetime="${this.state.stateObj.last_changed}" .hass="${this._hass}"></ha-relative-time>`
: this.state.info && this.state.info.format
? this.renderTimestamp(this.state.info.value, this.state.info.format)
: (this.state.info && `${this.state.info.name ? `${this.state.info.name} ` : ''}${this.state.info.value}`)
}
renderToggle(stateObj) {
return html`<ha-entity-toggle .stateObj="${stateObj}" .hass="${this._hass}"></ha-entity-toggle>`;
}
renderTimestamp(value, format) {
return !['unknown', 'unavailable'].includes(value.toLowerCase())
? html`<hui-timestamp-display .ts=${new Date(value)} .format=${format} .hass=${this._hass}></hui-timestamp-display>`
: html`${value}`;
}
renderIcon(entity) {
return html`<state-badge class="icon-small" .stateObj="${entity.stateObj}" .overrideIcon="${entity.icon}" .stateColor="${entity.state_color}"></state-badge>`;
}
renderEntityValue(entity) {
if (entity.toggle) return this.renderToggle(entity.stateObj);
else if (entity.icon) return this.renderIcon(entity);
else if (entity.format) return this.renderTimestamp(entity.value, entity.format);
else return html`${entity.value}`;
}
renderEntity(entity) {
return entity ? html`
<div class="entity" @click="${entity.onClick}">
<span>${entity.name}</span>
<div>${this.renderEntityValue(entity)}</div>
</div>` : null;
}
setConfig(config) {
if (!config.entity) throw new Error('Please define a main entity.');
if (config.entities) {
config.entities.map(entity => this.checkEntity(entity));
}
this.checkEntity(config.secondary_info);
this.lastChanged = config.secondary_info === 'last-changed';
this.stateHeader = config.state_header !== undefined ? config.state_header : null;
this.onRowClick = (!config.tap_action || config.tap_action.action !== 'none')
? this.moreInfoAction(config.tap_action, config.entity)
: null;
this.onStateClick = this.getAction(config.tap_action, config.entity);
this._config = config;
}
set hass(hass) {
this._hass = hass;
if (hass && this._config) {
const mainStateObj = hass.states[this._config.entity];
if (!mainStateObj) throw new Error(`Entity '${this._config.entity}' does not exist.`);
this.state = {
...this.state,
stateObj: mainStateObj,
name: this.entityName(this._config.name, mainStateObj),
value: this._config.show_state !== false ? this.entityStateValue(mainStateObj, this._config.unit) : null,
toggle: this.checkToggle(this._config, mainStateObj),
entities: this._config.entities ? this._config.entities.map(entity => this.initEntity(entity, mainStateObj)) : [],
info: this.lastChanged ? null :
typeof this._config.secondary_info === 'string'
? {value: this._config.secondary_info}
: this.initEntity(this._config.secondary_info, mainStateObj),
}
}
}
checkEntity(config) {
if (config && typeof config === 'object' && !(config.entity || config.attribute || config.icon)) {
throw new Error(`Entity object requires at least one 'entity', 'attribute' or 'icon'.`);
} else if (config && typeof config === 'string' && config === '') {
throw new Error('Entity ID string must not be blank.');
} else if (config && typeof config !== 'string' && typeof config !== 'object') {
throw new Error('Entity config must be a valid entity ID string or entity object.');
}
}
checkToggle(config, stateObj) {
return config.toggle === true && stateObj.state && !['unknown', 'unavailable'].includes(stateObj.state)
}
initEntity(config, mainStateObj) {
if (!config) return null;
const entity = typeof config === 'string' ? config : config.entity;
const stateObj = entity ? (this._hass && this._hass.states[entity]) : mainStateObj;
if (!stateObj) return {value: this._hass.localize('state.default.unavailable')};
return {
stateObj: stateObj,
name: entity ? this.entityName(config.name, stateObj) : (config.name || null),
value: config.attribute !== undefined
? this.entityAttribute(stateObj, config.attribute, config.unit)
: this.entityStateValue(stateObj, config.unit),
toggle: this.checkToggle(config, stateObj),
icon: config.icon === true ? stateObj.attributes.icon : config.icon,
format: config.format || false,
state_color: config.state_color || false,
onClick: this.getAction(config.tap_action, stateObj.entity_id),
};
}
entityName(name, stateObj) {
if (name === false) return null;
if (name !== undefined) return name;
return stateObj.attributes.friendly_name === undefined
? stateObj.entity_id.substr(stateObj.entity_id.indexOf('.') + 1).replace(/_/g, ' ')
: stateObj.attributes.friendly_name || '';
}
entityAttribute(stateObj, attribute, unit) {
return (attribute in stateObj.attributes)
? `${stateObj.attributes[attribute]}${unit ? ` ${unit}` : ''}`
: this._hass.localize('state.default.unavailable');
}
entityStateValue(stateObj, unit) {
let display;
const domain = stateObj.entity_id.substr(0, stateObj.entity_id.indexOf("."));
if (domain === 'binary_sensor') {
if (stateObj.attributes.device_class) {
display = this._hass.localize(`state.${domain}.${stateObj.attributes.device_class}.${stateObj.state}`);
}
if (!display) {
display = this._hass.localize(`state.${domain}.default.${stateObj.state}`);
}
} else if (unit !== false && (unit || stateObj.attributes.unit_of_measurement) && !['unknown', 'unavailable'].includes(stateObj.state)) {
display = `${stateObj.state} ${unit || stateObj.attributes.unit_of_measurement}`;
} else if (domain === 'zwave') {
display = ['initializing', 'dead'].includes(stateObj.state)
? this._hass.localize(`state.zwave.query_stage.${stateObj.state}`, 'query_stage', stateObj.attributes.query_stage)
: this._hass.localize(`state.zwave.default.${stateObj.state}`);
} else {
display = this._hass.localize(`state.${domain}.${stateObj.state}`);
}
return display ||
this._hass.localize(`state.default.${stateObj.state}`) ||
this._hass.localize(`component.${domain}.state.${stateObj.state}`) ||
stateObj.state;
}
getAction(config, entityId) {
if (config && config.action) {
if (config.action === 'none') {
return null;
}
const confirmation = config.confirmation === true ? 'Are you sure?' : config.confirmation;
if (config.action === 'call-service') {
const [domain, service] = config.service.split(".");
return () => {
if (!confirmation || confirm(confirmation)) {
this._hass.callService(domain, service, config.service_data);
this.forwardHaptic('light');
}
}
}
if (config.action === 'toggle') {
return () => {
if (!confirmation || confirm(confirmation)) {
this._hass.callService('homeassistant', 'toggle', {entity_id: entityId});
this.forwardHaptic('light');
}
}
}
if (config.action === 'url') {
return () => {
if (!confirmation || confirm(confirmation)) {
const win = window.open(config.url_path, '_blank');
if (win) win.focus();
}
}
}
}
return this.moreInfoAction(config, entityId);
}
moreInfoAction(config, entityId) {
return () => this.fireEvent('hass-more-info', (config && config.entity) || entityId);
}
fireEvent(type, entity, options = {}) {
const event = new Event(type, {
bubbles: options.bubbles || true,
cancelable: options.cancelable || true,
composed: options.composed || true,
});
event.detail = {entityId: entity};
this.dispatchEvent(event);
}
forwardHaptic(type) {
const event = new Event("haptic", {bubbles: true, cancelable: false, composed: true});
event.detail = type;
this.dispatchEvent(event);
}
}
customElements.define('multiple-entity-row', MultipleEntityRow);
})(window.LitElement || Object.getPrototypeOf(customElements.get('hui-view')));