From d3c7e18d38c2985a32e43d11a0ddc862f307f8a1 Mon Sep 17 00:00:00 2001 From: Martin Bauer Date: Sun, 3 May 2020 14:41:27 +0000 Subject: [PATCH] Fixes --- .storage/lovelace | 81 +-- .storage/lovelace.lovelace_admin | 116 ++++ .storage/lovelace.lovelace_rebecca | 502 ++++++++++++++++++ .storage/lovelace_dashboards | 25 + .storage/lovelace_resources | 38 ++ config_creation/ir_automations.py | 45 +- custom_components/httpsispmctl/switch.py | 2 +- .../lirc_network/binary_sensor.py | 2 +- custom_components/squeezebox_telnet/radio.py | 8 +- scenes/kinderzimmer.yaml | 11 + www/custom_ui/multiple-entity-row.js | 317 +++++++++++ 11 files changed, 1093 insertions(+), 54 deletions(-) create mode 100644 .storage/lovelace.lovelace_admin create mode 100644 .storage/lovelace.lovelace_rebecca create mode 100644 .storage/lovelace_dashboards create mode 100644 .storage/lovelace_resources create mode 100644 scenes/kinderzimmer.yaml create mode 100644 www/custom_ui/multiple-entity-row.js diff --git a/.storage/lovelace b/.storage/lovelace index a0cda41..7028564 100644 --- a/.storage/lovelace +++ b/.storage/lovelace @@ -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", diff --git a/.storage/lovelace.lovelace_admin b/.storage/lovelace.lovelace_admin new file mode 100644 index 0000000..c71297d --- /dev/null +++ b/.storage/lovelace.lovelace_admin @@ -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 +} \ No newline at end of file diff --git a/.storage/lovelace.lovelace_rebecca b/.storage/lovelace.lovelace_rebecca new file mode 100644 index 0000000..86102e1 --- /dev/null +++ b/.storage/lovelace.lovelace_rebecca @@ -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 +} \ No newline at end of file diff --git a/.storage/lovelace_dashboards b/.storage/lovelace_dashboards new file mode 100644 index 0000000..184d104 --- /dev/null +++ b/.storage/lovelace_dashboards @@ -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 +} \ No newline at end of file diff --git a/.storage/lovelace_resources b/.storage/lovelace_resources new file mode 100644 index 0000000..c67211a --- /dev/null +++ b/.storage/lovelace_resources @@ -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 +} \ No newline at end of file diff --git a/config_creation/ir_automations.py b/config_creation/ir_automations.py index 81de8c7..6e09bef 100644 --- a/config_creation/ir_automations.py +++ b/config_creation/ir_automations.py @@ -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 += default_shutter_controls(data['group'], 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") diff --git a/custom_components/httpsispmctl/switch.py b/custom_components/httpsispmctl/switch.py index 2558d27..dbc8acc 100644 --- a/custom_components/httpsispmctl/switch.py +++ b/custom_components/httpsispmctl/switch.py @@ -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) diff --git a/custom_components/lirc_network/binary_sensor.py b/custom_components/lirc_network/binary_sensor.py index 0845d8e..1d00677 100644 --- a/custom_components/lirc_network/binary_sensor.py +++ b/custom_components/lirc_network/binary_sensor.py @@ -12,4 +12,4 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info= sensor = IsConnectedSensor(connection.get_name() + "_connected") connection.connected_sensor = sensor sensors.append(sensor) - async_add_entities(sensors) + async_add_entities(sensors) \ No newline at end of file diff --git a/custom_components/squeezebox_telnet/radio.py b/custom_components/squeezebox_telnet/radio.py index c0ac28a..a8f1662 100644 --- a/custom_components/squeezebox_telnet/radio.py +++ b/custom_components/squeezebox_telnet/radio.py @@ -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'] diff --git a/scenes/kinderzimmer.yaml b/scenes/kinderzimmer.yaml new file mode 100644 index 0000000..82c91a4 --- /dev/null +++ b/scenes/kinderzimmer.yaml @@ -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 \ No newline at end of file diff --git a/www/custom_ui/multiple-entity-row.js b/www/custom_ui/multiple-entity-row.js new file mode 100644 index 0000000..4f24912 --- /dev/null +++ b/www/custom_ui/multiple-entity-row.js @@ -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` + + +
+
+ ${this.state.name} +
${this.renderSecondaryInfo()}
+
+ ${this.state.entities.map(entity => this.renderEntity(entity))} + ${this.state.value ? html` +
+ ${this.stateHeader && html`${this.stateHeader}`} + ${this.renderMainState()} +
` : null} +
`; + } + + renderMainState() { + if (this.state.toggle) return html`
${this.renderToggle(this.state.stateObj)}
`; + else if (this._config.format) return this.renderTimestamp(this.state.value, this._config.format); + else return html`
${this.state.value}
`; + } + + renderSecondaryInfo() { + return this.lastChanged + ? html`` + : 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``; + } + + renderTimestamp(value, format) { + return !['unknown', 'unavailable'].includes(value.toLowerCase()) + ? html`` + : html`${value}`; + } + + renderIcon(entity) { + return html``; + } + + 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` +
+ ${entity.name} +
${this.renderEntityValue(entity)}
+
` : 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')));