diff --git a/addons/repositories.yaml b/addons/repositories.yaml index 5f8d0e3..5900cd3 100644 --- a/addons/repositories.yaml +++ b/addons/repositories.yaml @@ -22,6 +22,10 @@ HA Add-ons by alexbelgium: maintainer: sguernion slug: 3bff5a27 source: https://github.com/rfxcom2mqtt/hassio-addons +'Home Assistant Add-on: Zigbee2MQTT': + maintainer: Koen Kanters + slug: 45df7312 + source: https://github.com/zigbee2mqtt/hassio-zigbee2mqtt Home Assistant Community Add-ons: maintainer: Franck Nijhof slug: a0d7b954 @@ -34,6 +38,10 @@ JDeath Addons: maintainer: jdeath slug: 2effc9b9 source: https://github.com/jdeath/homeassistant-addons +Music Assistant: + maintainer: Music Assistant + slug: d5369777 + source: https://github.com/music-assistant/home-assistant-addon NSPanel Manager: maintainer: NSPanel Manager Team slug: a5d2b728 diff --git a/config/.HA_VERSION b/config/.HA_VERSION index 39bb573..d20f6c5 100644 --- a/config/.HA_VERSION +++ b/config/.HA_VERSION @@ -1 +1 @@ -2024.5.5 \ No newline at end of file +2024.8.0 \ No newline at end of file diff --git a/config/.shopping_list.json b/config/.shopping_list.json new file mode 100644 index 0000000..6fceea3 --- /dev/null +++ b/config/.shopping_list.json @@ -0,0 +1,222 @@ +[ + { + "name": " 2 flutes", + "id": "767de33c845f4167acc9f1dd523ee3ac", + "complete": true + }, + { + "name": "Torsades panzani", + "id": "d3e86608a82349d297d7cb5058c500e7", + "complete": true + }, + { + "name": "Essui tout", + "id": "0d317248afe54b6d8c4572775360a3b4", + "complete": true + }, + { + "name": "2 Choco", + "id": "0057ba61739a45fa91d31eb84ef7056e", + "complete": true + }, + { + "name": "Dates", + "id": "5b702fc3a47244c7bfce29e27eb6b19f", + "complete": false + }, + { + "name": "Bananes", + "id": "6aa090cb21b141f8a2b37386e5896711", + "complete": false + }, + { + "name": "Abricot", + "id": "f7c78e2d17f446e0a7c862e70328254e", + "complete": true + }, + { + "name": "Beurre", + "id": "8cb3d58cd54f460c99a1ed2b265a3784", + "complete": true + }, + { + "name": "creme dessert", + "id": "c56d75c68e48429593ee586c98c1823a", + "complete": true + }, + { + "name": "Petit pois", + "id": "63e69a4131424922adf82dcde7199d01", + "complete": true + }, + { + "name": "Jus de fruit", + "id": "12f80d83e3ee401ea708b75a662b7088", + "complete": true + }, + { + "name": "allumettes", + "id": "de19c1fac9ce47b3a256fad7038f56e2", + "complete": true + }, + { + "name": "cafe", + "id": "75e54ab01fd7493eaeeace72262db613", + "complete": true + }, + { + "name": "sucre", + "id": "0dbdec86fa474f9e8b6e61917a3b811c", + "complete": true + }, + { + "name": "lait", + "id": "ca9ce2289dca47a382c084f4a4a27637", + "complete": true + }, + { + "name": "Creme vanilles", + "id": "14062f6ec2744523a03151b6d1ce3ff8", + "complete": true + }, + { + "name": "Peches", + "id": "21d7b3d751aa4f6f9a00d9fe2bfb08b1", + "complete": true + }, + { + "name": "Lave vaisselle", + "id": "c26afbde9f984049adb00c4b85ebf9ae", + "complete": true + }, + { + "name": "Sirop citron", + "id": "a3399494241f4f6696a3ec8469f9c179", + "complete": true + }, + { + "name": "Choco", + "id": "a74201251281498384db6944ff27ce59", + "complete": true + }, + { + "name": "Lait", + "id": "78dfd45120b04d798ac63020635b096c", + "complete": true + }, + { + "name": "Bleu", + "id": "6124a137218348009e641c02c7d36d9d", + "complete": true + }, + { + "name": "Blanc", + "id": "2e603522f7dd4d50a31af1acd5986c89", + "complete": true + }, + { + "name": "Vinaigre blanc", + "id": "a4f1a2acde9e4cf29804036b353d8a45", + "complete": true + }, + { + "name": "Grain jaune mouche", + "id": "164ff5e89df14b13bc1e9fc4eccbf859", + "complete": true + }, + { + "name": "eponge", + "id": "7bdaf185b84d408ca1bc510e3f184c8b", + "complete": true + }, + { + "name": "Fromage blanc", + "id": "7eec2511dacc4cd2bc41057ccc46b70c", + "complete": true + }, + { + "name": "fromage bleu", + "id": "f1cbdd4436d648e7b7634a7cf5c2796b", + "complete": true + }, + { + "name": "Sauce salade", + "id": "9ed3ece90d494813acf6326c8c05a150", + "complete": true + }, + { + "name": "Carrote", + "id": "f0f98a87cddc4dea88d9057ad6f59e59", + "complete": true + }, + { + "name": "oignon jaune", + "id": "85d93f5718e64a9ebc68ddb485eeff70", + "complete": true + }, + { + "name": "Carotte surgelé ", + "id": "f8fd9329ee304364a427aa5d05c5ba44", + "complete": true + }, + { + "name": "Bieres", + "id": "1f02899f840e4c179c7e3cee11ca182c", + "complete": true + }, + { + "name": "Yaourt nature", + "id": "8e967d12cebd474bb642f65bc6eafa8a", + "complete": true + }, + { + "name": "Iles flottantes", + "id": "6b3cff6926c14915b9bb1496c2d49a3e", + "complete": true + }, + { + "name": "Aliment poule", + "id": "941e3e0206e04e34924ec1a0bde9a46b", + "complete": true + }, + { + "name": "Cassis", + "id": "71f61483307245e9a05c4085daeff9ab", + "complete": true + }, + { + "name": "Riz", + "id": "a1640819666a404db09009d32494d5e3", + "complete": true + }, + { + "name": "pernod", + "id": "01d581130273469f90a36cb18d5f23a1", + "complete": true + }, + { + "name": "Citron", + "id": "49cff244225c4390896454e3b8e96f77", + "complete": true + }, + { + "name": "Gateaux", + "id": "6c61d1dd626543e99cf1c4b069ca76a1", + "complete": true + }, + { + "name": "Riz sachet", + "id": "6c3a91f180f8404bb6e88b942170d33e", + "complete": true + }, + { + "name": "Cafe", + "id": "c86f4ee4837044a298b0299240bde9bf", + "complete": true + }, + { + "name": "Tomates grappes", + "id": "5f4420e6430a456db1180ba262a26bd8", + "complete": false + } +] \ No newline at end of file diff --git a/config/01capteur/recorder/recorder.yaml b/config/01capteur/recorder/recorder.yaml new file mode 100644 index 0000000..adae98c --- /dev/null +++ b/config/01capteur/recorder/recorder.yaml @@ -0,0 +1,67 @@ +db_url: mysql://homeassistant:homeassistant@core-mariadb/homeassistant?charset=utf8mb4 +purge_keep_days: 30 +auto_purge: true +auto_repack: true +commit_interval: 5 +include: + domains: + - light + - switch + - cover + #entity_globs: + # - sensor* + entities: + - sensor.tac2100_compteur_puissance_active + - sensor.tac2100_compteur_courant + - sensor.ecowitt_tempin + - sensor.select_sql_query + - sensor.ecu_current_power + - sensor.tac2100_solar_puissance_active + - sensor.disjoncteur_domo_z_power + - sensor.tac2100_compteur_energie_active_totale + - sensor.ecu_today_energy + - sensor.ecowitt_temp + - sensor.ecowitt_dewpoint + - sensor.ecowitt_humidity + - sensor.ecowitt_humidityin + - sensor.ecowitt_baromabs + - sensor.ecowitt_dailyrain + - sensor.ecowitt_eventrain + - sensor.ecowitt_hourlyrain + - sensor.ecowitt_monthlyrain + - sensor.ecowitt_rainrate + - sensor.ecowitt_solarradiation + - sensor.ecowitt_totalrain + - sensor.ecowitt_windgust + - sensor.ecowitt_baromrel + - sensor.ecowitt_uv + - sensor.presence_cuisine_motion_state + - sensor.ecowitt_winddir + - sensor.ecowitt_maxdailygust + - sensor.ecowitt_windspeed + - sensor.ecowitt_frostpoint + - sensor.ecowitt_feelslike + - sensor.qualite_air_co2 + - sensor.geiger_wemos_geiger + - sensor.froling_s3_tdeg_fumee + - sensor.froling_s3_tdeg_board + - sensor.froling_s3_tdeg_depart_chauffage + - sensor.froling_s3_tdeg_chaudiere + - sensor.esp8266_tampon_temp_4d756f_temp_retour_chauff + - sensor.froling_s3_tampon_haut + - sensor.froling_s3_tampon_bas + - sensor.esp8266_tampon_temp_4d756f_tampon_milieu + - sensor.river2_battery_level + - sensor.tac2100_compteur_tension + - sensor.energy_pj1203_solar_power_b + - sensor.energy_pj1203_solar_power_a + - sensor.energy_pj1203_solar_current_b + - sensor.energy_pj1203_solar_current_a + - sensor.energy_pj1203_solar_energy_flow_a + - sensor.energy_pj1203_solar_energy_flow_b + - sensor.energy_pj1203_solar_energy_produced_a + - sensor.energy_pj1203_solar_energy_produced_b + - sensor.ecu_current_power + - sensor.dell_5520_battery_dell5520 + - sensor.blitzortung_lightning_counter + - sensor.compteur_eclair_mensuel diff --git a/config/01capteur/recorder/recorder.yaml.old b/config/01capteur/recorder/recorder.yaml.old deleted file mode 100644 index 73f13c8..0000000 --- a/config/01capteur/recorder/recorder.yaml.old +++ /dev/null @@ -1,42 +0,0 @@ -db_url: mysql://homeassistant:homeassistant@core-mariadb/homeassistant?charset=utf8mb4 -purge_keep_days: 30 -auto_purge: true -include: - domains: - - climate - - binary_sensor - - input_boolean - - input_datetime - - input_number - - input_select - - sensor - - switch - - person - - device_tracker - - light -exclude: - domains: - - camera - - zone - - automation - - sun - - weather - - cover - - group - - script - - pool_pump - entity_globs: - - sensor.clock* - - sensor.date* - - sensor.glances* - - sensor.load_*m - - sensor.time* - - sensor.uptime* - - device_tracker.nmap_tracker* - entities: - - camera.front_door - - sensor.memory_free - - sensor.memory_use - - sensor.memory_use_percent - - sensor.processor_use - - weather.openweathermap diff --git a/config/01capteur/sensor/systemmonitor.yaml b/config/01capteur/sensor/systemmonitor.yaml index 7c52ec2..5ad76d1 100644 --- a/config/01capteur/sensor/systemmonitor.yaml +++ b/config/01capteur/sensor/systemmonitor.yaml @@ -1,10 +1,10 @@ -- platform: systemmonitor - resources: - - type: processor_use - # - type: processor_temperature - - type: memory_free - - type: disk_use_percent - - type: disk_use - - type: disk_free - - type: load_5m +#- platform: systemmonitor +#resources: +# - type: processor_use +# - type: processor_temperature +# - type: memory_free +# - type: disk_use_percent +# - type: disk_use +# - type: disk_free +# - type: load_5m diff --git a/config/01capteur/solar/solar_optimizer.yaml b/config/01capteur/solar/solar_optimizer.yaml index 8881c89..15020e3 100644 --- a/config/01capteur/solar/solar_optimizer.yaml +++ b/config/01capteur/solar/solar_optimizer.yaml @@ -1,17 +1,17 @@ # http://10.0.0.2:8123/hacs/repository/643579135 -algorithm: - initial_temp: 1000 - min_temp: 0.1 - cooling_factor: 0.95 - max_iteration_number: 1000 -devices: - - name: "prise_ecran" - entity_id: "switch.prise_ecran" - power_max: 400 - check_usable_template: "{{%if states('sensor.energy_pj1203_energy_flow_b') == 'producing' %}}" - duration_min: 6 - duration_stop_min: 3 - action_mode: "service_call" - service_activation: switch/turn_on" - deactivation_service: "switch/turn_off" +# algorithm: +# initial_temp: 1000 +# min_temp: 0.1 +# cooling_factor: 0.95 +# max_iteration_number: 1000 +# devices: +# - name: "prise_ecran" +# entity_id: "switch.prise_ecran" +# power_max: 400 +# check_usable_template: "{{%if states('sensor.energy_pj1203_energy_flow_b') == 'producing' %}}" +# duration_min: 6 +# duration_stop_min: 3 +# action_mode: "service_call" +# service_activation: switch/turn_on" +# deactivation_service: "switch/turn_off" diff --git a/config/automations.yaml b/config/automations.yaml index a1941e5..037c427 100644 --- a/config/automations.yaml +++ b/config/automations.yaml @@ -3,7 +3,7 @@ trigger: - platform: state entity_id: - - binary_sensor.pir_sensor_2 + - binary_sensor.pir_sensor from: 'off' to: 'on' condition: @@ -206,7 +206,7 @@ service: climate.set_temperature target: entity_id: - - climate.salon_3 + - climate.salon - id: '1584210783163' alias: reglage du thermostat2 description: '' @@ -220,7 +220,7 @@ service: climate.set_temperature target: entity_id: - - climate.salon_3 + - climate.salon - id: '1584210783164' alias: reglage du thermostat3 description: '' @@ -233,7 +233,7 @@ temperature: 17 service: climate.set_temperature target: - entity_id: climate.salon_3 + entity_id: climate.salon - id: '1584210783165' alias: reglage du thermostat4 description: '' @@ -246,7 +246,7 @@ temperature: 18 service: climate.set_temperature target: - entity_id: climate.salon_3 + entity_id: climate.salon - id: light_on_le_matin alias: Lights On le matin trigger: @@ -289,13 +289,14 @@ alias: chauffage ECS trigger: - platform: time - at: '14:30:00' + at: '14:15:00' condition: - condition: time weekday: - - mon + - tue + - thu + - sat - wed - - fri action: - service: switch.turn_off entity_id: switch.sonoff_pow_relay @@ -631,7 +632,7 @@ alias: VMC_auto trigger: - platform: time_pattern - hours: /1 + hours: /2 action: - service: switch.turn_on data: {} @@ -639,7 +640,7 @@ entity_id: switch.sonoff_r2_vmc_relay2 - delay: hours: 0 - minutes: 10 + minutes: 5 seconds: 0 milliseconds: 0 - service: switch.turn_off @@ -732,9 +733,21 @@ state: 'on' action: - service: switch.turn_on + metadata: {} data: {} target: - entity_id: switch.yoga + entity_id: + - switch.yoga_12_screen_yoga12 + - delay: + hours: 1 + minutes: 5 + seconds: 0 + milliseconds: 0 + - service: switch.turn_off + target: + entity_id: + - switch.yoga_12_screen_yoga12 + data: {} mode: single - id: '1647212976229' alias: Extinction dashboard @@ -750,9 +763,10 @@ state: 'on' action: - service: switch.turn_off + metadata: {} data: {} target: - entity_id: switch.yoga + entity_id: switch.yoga_12_screen_yoga12 mode: single - id: '1647213126018' alias: Allumage automatique dashboard2 @@ -769,8 +783,7 @@ action: - service: switch.turn_on data: {} - target: - entity_id: switch.yoga + target: entity_id:switch.yoga_screen mode: single - id: '1661115407918' alias: arret auto ballon 2h @@ -785,7 +798,7 @@ action: - delay: hours: 2 - minutes: 0 + minutes: 40 seconds: 0 milliseconds: 0 - type: turn_off @@ -840,8 +853,8 @@ condition: [] action: - type: turn_on - device_id: 8f339d247995a56da0e236c585fbf5c3 - entity_id: 6e588a63d704d35f4961ccfde37c4c24 + device_id: 674c31ab50a3f54d86dce8d62df9c213 + entity_id: 12b3d25421518da24d45ec0afab9fe0e domain: light mode: single - id: '1673966882487' @@ -850,8 +863,8 @@ trigger: - type: opened platform: device - device_id: e42dfa2a334f4e7ff6fcdf0b3bad679a - entity_id: cef1f7040328449cb4fbb8ce80e937a0 + device_id: 492c9aa1221b038f3d823f7926122baa + entity_id: cf4fdcfa7639d0637055c0b72b682deb domain: binary_sensor condition: [] action: @@ -866,8 +879,8 @@ trigger: - type: not_opened platform: device - device_id: e42dfa2a334f4e7ff6fcdf0b3bad679a - entity_id: cef1f7040328449cb4fbb8ce80e937a0 + device_id: 492c9aa1221b038f3d823f7926122baa + entity_id: cf4fdcfa7639d0637055c0b72b682deb domain: binary_sensor condition: [] action: @@ -880,19 +893,22 @@ alias: notification courant compteur description: '' trigger: - - type: power + - type: value platform: device - device_id: a313df7677d0b61f7bc877c5842ac7fb - entity_id: 3a414ae30ce17744b790fae0155dbf78 + device_id: 8beaa50a01b938e8f9e2a8a8d0149a00 + entity_id: 180fb00e0b05a9fe3e724a7aa904407f domain: sensor - above: 3300 + above: 14 condition: [] action: - - service: tts.google_say + - service: tts.speak + target: + entity_id: tts.google_fr_fr data: - entity_id: media_player.hp_salon - message: Atttention le compteur va sauter + cache: true + media_player_entity_id: media_player.hp_salon language: fr + message: courant compteur trop elevé - service: switch.turn_off data: {} target: @@ -940,7 +956,7 @@ description: '' trigger: - platform: state - entity_id: binary_sensor.pir_sensor_2 + entity_id: binary_sensor.pir_sensor from: 'off' to: 'on' condition: @@ -983,15 +999,14 @@ - light.plafond_cuisine mode: single - id: '1678205963063' - alias: allume toute les lumieres + alias: eteindre toute les lumieres description: '' trigger: - platform: time at: 08:20:00 condition: [] action: - - service: light.turn_on - data: {} + - service: light.turn_off target: entity_id: - light.applique_cuisine @@ -1004,65 +1019,7 @@ - light.lumieres_escaliers - light.lumieres_plafond - light.plafond_cuisine - mode: single -- id: '1681964652926' - alias: easun battery low - description: '' - trigger: - - type: battery_level - platform: device - device_id: 477cc04b209cbec1076e213a0a0a1bdd - entity_id: sensor.battery_soc - domain: sensor - below: 23 - condition: [] - action: - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.charger_source_priority_2 - type: select_option - option: Solar and utility simultaneously - - delay: - hours: 0 - minutes: 0 - seconds: 30 - milliseconds: 0 - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.output_source_priority_2 - type: select_option - option: Utility first - mode: single -- id: '1682268105598' - alias: 'easun passgeen PV ' - description: '' - trigger: - - type: irradiance - platform: device - device_id: ece62aa07124a7fa58b7d8e490850dcc - entity_id: sensor.ecowitt_solarradiation - domain: sensor - above: 50 - condition: - - condition: numeric_state - entity_id: sensor.battery_soc - above: 28 - action: - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.charger_source_priority_2 - type: select_option - option: Solar first - - delay: - hours: 0 - minutes: 0 - seconds: 30 - milliseconds: 0 - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.output_source_priority_2 - type: select_option - option: Solar first + data: {} mode: single - id: '1685394964755' alias: toggle chambre @@ -1080,52 +1037,6 @@ target: entity_id: light.plafond_chambre_z mode: single -- id: '1686068928189' - alias: easun conso load power eleve - description: '' - trigger: - - type: power - platform: device - device_id: 039b70e127d5dadf9db2ff249d45be5c - entity_id: sensor.load_power_3 - domain: sensor - above: 2800 - condition: [] - action: - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.output_source_priority_2 - type: select_option - option: Utility first - - delay: - hours: 0 - minutes: 0 - seconds: 10 - milliseconds: 0 - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.output_source_priority_2 - type: select_option - option: Utility first - mode: single -- id: '1686071509982' - alias: easun conso load power retour normal - description: '' - trigger: - - type: power - platform: device - device_id: 039b70e127d5dadf9db2ff249d45be5c - entity_id: sensor.load_power_3 - domain: sensor - below: 1450 - condition: [] - action: - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.output_source_priority_2 - type: select_option - option: Solar first - mode: single - id: '1686196586553' alias: monter volet arriere description: '' @@ -1240,16 +1151,15 @@ trigger: - platform: device domain: mqtt - device_id: 157ec5a089ec7c168bdf5dee18a3c068 + device_id: bb5d6f72df5f77144822d4aabd8a186a type: action subtype: shake - discovery_id: 0x00158d0005d3f635 action_shake condition: [] action: - service: switch.turn_on data: {} target: - entity_id: switch.tasmota + entity_id: switch.tasmotaaorus - delay: hours: 0 minutes: 0 @@ -1377,7 +1287,7 @@ description: '' trigger: - platform: zone - entity_id: device_tracker.iphonex + entity_id: device_tracker.iphonex_3 zone: zone.home event: leave condition: [] @@ -1396,7 +1306,7 @@ description: '' trigger: - platform: zone - entity_id: device_tracker.iphonex + entity_id: device_tracker.iphonex_3 zone: zone.home event: enter condition: [] @@ -1419,7 +1329,7 @@ - service: switch.turn_on data: {} target: - entity_id: switch.tasmota + entity_id: switch.tasmotaaorus - delay: hours: 0 minutes: 0 @@ -1436,27 +1346,28 @@ trigger: - type: irradiance platform: device - device_id: ece62aa07124a7fa58b7d8e490850dcc - entity_id: 82a2c21dc63253a4a7512559a8234d92 + device_id: 76ceef32d91b8f2d29120e68c81236a0 + entity_id: c6afb5532f85479c5d9e271f23c43682 + domain: sensor + above: 650 + enabled: false + - type: power + platform: device + device_id: 730634dc456fcb953ced57233ce5c8a1 + entity_id: 4b73e39f14680db1a7362a4fa3fbe2a0 domain: sensor above: 300 condition: - - type: is_power - condition: device - device_id: a313df7677d0b61f7bc877c5842ac7fb - entity_id: 3a414ae30ce17744b790fae0155dbf78 - domain: sensor - below: 300 - type: is_battery_level condition: device - device_id: 3012934cdfcc3dc76324be73eab48e9e - entity_id: 6883b5c05e197fe7aa465ad129ad30ed + device_id: 65aee3dac255b68a21ac6bc5c8a730ed + entity_id: 0004050c9e944586d6151c629e4ecc26 domain: sensor - below: 98 + below: 95 action: - type: turn_on - device_id: e0bc07770566e349f57fb6c01a589ed4 - entity_id: 16d753c211622421f689756eecfa2427 + device_id: 94346ef14e94ac7a197c9ebb889b9222 + entity_id: 26bb0ae61d6ca89513be10ce81e07f05 domain: switch - delay: hours: 0 @@ -1464,49 +1375,10 @@ seconds: 30 milliseconds: 0 - type: turn_on - device_id: 3012934cdfcc3dc76324be73eab48e9e - entity_id: fde76fb92fef7074e814f22e8828443c + device_id: 65aee3dac255b68a21ac6bc5c8a730ed + entity_id: 1a69d7ac01996d82d07d819c53c9ee47 domain: switch mode: single -- id: '1690639616100' - alias: easun battery full - description: '' - trigger: - - type: battery_level - platform: device - device_id: 477cc04b209cbec1076e213a0a0a1bdd - entity_id: sensor.battery_soc - domain: sensor - above: 90 - condition: - - type: is_irradiance - condition: device - device_id: ece62aa07124a7fa58b7d8e490850dcc - entity_id: 82a2c21dc63253a4a7512559a8234d92 - domain: sensor - above: 5 - action: - - delay: - hours: 1 - minutes: 0 - seconds: 0 - milliseconds: 0 - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.charger_source_priority_2 - type: select_option - option: Solar only - - delay: - hours: 0 - minutes: 0 - seconds: 10 - milliseconds: 0 - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.output_source_priority_2 - type: select_option - option: Solar/Battery/Utility - mode: single - id: '1690896885121' alias: off aorus description: '' @@ -1526,8 +1398,8 @@ trigger: - platform: device type: turned_on - device_id: 12296fcf4051cff3e333257039c1d27d - entity_id: bd06db8121a920cc1bb72c1d16d4f58a + device_id: 95ffcfad5e490c51076c93f815df109e + entity_id: 68b649d407c097858cda7aa4b6730b6e domain: switch condition: [] action: @@ -1540,11 +1412,13 @@ data: {} target: entity_id: switch.prise_salon_uv_z - - service: tts.google_say + - service: tts.speak + target: + entity_id: tts.google_fr_fr data: - cache: false - entity_id: media_player.hp_salon - message: la séance d'uv est terminée + cache: true + media_player_entity_id: media_player.hp_salon + message: cycle UV terminée language: fr mode: single - id: '1691485011308' @@ -1552,7 +1426,7 @@ description: '' trigger: - platform: zone - entity_id: device_tracker.iphonex + entity_id: device_tracker.iphonex_3 zone: zone.home event: enter condition: [] @@ -1573,7 +1447,9 @@ to: 'off' condition: [] action: - - service: tts.google_say + - service: tts.speak + target: + entity_id: tts.google_fr_fr data: cache: false entity_id: media_player.hp_salon @@ -1584,14 +1460,22 @@ alias: Eclairage hotte cuisine description: '' trigger: - - platform: device - domain: mqtt - device_id: a6d6e5b68e9ed69f82f6bccb330a430f - type: action - subtype: single - discovery_id: 0xa4c1386e85dbf484 action_single + - type: present + platform: device + device_id: ea17ed9cfe006b7c2038562d799e9c50 + entity_id: ace3e9a593a66c3f854b5d5995b03367 + domain: binary_sensor condition: [] action: + - service: switch.toggle + data: {} + target: + entity_id: switch.prise_eclairage_hotte_cuisine + - delay: + hours: 0 + minutes: 0 + seconds: 30 + milliseconds: 0 - service: switch.toggle data: {} target: @@ -1610,27 +1494,8 @@ data: {} target: entity_id: - - switch.h801light_6f9188_h801_restart - - switch.sonoff_4ch_restart_2 - - switch.kc868_a8_d758d0_d758d0_kc868_a8_restart - - switch.nmcuvoletporte_volet_porte_restart - - switch.esp32_4_relays_garage_5a10c8_esp32_4_relays_garage_restart - - switch.esp32_4_relays_garage_5a10c8_esp32_4_relays_garage_restart - - switch.sonoff_4ch_garage_restart - switch.sonoff_r2_vmc_restart - - switch.nmcuvoletarriere1_volet_arriere_restart - - switch.nmcuvoletsalon1_volet_salon_1_restart - - switch.nmcuvoletsalon2_volet_salon_2_restart - switch.eclairage_bois_restart - - switch.sonoff_dressing_restart_2 - - switch.sonoff_escalier_restart_2 - - switch.nmcuvoletchambre1_volet_chambre_1_restart - - switch.volet_chambre_2_restart_nmcuvoletchambre2 - - switch.nmcuvoletcuisine1_volet_cuisine_1_restart - - switch.nmcuvoletcuisine2_volet_cuisine_2_restart - - switch.meuble_dashboard_restart - - switch.wemos_pir_comble1_restart_2 - - switch.geiger_wemos_geiger_restart mode: single - id: '1697147323532' alias: 4switchz_1_cuisine @@ -1638,10 +1503,9 @@ trigger: - platform: device domain: mqtt - device_id: a0783be10b7e41c8758160cb3f3332a2 + device_id: 43d8325013319e1fd0fb56ab8b63ee7c type: action subtype: 1_single - discovery_id: 0xb43a31fffe2667d8 action_1_single condition: [] action: - service: light.toggle @@ -1687,7 +1551,7 @@ description: '' trigger: - platform: zone - entity_id: device_tracker.iphonex + entity_id: device_tracker.iphonex_3 zone: zone.home event: enter condition: [] @@ -1702,7 +1566,7 @@ description: '' trigger: - platform: zone - entity_id: device_tracker.iphonex + entity_id: device_tracker.iphonex_3 zone: zone.home event: enter condition: @@ -1749,43 +1613,14 @@ target: entity_id: tts.google_say mode: single -- id: '1700067438541' - alias: rajouter du bois - description: '' - trigger: - - type: temperature - platform: device - device_id: 26137125cd7042f7127ecf3eed68c699 - entity_id: 544f7121b81fc415b1870af9b35b9fd8 - domain: sensor - below: 110 - condition: - - condition: state - entity_id: sensor.froling_s3_etat_chaudiere - state: '3' - - type: is_temperature - condition: device - device_id: 26137125cd7042f7127ecf3eed68c699 - entity_id: 9ea08059d271eff6c93061b320c0e6ae - domain: sensor - below: 50 - action: - - service: tts.speak - data: - cache: true - media_player_entity_id: media_player.hp_salon - message: ajouter du bois - target: - entity_id: tts.google_say - mode: single - id: '1700428678105' alias: ouverture porte garage description: '' trigger: - type: opened platform: device - device_id: a1197e1c85c0f91b5007c97f2fb044d8 - entity_id: a51ee606044b1a471c3389cae7feb66f + device_id: d8eb425a10e7c2a3b8e88fc11d402998 + entity_id: ad5754e3a68a99af811ecb5e276f6143 domain: binary_sensor condition: - condition: sun @@ -1814,10 +1649,9 @@ trigger: - platform: device domain: mqtt - device_id: a0783be10b7e41c8758160cb3f3332a2 + device_id: 43d8325013319e1fd0fb56ab8b63ee7c type: action - subtype: 4_single - discovery_id: 0xb43a31fffe2667d8 action_4_single + subtype: 1_single condition: [] action: - service: light.toggle @@ -1827,18 +1661,6 @@ entity_id: - light.applique_salon mode: single -- id: '1701539536870' - alias: purge_db - description: '' - trigger: - - platform: state - entity_id: - - input_button.purge_db - condition: [] - action: - - service: script.purge_database - data: {} - mode: single - id: '1701586721944' alias: Notification - Alertes météo description: '' @@ -1893,8 +1715,8 @@ use_blueprint: path: SgtBatten/Stable.yaml input: - camera: camera.terrasse - notify_device: 4924071cde65b291a4b96864ae6e3d82 + camera: camera.terrasse_2 + notify_device: e3db563ac351029614c7c5be832ccff0 base_url: http://maison43.duckdns.org:8123 title: frigate tap_action: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' @@ -1912,53 +1734,15 @@ ''camera.'' ~ camera, ''access_token'')}}' loiter_timer: 114 silence_timer: 92 -- id: '1701725588574' - alias: ajouter du bois - description: '' - trigger: - - type: temperature - platform: device - device_id: 26137125cd7042f7127ecf3eed68c699 - entity_id: 544f7121b81fc415b1870af9b35b9fd8 - domain: sensor - below: 120 - for: - hours: 0 - minutes: 1 - seconds: 0 - condition: - - type: is_not_open - condition: device - device_id: 26137125cd7042f7127ecf3eed68c699 - entity_id: 9114f3f450c4a9992a4ac39bc2370377 - domain: binary_sensor - - type: is_temperature - condition: device - device_id: 26137125cd7042f7127ecf3eed68c699 - entity_id: 9ea08059d271eff6c93061b320c0e6ae - domain: sensor - below: 45 - action: - - service: tts.google_say - data: - cache: false - entity_id: media_player.hp_salon - message: ajouter du bois - language: fr - mode: single - id: '1702411697026' alias: matin allume garage description: '' trigger: - - type: opened + - type: not_opened platform: device - device_id: db5c6c1763d37f1890a2f0b7ef1e0a71 - entity_id: 423657dccf57d3710e3afd3531039621 + device_id: 741300aea40224e552c530469f297524 + entity_id: cb740d88bd6c05323f4ba5e0be607f76 domain: binary_sensor - for: - hours: 0 - minutes: 0 - seconds: 2 condition: - condition: time after: 06:30:00 @@ -1979,11 +1763,11 @@ alias: presence cuisine description: '' trigger: - - type: present - platform: device - device_id: 5f4a2468d39c0264d04f5d7349dd2b34 - entity_id: bc10f7d7ea63a6e94a3db8e18b0ec6fd - domain: binary_sensor + - platform: device + type: changed_states + device_id: ea17ed9cfe006b7c2038562d799e9c50 + entity_id: df189eefa2249aad89ab9abf3f40ad6c + domain: switch condition: - condition: sun before: sunrise @@ -1994,6 +1778,16 @@ target: entity_id: switch.prise_eclairage_hotte_cuisine data: {} + - delay: + hours: 0 + minutes: 0 + seconds: 30 + milliseconds: 0 + - service: switch.turn_off + target: + entity_id: + - switch.prise_eclairage_hotte_cuisine + data: {} mode: single - id: '1704002531297' alias: fin detection cuisine @@ -2001,8 +1795,8 @@ trigger: - type: not_present platform: device - device_id: 5f4a2468d39c0264d04f5d7349dd2b34 - entity_id: bc10f7d7ea63a6e94a3db8e18b0ec6fd + device_id: ea17ed9cfe006b7c2038562d799e9c50 + entity_id: ace3e9a593a66c3f854b5d5995b03367 domain: binary_sensor condition: [] action: @@ -2054,17 +1848,17 @@ trigger: - type: motion platform: device - device_id: b3754c8ee053109a4a37d3673b1f43cb - entity_id: c167b5b31415f51df78e137d49222981 + device_id: 6fe631fa34695cb3d862d2f1f9b8846c + entity_id: 2d2226dd20b75aaa1ce695141cd7d8ca domain: binary_sensor condition: - condition: numeric_state entity_id: sensor.kc868_a8_d758d0_d758d0_bh1750_illuminance below: 10 action: - - type: turn_on - device_id: dd8b56ae02d022db8d8a388fa7ac29c3 - entity_id: 959475d99663bf1db611fb71a6037334 + - type: turn_off + device_id: ba4fb633f91a13531e2fe0453b25d0f2 + entity_id: 5eae92de6bae34276b50678152b3bb08 domain: light - delay: hours: 0 @@ -2072,8 +1866,8 @@ seconds: 0 milliseconds: 0 - type: turn_off - device_id: dd8b56ae02d022db8d8a388fa7ac29c3 - entity_id: 959475d99663bf1db611fb71a6037334 + device_id: ba4fb633f91a13531e2fe0453b25d0f2 + entity_id: 5eae92de6bae34276b50678152b3bb08 domain: light mode: single - id: '1710354523724' @@ -2106,8 +1900,8 @@ trigger: - type: not_opened platform: device - device_id: a1197e1c85c0f91b5007c97f2fb044d8 - entity_id: a51ee606044b1a471c3389cae7feb66f + device_id: d8eb425a10e7c2a3b8e88fc11d402998 + entity_id: ad5754e3a68a99af811ecb5e276f6143 domain: binary_sensor condition: - condition: time @@ -2123,31 +1917,6 @@ - light.lumieres_escaliers - light.lumiere_garage1 mode: single -- id: '1711426426777' - alias: easun y a plus de soleil - description: '' - trigger: - - platform: sun - event: sunset - offset: 0 - condition: [] - action: - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.charger_source_priority_2 - type: select_option - option: Utility first - - delay: - hours: 0 - minutes: 0 - seconds: 30 - milliseconds: 0 - - device_id: 039b70e127d5dadf9db2ff249d45be5c - domain: select - entity_id: select.output_source_priority_2 - type: select_option - option: Utility first - mode: single - id: '1712403053393' alias: si batterye vide description: '' @@ -2226,32 +1995,6 @@ entity_id: 959475d99663bf1db611fb71a6037334 domain: light mode: single -- id: '1714787013186' - alias: automatique purge db - description: '' - trigger: - - platform: time - at: 01:00:00 - condition: [] - action: - - service: automation.trigger - target: - entity_id: automation.purge_db - data: - skip_condition: true - mode: single -- id: '1714980226749' - alias: clear_log - description: '' - trigger: - - platform: state - entity_id: - - input_button.clear_log - condition: [] - action: - - service: script.1714980028797 - data: {} - mode: single - id: '1715171546682' alias: apsystem pas beaucoup soleil description: '' @@ -2303,3 +2046,251 @@ entity_id: a1c5260977db4e2f04bc3182d6effe2e domain: switch mode: single +- id: '1717638094310' + alias: allumer hotte + description: '' + trigger: + - platform: device + domain: mqtt + device_id: 919d9c0cf1d805314bcc290c7b5f4909 + type: action + subtype: single + condition: [] + action: + - type: toggle + device_id: 872ad88f0351b0e4b70f4ad953a6901c + entity_id: 0c0e6199e0a9c0b62b80e68beb311814 + domain: switch + mode: single +- id: '1720289245852' + alias: poussin eteint + description: '' + trigger: + - type: temperature + platform: device + device_id: a144ddcb1e620e174b945fd409792363 + entity_id: 182b0bb07a58480eb8cbe0d37ecdcaa1 + domain: sensor + above: 30 + condition: [] + action: + - type: turn_off + device_id: 1b29e83003f537078e7c02bee7537f5c + entity_id: 81b978a35f1e6bfd9ac6405263a3fff9 + domain: switch + mode: single +- id: '1720289352590' + alias: poussin allume + description: '' + trigger: + - type: temperature + platform: device + device_id: a144ddcb1e620e174b945fd409792363 + entity_id: 182b0bb07a58480eb8cbe0d37ecdcaa1 + domain: sensor + below: 27 + condition: [] + action: + - type: turn_on + device_id: 1b29e83003f537078e7c02bee7537f5c + entity_id: 81b978a35f1e6bfd9ac6405263a3fff9 + domain: switch + - delay: + hours: 0 + minutes: 3 + seconds: 0 + milliseconds: 0 + - type: turn_off + device_id: 1b29e83003f537078e7c02bee7537f5c + entity_id: 81b978a35f1e6bfd9ac6405263a3fff9 + domain: switch + mode: single +- id: '1720425369246' + alias: Poussin 2 + description: '' + trigger: + - platform: time_pattern + minutes: /15 + condition: + - condition: or + conditions: + - type: is_temperature + condition: device + device_id: a144ddcb1e620e174b945fd409792363 + entity_id: 182b0bb07a58480eb8cbe0d37ecdcaa1 + domain: sensor + below: 25 + - condition: template + value_template: '{{ (as_timestamp(now()) - as_timestamp(states.sensor.capteur_temperature_rond_temperature.last_changed)) + > 1200}}' + action: + - type: turn_on + device_id: 1b29e83003f537078e7c02bee7537f5c + entity_id: 81b978a35f1e6bfd9ac6405263a3fff9 + domain: switch + - delay: + hours: 0 + minutes: 2 + seconds: 30 + milliseconds: 0 + - type: turn_off + device_id: 1b29e83003f537078e7c02bee7537f5c + entity_id: 81b978a35f1e6bfd9ac6405263a3fff9 + domain: switch + mode: single +- id: '1721570953699' + alias: ajouter du bois + description: '' + trigger: + - type: temperature + platform: device + device_id: 22a88c6c5984fbf5c76a9fcea0cb5b66 + entity_id: 7526a8175a584ceac4667290dfea8324 + domain: sensor + below: 100 + above: 50 + condition: [] + action: + - service: tts.speak + target: + entity_id: tts.google_fr_fr + data: + cache: true + media_player_entity_id: media_player.hp_salon + message: Ajouter du bois + language: fr + mode: single +- id: '1721578739513' + alias: ecl_bureau_zb_bt_rond4 + description: '' + trigger: + - platform: device + domain: mqtt + device_id: 7b7ec1587c187c08790edbcb24eed62e + type: action + subtype: single + condition: [] + action: + - service: light.toggle + target: + entity_id: light.light_bureau_z + data: {} + mode: single +- id: '1721707887870' + alias: river2 off AC + description: '' + trigger: + - platform: time + at: '23:00:00' + condition: [] + action: + - type: turn_off + device_id: 94346ef14e94ac7a197c9ebb889b9222 + entity_id: 26bb0ae61d6ca89513be10ce81e07f05 + domain: switch + mode: single +- id: '1722012219999' + alias: J arrive ouvre volet cuisine + description: '' + trigger: + - type: opened + platform: device + device_id: d8eb425a10e7c2a3b8e88fc11d402998 + entity_id: ad5754e3a68a99af811ecb5e276f6143 + domain: binary_sensor + condition: + - condition: device + device_id: ad59ac98d5e40b70c1f204c114376119 + domain: cover + entity_id: 5eaa04d80a8ab739f91e4679b142ba33 + type: is_closed + - condition: time + after: '16:00:00' + before: '22:00:00' + action: + - device_id: ad59ac98d5e40b70c1f204c114376119 + domain: cover + entity_id: 5eaa04d80a8ab739f91e4679b142ba33 + type: open + mode: single +- id: '1722919447787' + alias: beaucoup soleil usb charger + description: '' + trigger: + - type: irradiance + platform: device + device_id: 76ceef32d91b8f2d29120e68c81236a0 + entity_id: c6afb5532f85479c5d9e271f23c43682 + domain: sensor + above: 650 + enabled: false + - type: power + platform: device + device_id: 730634dc456fcb953ced57233ce5c8a1 + entity_id: 4b73e39f14680db1a7362a4fa3fbe2a0 + domain: sensor + above: 250 + condition: [] + action: + - type: turn_on + device_id: 526ae0807201ce9279bef6123ec3946f + entity_id: 2ababeccd22cd686354901927ef9bc13 + domain: switch + mode: single +- id: '1722919637707' + alias: chargeur usb off + description: '' + trigger: + - platform: time + at: '23:00:00' + condition: [] + action: + - type: turn_off + device_id: 526ae0807201ce9279bef6123ec3946f + entity_id: 2ababeccd22cd686354901927ef9bc13 + domain: switch + mode: single +- id: '1722920133715' + alias: je part au boulot 2 volet + description: '' + trigger: + - type: not_opened + platform: device + device_id: d8eb425a10e7c2a3b8e88fc11d402998 + entity_id: ad5754e3a68a99af811ecb5e276f6143 + domain: binary_sensor + condition: + - condition: time + after: 06:50:00 + before: 07:10:00 + action: + - device_id: a7ea77e4d13cd39521f02f788bfb2203 + domain: cover + entity_id: 4eee396bb9490b088f0533afe05fdf91 + type: set_position + position: 0 + - device_id: ad59ac98d5e40b70c1f204c114376119 + domain: cover + entity_id: 5eaa04d80a8ab739f91e4679b142ba33 + type: set_position + position: 0 + mode: single +- id: '1722920471124' + alias: je part au boulot 3 dell + description: '' + trigger: + - type: not_opened + platform: device + device_id: d8eb425a10e7c2a3b8e88fc11d402998 + entity_id: ad5754e3a68a99af811ecb5e276f6143 + domain: binary_sensor + condition: + - condition: time + after: 06:50:00 + before: 07:10:00 + action: + - type: turn_on + device_id: d8de9d10770930a54853f2b622f4c314 + entity_id: 32ea2685688656a175fb0cc49a5cafb2 + domain: switch + mode: single diff --git a/config/blueprints/automation/EPMatt/on_off_schedule_state_persistence.yaml b/config/blueprints/automation/EPMatt/on_off_schedule_state_persistence.yaml new file mode 100644 index 0000000..f647c56 --- /dev/null +++ b/config/blueprints/automation/EPMatt/on_off_schedule_state_persistence.yaml @@ -0,0 +1,88 @@ +blueprint: + name: On-Off schedule with state persistence + description: '# On-Off schedule with state persistence + + + A simple on-off schedule, with the addition of state persistence across disruptive + events, making sure the target device is always in the expected state. + + + 📕 Full documentation regarding this blueprint is available [here](https://epmatt.github.io/awesome-ha-blueprints/docs/blueprints/automation/on_off_schedule_state_persistence). + + + 🚀 This blueprint is part of the **[Awesome HA Blueprints](https://epmatt.github.io/awesome-ha-blueprints) + project**. + + + ℹ️ Version 2021.10.26 + + ' + source_url: https://github.com/EPMatt/awesome-ha-blueprints/blob/main/blueprints/automation/on_off_schedule_state_persistence/on_off_schedule_state_persistence.yaml + domain: automation + input: + automation_target: + name: (Required) Automation target + description: The target which the automation will turn on and off based on the + provided schedule. + selector: + target: {} + on_time: + name: (Required) On Time + description: Time when the target should be placed in the on state. + selector: + time: {} + off_time: + name: (Required) Off Time + description: Time when the target should be placed in the off state. + selector: + time: {} + custom_trigger_event: + name: (Optional) Custom Trigger Event + description: A custom event which can trigger the state check (eg. a powercut + event reported by external integrations). + default: '' + selector: + text: {} + trigger_at_homeassistant_startup: + name: (Optional) Trigger at Home Assistant startup + description: Trigger the target state check and enforcement at Home Assistant + startup. + default: false + selector: + boolean: {} +variables: + off_time: !input 'off_time' + on_time: !input 'on_time' + trigger_at_homeassistant_startup: !input 'trigger_at_homeassistant_startup' + time_fmt: '%H:%M:%S' + first_event: '{{ on_time if strptime(on_time,time_fmt).time() < strptime(off_time,time_fmt).time() + else off_time }}' + second_event: '{{ on_time if strptime(on_time,time_fmt).time() >= strptime(off_time,time_fmt).time() + else off_time }}' +mode: single +max_exceeded: silent +trigger: +- platform: time + at: + - !input 'on_time' + - !input 'off_time' +- platform: homeassistant + event: start +- platform: event + event_type: !input 'custom_trigger_event' +condition: +- condition: template + value_template: '{{ trigger.platform!="homeassistant" or trigger_at_homeassistant_startup + }}' +action: +- choose: + - conditions: + - condition: template + value_template: '{{ now().time() >= strptime(first_event,time_fmt).time() and + now().time() < strptime(second_event,time_fmt).time() }}' + sequence: + - service: homeassistant.{{ "turn_on" if first_event == on_time else "turn_off"}} + target: !input 'automation_target' + default: + - service: homeassistant.{{ "turn_on" if second_event == on_time else "turn_off"}} + target: !input 'automation_target' diff --git a/config/blueprints/automation/EvTheFuture/dim_lights_based_on_sun_elevation.yaml b/config/blueprints/automation/EvTheFuture/dim_lights_based_on_sun_elevation.yaml new file mode 100644 index 0000000..8b5c72a --- /dev/null +++ b/config/blueprints/automation/EvTheFuture/dim_lights_based_on_sun_elevation.yaml @@ -0,0 +1,334 @@ +blueprint: + name: Dim lights based on sun elevation + description: Adjust brightness of lights based on the current sun elevation. If + force debug is enabled, you need to execute this automation manually or let Home + Assitant restart before the change take effect. + source_url: https://github.com/EvTheFuture/homeassistant-blueprints/blob/master/blueprints/dim_lights_based_on_sun_elevation.yaml + domain: automation + input: + target_lights: + name: Lights + description: The lights to control the brightness of + selector: + target: + entity: + domain: light + max_brightness: + name: Maximum brightness percent + description: Brightness to set as the maximum brightness + default: 100 + selector: + number: + min: 2.0 + max: 100.0 + unit_of_measurement: '%' + mode: slider + step: 1.0 + min_brightness: + name: Minimum brightnes percent + description: Brightness to set as the minimum brightness + default: 1 + selector: + number: + min: 1.0 + max: 99.0 + unit_of_measurement: '%' + mode: slider + step: 1.0 + reverse: + name: Reverse brightness + description: If checked, light will start dim when sun starts to set (start + elevation value) and will be at full brightness when the elevation has reached + the end elevation value. + default: false + selector: + boolean: {} + allowance: + name: Change Allowance + description: How much can the brightnes be changed without this automation stop + updating the brightness. If set to 0% this automation will stop update the + brightness if the brightness has been changed at all since the last triggering + of this automation. If set to 100% this automation will keep on and update + the brightness even if you have manually changed the brightness to any other + value since the last trigger. + default: 0 + selector: + number: + min: 0.0 + max: 100.0 + unit_of_measurement: '%' + mode: slider + step: 1.0 + turn_on: + name: Turn on lights automatically + description: Turn on lights when sun is setting. + default: false + selector: + boolean: {} + start_elevation_setting: + name: Elevation of the sun to start dim the light when the sun is setting + default: 0 + selector: + number: + min: -60.0 + max: 60.0 + unit_of_measurement: ° + mode: slider + step: 0.5 + end_elevation_setting: + name: Elevation of the sun when the light shall be fully dimmed when the sun + is setting + default: -30 + selector: + number: + min: -60.0 + max: 60.0 + unit_of_measurement: ° + mode: slider + step: 0.5 + turn_off: + name: Turn off lights automatically + description: Turn off lights when sun has risen. + default: false + selector: + boolean: {} + start_elevation_rising: + name: Elevation of the sun to start brighten the light when the sun is rising + default: -8 + selector: + number: + min: -60.0 + max: 60.0 + unit_of_measurement: ° + mode: slider + step: 0.5 + end_elevation_rising: + name: Elevation of the sun when the light shall have max brightness when the + sun is rising + default: 6 + selector: + number: + min: -60.0 + max: 60.0 + unit_of_measurement: ° + mode: slider + step: 0.5 + transition_time: + name: Transition time in seconds between brightness values + default: 0 + selector: + number: + min: 0.0 + max: 5.0 + unit_of_measurement: s + mode: slider + step: 0.25 + debugging: + name: Debug logging + description: 'WARNING: Don''t enable this unless you have activated ''logger'' + in your configuration.yaml file. Turn on debugging of this automation. In + order for this to take effect you need to manually trigger (EXECUTE) this + automation or let Home Assistant restart before debug will be turned on/off.' + default: false + selector: + boolean: {} +variables: + allowance_input: !input 'allowance' + allowance_value: '{{ allowance_input|float * 2.54 }}' + debugging: !input 'debugging' + target_lights: !input 'target_lights' + entity_list: "{%- if target_lights.entity_id is string -%}\n {{ [target_lights.entity_id]\ + \ }}\n{%- else -%}\n {{ target_lights.entity_id }}\n{%- endif -%}" + transition_time: !input 'transition_time' + turn_on: !input 'turn_on' + turn_off: !input 'turn_off' + reverse: !input 'reverse' + start_setting: !input 'start_elevation_setting' + start_rising: !input 'start_elevation_rising' + end_setting: !input 'end_elevation_setting' + end_rising: !input 'end_elevation_rising' + max_brightness_input: !input 'max_brightness' + max_brightness: '{{ max_brightness_input|float }}' + min_brightness_input: !input 'min_brightness' + min_brightness: '{{ min_brightness_input|float }}' + trigger_is_event: '{{ trigger is defined and trigger.platform == ''event'' }}' + skip_event: '{{ trigger_is_event and trigger.event.data.service_data|length > 1 + }}' + affected_entities: "{%- if skip_event -%}\n {{ [] }}\n{%- elif trigger is not defined\ + \ or trigger.platform != 'event' or trigger.event.data.service_data is not defined\ + \ or trigger.event.data.service_data.entity_id is not defined -%}\n {{ entity_list\ + \ }}\n{%- else -%}\n {%- if trigger.event.data.service_data.entity_id is string\ + \ -%}\n {%- set eids = [trigger.event.data.service_data.entity_id] -%}\n {%-\ + \ else -%}\n {%- set eids = trigger.event.data.service_data.entity_id -%}\n\ + \ {%- endif -%}\n {%- set data = namespace(e=[]) -%}\n {%- for e in eids -%}\n\ + \ {%- if e in entity_list -%}\n {%- set data.e = data.e + [e] -%}\n \ + \ {%- endif -%}\n {% endfor %}\n {{ data.e }}\n{%- endif -%}" + current_states: "{%- set data = namespace(e=[]) -%} {%- for e in entity_list -%}\n\ + \ {%- set a = {'entity_id': e, 'state': states(e), 'brightness': state_attr(e,\ + \ 'brightness')} -%}\n {%- set data.e = data.e + [a] -%}\n{%- endfor -%} {{ data.e\ + \ }}" + error_msg: "{%- if start_setting|float <= end_setting|float -%}\n {{ 'Start elevation\ + \ must be greater than end evevation when the sun is setting' }}\n{%- elif start_rising|float\ + \ >= end_rising|float -%}\n {{ 'End elevation must be greater than start evevation\ + \ when the sun is rising' }}\n{%- elif entity_list|length == 0 -%}\n {{ 'No valid\ + \ entites specified or found' }}\n{%- endif -%}" + has_last: "{% if trigger is defined and trigger.platform == 'state' and trigger.from_state.entity_id\ + \ == 'sun.sun' -%}\n {{ True }}\n{% else %}\n {{ False }}\n{% endif %}" + rising: '{{ state_attr(''sun.sun'', ''rising'') }}' + last_rising: '{% if has_last %}{{ trigger.from_state.attributes.rising }}{% else + %}{{ rising }}{% endif %}' + elevation: '{{ state_attr(''sun.sun'', ''elevation'') }}' + last_elevation: '{% if has_last %}{{ trigger.from_state.attributes.elevation }}{% + else %}{{ elevation }}{% endif %}' + force_turn_on: '{{ turn_on and not rising and last_elevation != "" and last_elevation + >= end_setting|float and elevation <= start_setting|float }}' + force_turn_off: '{{ turn_off and rising and last_elevation != "" and last_elevation + <= end_rising|float and elevation >= end_rising|float }}' + max_elevation: '{% if rising %}{{end_rising|float}}{% else %}{{start_setting|float}}{% + endif %}' + min_elevation: '{% if rising %}{{start_rising|float}}{% else %}{{end_setting|float}}{% + endif %}' + last_max_elevation: '{% if last_rising %}{{end_rising|float}}{% else %}{{start_setting|float}}{% + endif %}' + last_min_elevation: '{% if last_rising %}{{start_rising|float}}{% else %}{{end_setting|float}}{% + endif %}' + elevation_range: '{{ max_elevation - min_elevation }}' + last_elevation_range: '{{ last_max_elevation - last_min_elevation }}' + brightness_range: '{{ max_brightness - min_brightness }}' + delta_to_min: '{{ elevation - min_elevation }}' + last_delta_to_min: '{{ last_elevation|float - last_min_elevation }}' + full_percent_raw: '{% if delta_to_min / elevation_range < 0 %}0{% elif delta_to_min + / elevation_range > 1 %}1{% else %}{{delta_to_min / elevation_range}}{% endif + %}' + full_percent: '{% if reverse %}{{1 - full_percent_raw}}{% else %}{{full_percent_raw}}{% + endif %}' + last_full_percent_raw: '{% if last_delta_to_min / elevation_range < 0 %}0{% elif + last_delta_to_min / elevation_range > 1 %}1{% else %}{{last_delta_to_min / elevation_range}}{% + endif %}' + last_full_percent: '{% if reverse %}{{1 - last_full_percent_raw}}{% else %}{{last_full_percent_raw}}{% + endif %}' + brightness_pct: '{{ full_percent * brightness_range + min_brightness }}' + last_brightness_pct: '{{ last_full_percent * brightness_range + min_brightness }}' + brightness: '{{ (brightness_pct * 2.54)|int }}' + last_brightness: '{{ (last_brightness_pct * 2.54)|int }}' + turn_on_entities: "{%- if force_turn_on -%}\n {%- set data = namespace(entities=[])\ + \ -%}\n {%- for e in entity_list -%}\n {%- if not state_attr(e, 'supported_features')|bitwise_and(1)\ + \ -%}\n {%- set data.entities = data.entities + [e] -%}\n {%- endif -%}\n\ + \ {%- endfor -%}\n {{ data.entities }}\n{%- else -%}\n {{ [] }}\n{%- endif\ + \ -%}" + dim_entities: "{%- set data = namespace(entities=[]) -%} {%- for e in entity_list\ + \ -%}\n {%- set current_brightness = state_attr(e, 'brightness') -%}\n {%- set\ + \ is_on = states(e) == 'on' -%}\n {%- set last_changed = (now() - states[e].last_changed)\ + \ -%}\n {%- set can_dim = state_attr(e, 'supported_features')|bitwise_and(1)|bitwise_or(not\ + \ is_on) -%}\n {#\n Set brightness and turn on if\n * Trigger is an event\ + \ to turn on entity and it is currently off\n OR\n * dimming is supported\ + \ by the entity AND light shall be turned on because the sun is setting (force_turn_on)\n\ + \ OR\n * dimming is supported by the entity AND light is ON AND the current\ + \ brightness differ from the new brightness\n AND\n * current brightness\ + \ is equal to last set brightness (has not been changed by the user within the\ + \ allowance)\n #}\n {%- if e in affected_entities -%}\n {%- if trigger_is_event\ + \ and (not is_on or (is_on and last_changed.seconds < 2)) -%}\n {%- set data.entities\ + \ = data.entities + [e] -%}\n {%- elif can_dim and force_turn_on -%}\n \ + \ {%- set data.entities = data.entities + [e] -%}\n {%- elif can_dim and is_on\ + \ and current_brightness != brightness and (current_brightness - last_brightness)|abs\ + \ <= allowance_value -%}\n {%- set data.entities = data.entities + [e] -%}\n\ + \ {%- endif -%}\n {%- endif -%}\n{%- endfor -%} {{ data.entities }}" + turn_off_entities: "{%- if force_turn_off -%}\n {{ entity_list }}\n{%- else -%}\n\ + \ {{ [] }}\n{%- endif -%}" +trigger: +- platform: state + entity_id: sun.sun + attribute: elevation +- platform: event + event_type: call_service + event_data: + domain: light + service: turn_on +- platform: homeassistant + event: start +mode: queued +action: +- choose: + - conditions: + - condition: template + value_template: '{{ debugging and trigger is not defined }}' + sequence: + - service: logger.set_level + data: + homeassistant.components.blueprint.dim_lights_based_on_sun_elevation: DEBUG + - conditions: + - condition: template + value_template: '{{ debugging and trigger.platform == ''homeassistant'' and + trigger.event == ''start'' }}' + sequence: + - service: logger.set_level + data: + homeassistant.components.blueprint.dim_lights_based_on_sun_elevation: DEBUG + default: + - choose: + - conditions: + - condition: template + value_template: '{{ error_msg|length }}' + sequence: + - service: system_log.write + data: + level: error + logger: homeassistant.components.blueprint.dim_lights_based_on_sun_elevation + message: '{{ error_msg }}' + default: + - choose: + - conditions: + - condition: template + value_template: '{{ debugging }}' + sequence: + - service: system_log.write + data: + level: debug + logger: homeassistant.components.blueprint.dim_lights_based_on_sun_elevation + message: " DEBUG:\n skip_event: {{ skip_event }}\n allowance_value: {{\ + \ allowance_value }}\n affected_entities: {{ affected_entities }}\n\n\ + \ elevation: {{ elevation }} ({% if rising %}{{ start_rising ~ ', '\ + \ ~ end_rising }}{% else %}{{ start_setting ~ ', ' ~ end_setting }}{%\ + \ endif %})\n {% if last_elevation != \"\" -%}last elevation: {{ last_elevation\ + \ }}\n{% endif %} new brightness: {{ brightness }}\n {% if last_elevation\ + \ != \"\" -%}last brightness: {{ last_brightness }}\n{% endif %} \n\ + \ current_states: {{ current_states }}\n \n force_turn_on: {{ force_turn_on\ + \ }}\n force_turn_off: {{ force_turn_off }}\n \n entities: {{ entity_list\ + \ }}\n \n turn_on_entities: {{ turn_on_entities }}\n \n dim_entities:\ + \ {{ dim_entities }}\n \n turn_off_entities: {{ turn_off_entities }}\n\ + \ \n {% if trigger is defined %}Triggered by: {{ trigger.platform }}\n\ + {% endif %} {% if trigger is defined and trigger.platform == 'state'\ + \ and trigger.from_state.entity_id == 'sun.sun' -%} from: (elevation:\ + \ {{ trigger.from_state.attributes.elevation }}, azimuth: {{ trigger.from_state.attributes.azimuth\ + \ }})\n to: (elevation: {{ trigger.to_state.attributes.elevation }},\ + \ azimuth: {{ trigger.to_state.attributes.azimuth }})\n {% endif %}\ + \ {% if trigger is defined and trigger.platform == 'event' -%} entity_id:\ + \ {{ trigger.event.data.service_data.entity_id }}\n service_data_length:\ + \ {{ trigger.event.data.service_data|length }}\n complete event data:\ + \ {{ trigger.event.data }}\n {% endif %} " + default: [] + - choose: + - conditions: + - condition: template + value_template: '{{ not skip_event and turn_off_entities|length > 0 }}' + sequence: + - service: light.turn_off + data: + entity_id: '{{ turn_off_entities }}' + - conditions: + - condition: template + value_template: '{{ not skip_event and turn_on_entities|length > 0 }}' + sequence: + - service: light.turn_on + data: + entity_id: '{{ turn_on_entities }}' + - conditions: + - condition: template + value_template: '{{ not skip_event and dim_entities|length > 0 }}' + sequence: + - service: light.turn_on + data: + entity_id: '{{ dim_entities }}' + brightness: '{{ brightness }}' + transition: '{{ transition_time }}' + default: [] diff --git a/config/blueprints/automation/FabienYt/cover_immediate.yaml b/config/blueprints/automation/FabienYt/cover_immediate.yaml new file mode 100644 index 0000000..f99e000 --- /dev/null +++ b/config/blueprints/automation/FabienYt/cover_immediate.yaml @@ -0,0 +1,51 @@ +blueprint: + name: Cover - Immediate conditions + description: 'Version: 1.0.1' + domain: automation + input: + entities_condition: + name: Immediate conditions + description: Select all entities that match your immediate conditions + selector: + entity: + multiple: true + timer: + name: Timer + description: Timer used for remaining suspension time + selector: + entity: + domain: timer + multiple: false + position: + name: Desired roller shutter position + selector: + entity: + domain: input_number + multiple: false + automation: + name: Roller shutter positioning + description: Automation containing roller shutter positioning rules + selector: + entity: + domain: automation + multiple: false + source_url: https://github.com/FabienYt/home-assistant/blob/main/blueprints/automation/FabienYt/cover_immediate.yaml +mode: restart +max_exceeded: silent +trigger: +- platform: state + entity_id: !input entities_condition +action: +- service: input_number.set_value + target: + entity_id: !input position + data: + value: -1 +- service: timer.cancel + target: + entity_id: !input timer +- service: automation.trigger + data: + skip_condition: true + target: + entity_id: !input automation diff --git a/config/blueprints/automation/HASwitchPlate/hasp_Activate_Page_on_Idle.yaml b/config/blueprints/automation/HASwitchPlate/hasp_Activate_Page_on_Idle.yaml new file mode 100644 index 0000000..4c1e92f --- /dev/null +++ b/config/blueprints/automation/HASwitchPlate/hasp_Activate_Page_on_Idle.yaml @@ -0,0 +1,137 @@ +blueprint: + name: HASPone activates a selected page after a specified period of inactivity + description: ' + + ## Blueprint Version: `1.05.00` + + + # Description + + + Activates a selected page after a specified period of inactivity. + + + ## HASPone Page and Button Reference + + + The images below show each available HASPone page along with the layout of available + button objects. + + +
+ + + | Page 0 | Pages 1-3 | Pages 4-5 | + + |--------|-----------|-----------| + + | ![Page 0](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p0_Init_Screen.png) + | ![Pages 1-3](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p1-p3_4buttons.png) + | ![Pages 4-5](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p4-p5_3sliders.png) + | + + + | Page 6 | Page 7 | Page 8 | + + |--------|--------|--------| + + | ![Page 6](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p6_8buttons.png) + | ![Page 7](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p7_12buttons.png) + | ![Page 8](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p8_5buttons+1slider.png) + | + + + | Page 9 | Page 10 | Page 11 | + + |--------|---------|---------| + + | ![Page 9](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p9_9buttons.png) + | ![Page 10](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p10_5buttons.png) + | ![Page 11](https://raw.githubusercontent.com/HASwitchPlate/HASPone/main/images/NextionUI_p11_1button+1slider.png) + + +
+ + ' + domain: automation + input: + haspdevice: + name: HASPone Device + description: Select the HASPone device + selector: + device: + integration: mqtt + manufacturer: HASwitchPlate + model: HASPone v1.0.0 + multiple: false + targetpage: + name: Page to activate + description: Select a destination page for this button to activate. + default: 1 + selector: + number: + min: 1.0 + max: 11.0 + mode: slider + unit_of_measurement: page + step: 1.0 + idletime: + name: Idle Time + description: Idle time in seconds + default: 30 + selector: + number: + min: 5.0 + max: 900.0 + step: 5.0 + mode: slider + unit_of_measurement: seconds + source_url: https://github.com/HASwitchPlate/HASPone/blob/main/Home_Assistant/blueprints/hasp_Activate_Page_on_Idle.yaml +mode: restart +max_exceeded: silent +variables: + haspdevice: !input haspdevice + haspname: "{%- for entity in device_entities(haspdevice) -%}\n {%- if entity|regex_search(\"^sensor\\..+_sensor(?:_\\d+|)$\") + -%}\n {{- entity|regex_replace(find=\"^sensor\\.\", replace=\"\", ignorecase=true)|regex_replace(find=\"_sensor(?:_\\d+|)$\", + replace=\"\", ignorecase=true) -}}\n {%- endif -%}\n{%- endfor -%}" + targetpage: !input targetpage + idletime: !input idletime + pagecommandtopic: '{{ "hasp/" ~ haspname ~ "/command/page" }}' + activepage: "{%- set activepage = namespace() -%} {%- for entity in device_entities(haspdevice) + -%}\n {%- if entity|regex_search(\"^number\\..*_active_page(?:_\\d+|)$\") -%}\n + \ {%- set activepage.entity=entity -%}\n {%- endif -%}\n{%- endfor -%} {{ states(activepage.entity) + | int(default=-1) }}" +trigger_variables: + haspdevice: !input haspdevice + haspname: "{%- for entity in device_entities(haspdevice) -%}\n {%- if entity|regex_search(\"^sensor\\..+_sensor(?:_\\d+|)$\") + -%}\n {{- entity|regex_replace(find=\"^sensor\\.\", replace=\"\", ignorecase=true)|regex_replace(find=\"_sensor(?:_\\d+|)$\", + replace=\"\", ignorecase=true) -}}\n {%- endif -%}\n{%- endfor -%}" + haspsensor: "{%- for entity in device_entities(haspdevice) -%}\n {%- if entity|regex_search(\"^sensor\\..+_sensor(?:_\\d+|)$\") + -%}\n {{ entity }}\n {%- endif -%}\n{%- endfor -%}" + jsontopic: '{{ "hasp/" ~ haspname ~ "/state/json" }}' + targetpage: !input targetpage + pagejsonpayload: '{"event":"page","value":{{targetpage}}}' +trigger: +- platform: mqtt + topic: '{{jsontopic}}' +condition: +- condition: template + value_template: '{{ is_state(haspsensor, ''ON'') }}' +- condition: template + value_template: "{{-\n (trigger.payload_json.event is defined)\nand\n (trigger.payload_json.event + == 'page')\nand\n (trigger.payload_json.value is defined)\nand\n (trigger.payload_json.value + != targetpage)\n-}}" +action: +- delay: + seconds: '{{idletime|int}}' +- condition: template + value_template: "{%- set currentpage = namespace() -%} {%- for entity in device_entities(haspdevice) + -%}\n {%- if entity|regex_search(\"^number\\..*_active_page(?:_\\d+|)$\") -%}\n + \ {%- set currentpage.entity=entity -%}\n {%- endif -%}\n{%- endfor -%} {%- + if states(currentpage.entity) == targetpage -%}\n {{false}}\n{%- else -%}\n {{true}}\n{%- + endif -%}" +- service: mqtt.publish + data: + topic: '{{pagecommandtopic}}' + payload: '{{targetpage}}' + retain: true diff --git a/config/blueprints/automation/Mikkelmoeller/aqara-magic-cube-zha-51-actions.yaml b/config/blueprints/automation/Mikkelmoeller/aqara-magic-cube-zha-51-actions.yaml new file mode 100644 index 0000000..3e24112 --- /dev/null +++ b/config/blueprints/automation/Mikkelmoeller/aqara-magic-cube-zha-51-actions.yaml @@ -0,0 +1,544 @@ +blueprint: + name: Aqara Magic Cube + description: Control anything using Aqara Magic Cube. + domain: automation + input: + remote: + name: Magic Cube + description: Select the Aqara Magic Cube device + selector: + device: + integration: zha + manufacturer: LUMI + flip_90: + name: Flip 90 degrees + description: 'Actions to run when cube flips 90 degrees. + + This cancels all specific 90 degrees functions. + + e.g From side 1 to side 2 will be the same as from side 6 to side 2' + default: false + selector: + boolean: {} + cube_flip_90: + name: Flip cube 90 degrees + description: Action to run when cube flips 90 degrees. This only works if 'Flip + 90 degrees' is toggled + default: [] + selector: + action: {} + flip_180: + name: Flip 180 degrees + description: 'Actions to run when cube flips 180 degrees. + + This cancels all specific 180 degrees functions + + e.g From side 1 to side 4 will be the same as from side 5 to side 2' + default: false + selector: + boolean: {} + cube_flip_180: + name: Flip cube 180 degrees + description: Action to run when cube flips 180 degrees. This only works if 'Flip + 180 degrees' is toggled + default: [] + selector: + action: {} + slide_any_side: + name: Slide any side + description: 'Actions to run when cube slides on any side. + + This cancels all specific ''slide'' functions + + e.g Slide on side 1 will be the same as slide on side 2' + default: false + selector: + boolean: {} + cube_slide_any: + name: Slide cube on any side + description: Action to run when cube slides on any slide. This only works if + 'Slide any side' is toggled + default: [] + selector: + action: {} + knock_any_side: + name: Knock on any side + description: 'Actions to run when knocking cube regardless of the side. + + This cancels all specific ''knock'' functions + + e.g Knock on side 1 will be the same as knocking side 2' + default: false + selector: + boolean: {} + cube_knock_any: + name: Knock cube on any side + description: Action to run when knocking cube on any side. This only works if + 'Knock on any side' is toggled + default: [] + selector: + action: {} + one_to_two: + name: From side 1 to side 2 + description: Action to run when cube goes from side 1 to side 2 + default: [] + selector: + action: {} + one_to_three: + name: From side 1 to side 3 + description: Action to run when cube goes from side 1 to side 3 + default: [] + selector: + action: {} + one_to_four: + name: From side 1 to side 4 + description: Action to run when cube goes from side 1 to side 4 + default: [] + selector: + action: {} + one_to_five: + name: From side 1 to side 5 + description: Action to run when cube goes from side 1 to side 5 + default: [] + selector: + action: {} + one_to_six: + name: From side 1 to side 6 + description: Action to run when cube goes from side 1 to side 6 + default: [] + selector: + action: {} + two_to_one: + name: From side 2 to side 1 + description: Action to run when cube goes from side 2 to side 1 + default: [] + selector: + action: {} + two_to_three: + name: From side 2 to side 3 + description: Action to run when cube goes from side 2 to side 3 + default: [] + selector: + action: {} + two_to_four: + name: From side 2 to side 4 + description: Action to run when cube goes from side 2 to side 4 + default: [] + selector: + action: {} + two_to_five: + name: From side 2 to side 5 + description: Action to run when cube goes from side 2 to side 5 + default: [] + selector: + action: {} + two_to_six: + name: From side 2 to side 6 + description: Action to run when cube goes from side 2 to side 6 + default: [] + selector: + action: {} + three_to_one: + name: From side 3 to side 1 + description: Action to run when cube goes from side 3 to side 1 + default: [] + selector: + action: {} + three_to_two: + name: From side 3 to side 2 + description: Action to run when cube goes from side 3 to side 2 + default: [] + selector: + action: {} + three_to_four: + name: From side 3 to side 4 + description: Action to run when cube goes from side 3 to side 4 + default: [] + selector: + action: {} + three_to_five: + name: From side 3 to side 5 + description: Action to run when cube goes from side 3 to side 5 + default: [] + selector: + action: {} + three_to_six: + name: From side 3 to side 6 + description: Action to run when cube goes from side 3 to side 6 + default: [] + selector: + action: {} + four_to_one: + name: From side 4 to side 1 + description: Action to run when cube goes from side 4 to side 1 + default: [] + selector: + action: {} + four_to_two: + name: From side 4 to side 2 + description: Action to run when cube goes from side 4 to side 2 + default: [] + selector: + action: {} + four_to_three: + name: From side 4 to side 3 + description: Action to run when cube goes from side 4 to side 3 + default: [] + selector: + action: {} + four_to_five: + name: From side 4 to side 5 + description: Action to run when cube goes from side 4 to side 5 + default: [] + selector: + action: {} + four_to_six: + name: From side 4 to side 6 + description: Action to run when cube goes from side 4 to side 6 + default: [] + selector: + action: {} + five_to_one: + name: From side 5 to side 1 + description: Action to run when cube goes from side 5 to side 1 + default: [] + selector: + action: {} + five_to_two: + name: From side 5 to side 2 + description: Action to run when cube goes from side 5 to side 2 + default: [] + selector: + action: {} + five_to_three: + name: From side 5 to side 3 + description: Action to run when cube goes from side 5 to side 3 + default: [] + selector: + action: {} + five_to_four: + name: From side 5 to side 4 + description: Action to run when cube goes from side 5 to side 4 + default: [] + selector: + action: {} + five_to_six: + name: From side 5 to side 6 + description: Action to run when cube goes from side 5 to side 6 + default: [] + selector: + action: {} + six_to_one: + name: From side 6 to side 1 + description: Action to run when cube goes from side 6 to side 1 + default: [] + selector: + action: {} + six_to_two: + name: From side 6 to side 2 + description: Action to run when cube goes from side 6 to side 2 + default: [] + selector: + action: {} + six_to_three: + name: From side 6 to side 3 + description: Action to run when cube goes from side 6 to side 3 + default: [] + selector: + action: {} + six_to_four: + name: From side 6 to side 4 + description: Action to run when cube goes from side 6 to side 4 + default: [] + selector: + action: {} + six_to_five: + name: From side 6 to side 5 + description: Action to run when cube goes from side 6 to side 5 + default: [] + selector: + action: {} + one_to_one: + name: Knock - Side 1 + description: Action to run when knocking on side 1 + default: [] + selector: + action: {} + two_to_two: + name: Knock - Side 2 + description: Action to run when knocking on side 2 + default: [] + selector: + action: {} + three_to_three: + name: Knock - Side 3 + description: Action to run when knocking on side 3 + default: [] + selector: + action: {} + four_to_four: + name: Knock - Side 4 + description: Action to run when knocking on side 4 + default: [] + selector: + action: {} + five_to_five: + name: Knock - Side 5 + description: Action to run when knocking on side 5 + default: [] + selector: + action: {} + six_to_six: + name: Knock - Side 6 + description: Action to run when knocking on side 6 + default: [] + selector: + action: {} + slide_on_one: + name: Slide - Side 1 up + description: Action to run when slides with Side 1 up + default: [] + selector: + action: {} + slide_on_two: + name: Slide - Side 2 up + description: Action to run when slides with Side 2 up + default: [] + selector: + action: {} + slide_on_three: + name: Slide - Side 3 up + description: Action to run when slides with Side 3 up + default: [] + selector: + action: {} + slide_on_four: + name: Slide - Side 4 up + description: Action to run when slides with Side 4 up + default: [] + selector: + action: {} + slide_on_five: + name: Slide - Side 5 up + description: Action to run when slides with Side 5 up + default: [] + selector: + action: {} + slide_on_six: + name: Slide - Side 6 up + description: Action to run when slides with Side 6 up + default: [] + selector: + action: {} + cube_wake: + name: Wake up the cube + description: Action to run when cube wakes up + default: [] + selector: + action: {} + cube_drop: + name: Cube drops + description: Action to run when cube drops + default: [] + selector: + action: {} + cube_shake: + name: Shake cube + description: Action to run when you shake the cube + default: [] + selector: + action: {} + rotate_right: + name: Rotate right + description: Action to run when cube rotates right + default: [] + selector: + action: {} + rotate_left: + name: Rotate left + description: Action to run when cube rotates left + default: [] + selector: + action: {} + source_url: https://community.home-assistant.io/t/aqara-magic-cube-zha-51-actions/270829 +mode: restart +max_exceeded: silent +trigger: +- platform: event + event_type: zha_event + event_data: + device_id: !input 'remote' +action: +- variables: + command: '{{ trigger.event.data.command }}' + value: '{{ trigger.event.data.args.value }}' + flip_degrees: '{{ trigger.event.data.args.flip_degrees }}' + relative_degrees: '{{ trigger.event.data.args.relative_degrees }}' + flip_90: !input 'flip_90' + flip_180: !input 'flip_180' + slide_any_side: !input 'slide_any_side' + knock_any_side: !input 'knock_any_side' + flip90: 64 + flip180: 128 + slide: 256 + knock: 512 + shake: 0 + drop: 3 + activated_face: "\n{% if command == \"slide\" or command == \"knock\" %}\n\n \ + \ {% if trigger.event.data.args.activated_face == 1 %} 1\n\n {% elif trigger.event.data.args.activated_face\ + \ == 2 %} 5\n\n {% elif trigger.event.data.args.activated_face == 3 %} 6\n\n\ + \ {% elif trigger.event.data.args.activated_face == 4 %} 4\n\n {% elif trigger.event.data.args.activated_face\ + \ == 5 %} 2\n\n {% elif trigger.event.data.args.activated_face == 6 %} 3\n\n\ + \ {% endif %}\n\n{% elif command == 'flip' %}\n\n {{ trigger.event.data.args.activated_face\ + \ | int }}\n\n{% endif %}\n" + from_face: "\n{% if command == \"flip\" and flip_degrees == 90 %}\n\n {{ ((value\ + \ - flip90 - (trigger.event.data.args.activated_face - 1)) / 8) + 1 | int }}\n\ + \n{% endif %}\n" +- choose: + - conditions: + - '{{ command == ''rotate_right'' }}' + sequence: !input 'rotate_right' + - conditions: + - '{{ command == ''rotate_left'' }}' + sequence: !input 'rotate_left' + - conditions: + - '{{ command == ''checkin'' }}' + sequence: !input 'cube_wake' + - conditions: + - '{{ value == shake }}' + sequence: !input 'cube_shake' + - conditions: + - '{{ value == drop }}' + sequence: !input 'cube_drop' + - conditions: + - '{{ command == ''knock'' and knock_any_side }}' + sequence: !input 'cube_knock_any' + - conditions: + - '{{ command == ''slide'' and slide_any_side }}' + sequence: !input 'cube_slide_any' + - conditions: + - '{{ flip_degrees == 90 and flip_90 }}' + sequence: !input 'cube_flip_90' + - conditions: + - '{{ flip_degrees == 180 and flip_180 }}' + sequence: !input 'cube_flip_180' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 1 }}' + sequence: + - choose: + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_one' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_one' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_one' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_one' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 2 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_two' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_two' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_two' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_two' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 3 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_three' + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_three' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_three' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_three' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 4 }}' + sequence: + - choose: + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_four' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_four' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_four' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_four' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 5 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_five' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_five' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_five' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_five' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 6 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_six' + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_six' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_six' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_six' + - conditions: + - '{{ value == flip180 + activated_face - 1 }}' + sequence: + - choose: + - conditions: '{{ activated_face == 1 }}' + sequence: !input 'four_to_one' + - conditions: '{{ activated_face == 2 }}' + sequence: !input 'five_to_two' + - conditions: '{{ activated_face == 3 }}' + sequence: !input 'six_to_three' + - conditions: '{{ activated_face == 4 }}' + sequence: !input 'one_to_four' + - conditions: '{{ activated_face == 5 }}' + sequence: !input 'two_to_five' + - conditions: '{{ activated_face == 6 }}' + sequence: !input 'three_to_six' + - conditions: + - '{{ value == knock + activated_face - 1 }}' + sequence: + - choose: + - conditions: '{{ activated_face == 1 }}' + sequence: !input 'one_to_one' + - conditions: '{{ activated_face == 2 }}' + sequence: !input 'two_to_two' + - conditions: '{{ activated_face == 3 }}' + sequence: !input 'three_to_three' + - conditions: '{{ activated_face == 4 }}' + sequence: !input 'four_to_four' + - conditions: '{{ activated_face == 5 }}' + sequence: !input 'five_to_five' + - conditions: '{{ activated_face == 6 }}' + sequence: !input 'six_to_six' + - conditions: + - '{{ value == slide + activated_face - 1 }}' + sequence: + - choose: + - conditions: '{{ activated_face == 1 }}' + sequence: !input 'slide_on_one' + - conditions: '{{ activated_face == 2 }}' + sequence: !input 'slide_on_two' + - conditions: '{{ activated_face == 3 }}' + sequence: !input 'slide_on_three' + - conditions: '{{ activated_face == 4 }}' + sequence: !input 'slide_on_four' + - conditions: '{{ activated_face == 5 }}' + sequence: !input 'slide_on_five' + - conditions: '{{ activated_face == 6 }}' + sequence: !input 'slide_on_six' diff --git a/config/blueprints/automation/SgtBatten/Stable.yaml b/config/blueprints/automation/SgtBatten/Stable.yaml new file mode 100644 index 0000000..2cfc782 --- /dev/null +++ b/config/blueprints/automation/SgtBatten/Stable.yaml @@ -0,0 +1,1122 @@ +blueprint: + name: Frigate Notifications (0.12.0.2) + description: "## Frigate Notifications\n\nThis blueprint will send a notification + to your device when a Frigate event for the selected camera is fired. The notification + will initially include the thumbnail of the detection, but include an actionable + notification allowing you to view the clip and snapshot.\n\nWith this blueprint, + you may send the notification to multiple devices by leaving \"Device\" blank + and instead use a [notification group][1].\n\n### Software Version Requirements\n + \ - Minimum Home Assistant Version: 2022.2\n - Minimum Frigate Version: 0.11.0\n + \ - Minimum Frigate Integration Version: 3.0.0\n - **Note:** “Enable the unauthenticated + notification event proxy” must be ticked during setup\n - An MQTT broker connected + to home assistant and frigate.\n - Minimum iOS Version: 15.0\n\n### Required + entities:\n - Frigate Camera Name\n - Mobile App Device **or** the name of a + Notification Group or TV\n\n### Features:\n - Easily select the camera entity + or mobile device using a drop down menu.\n - Send notifications to an Android + or iOS mobile device or a TV.\n - or a group containing any combination of + the above.\n - Configure the title and message of the notification. \n - Dynamically + handle things like object type, zones and face detection from doubletake.\n - + Automatically handle some common errors like case matching and bad urls etc.\n + \ - Optionally send the notification as a critical alert. (Critical)\n - Optionally + limit the playing of audio for secondary notification updates, and on iOS, customise + the sound. (Alert Once)\n - Choose whether or not to update the notification + with new thumbnails as they become available.\n - Customise the notification + colour and icon.\n - Optionally send a live view to iOS.\n - Configure custom + notification channels on Android.\n - Specify which [zones][2] to be notified + about. (Zone Filter)\n - Choose between enforcing all required zones simultaneously + or any one zone\n - Specify what type of [objects][3] to be notified about. (Object + Filter)\n - Disable notifications if a presence entity or group is \"home\". + (Presence Filter)\n - Limit notifications based on the state of another entity. + (State Filter)\n - Limit notifications to certain hours of the day. (Time Filter)\n + \ - Configure a cooldown for the camera to reduce the number of notifications + when back-to-back events occur.\n - Silence future notifications for a defined + amount of time through actionable notifications. This is helpful in situations + where you know you will be triggering detections for an extended period of time. + i.e. kids playing outside.\n - Set a loitering timer to notify you of stationary + objects that remain for a set period of time.\n - Configure what happens when + you tap the notification (Tap Action)\n - Configure 3 action buttons to open + almost anything (defaults are: View Clip, View Snapshot and Silence New Notifications)\n + \ - Configure the size, transparency, position and duration of TV notifications.\n + \ - Debug option to help troubleshooting\n\n\n[1]: https://companion.home-assistant.io/docs/notifications/notifications-basic#sending-notifications-to-multiple-devices\n[2]: + https://blakeblackshear.github.io/frigate/configuration/cameras#zones\n[3]: https://blakeblackshear.github.io/frigate/configuration/objects\n\n## + Support\nGo to https://github.com/SgtBatten/HA_blueprints to report bugs, request + new features or get support with your configuration.\n" + domain: automation + source_url: https://github.com/SgtBatten/HA_blueprints/blob/main/Frigate Camera + Notifications/Stable + input: + camera: + name: Frigate Camera + description: "Select the camera entity that will trigger notifications. \nIf + you do not see cameras listed in the drop down, check you have the frigate + integration installed.\nNote: The automation relies on this matching your + frigate config (by default it does).\n" + selector: + entity: + integration: frigate + domain: + - camera + device_class: + - camera + multiple: false + notify_device: + name: Mobile Device + description: Select a device that runs the official Home Assistant app to receive + notifications. If you wish to notify a group of devices or and Android/Fire + TV use the field below to override this selection. This can be left blank + in that case + default: '' + selector: + device: + integration: mobile_app + multiple: false + notify_group: + name: Notification Group or Android/Fire TV (Optional) + description: 'The name of the group or individual TV to send notifications to. + + If set, this will override individual devices above. + + + Note: If the group contains both mobile devices and TVs, the TV will not display + the snapshot unless ''TV notifications'' to true, however this will stop android + phones recieving thumbnails. + + ' + default: '' + base_url: + name: Base URL (Optional) + description: "The external url for your Home Assistant instance. \nRecommended + for iOS and required for Android/Fire TV.\n" + default: '' + title: + name: Notification Title (Optional) + description: '# Notification Customisations + + + The title of the notification. + + ' + default: '' + message: + name: Notification Message + description: 'The message of the notification. + + You can use variables such as {{camera_name}} and {{label}} + + e.g A {{ label }} {{ ''is loitering'' if loitering else ''was detected'' }} + on the {{ camera_name }} camera. + + ' + default: A {{ label }} {{ 'is loitering' if loitering else 'was detected' }} + on the {{ camera_name }} camera. + selector: + select: + options: + - label: 'Default: e.g A Person was detected on the Side camera.' + value: A {{ label }} {{ 'is loitering' if loitering else 'was detected' + }} on the {{ camera_name }} camera. + - label: 'Short: e.g Person detected - Side' + value: '{{ label }} detected - {{ camera_name }}' + - label: 'Long: e.g A Person was detected on the Side camera in the + driveway.' + value: A {{ label }} {{ 'is loitering' if loitering else 'was detected' + }} on the {{ camera_name }} camera{{ ' in the ' + entered_zones if entered_zones + else '.'}} + custom_value: true + sort: false + multiple: false + critical: + name: Critical Notification (Optional) + description: 'Send as a critical notification to the mobile device. + + Sometimes notifications (particularly on android) are delayed, this will resolve + this. + + You can choose to limit critical notifications to certain times using a template + (two examples provided but you can enter your own as long as it outputs true + or false) + + ' + default: 'false' + selector: + select: + options: + - 'false' + - 'true' + - '{{''false'' if now().hour in [8,9,10,11,12,13,14,15,16,17,18] else ''true''}}' + - '{{''true'' if is_state(''sun.sun'', ''above_horizon'') else ''false'' + }}' + custom_value: true + sort: false + multiple: false + alert_once: + name: Alert Once (Optional) + description: Only the first notification for each event will play a sound. Updates, + including new thumbnails will be silent. iOS users who use Critical Notifications + above will still hear default critical sounds for updates. + default: false + selector: + boolean: {} + attachment: + name: Attachment + description: 'Choose which image to attach to the notification. + + + Note: TVs will always get sent the snapshot if TV is true + + ' + default: thumbnail + selector: + select: + options: + - label: Thumbnail + value: thumbnail + - label: Snapshot + value: snapshot + custom_value: false + sort: false + multiple: false + update_thumbnail: + name: Update Attachment (Optional) + description: Update the notification if a new "better" image is available. + default: false + selector: + boolean: {} + color: + name: Notification Color (Optional) + description: Set the color of the notification on your mobile device or TV. + default: steelblue + selector: + select: + options: + - steelblue + - grey + - black + - indigo + - green + - red + - cyan + - teal + - amber + - pink + custom_value: false + sort: false + multiple: false + icon: + name: Notification Icon (Optional) + description: Change the icon that displays on the notification. You can enter + a single icon or create a template like the example given in the dropdown. + You must include 'mdi:' in the icon name. + default: mdi:home-assistant + selector: + select: + options: + - mdi:home-assistant + - mdi:cctv + - mdi:{{'account-outline' if label == 'Person' else 'dog' if label == 'Dog' + else 'cat' if label == 'Cat' else 'car' if label == 'Car' else 'home-assistant'}} + custom_value: true + sort: false + multiple: false + sound: + name: Notification Sound - iOS only (Optional) + description: You can specify a soud file on your device that will play for the + notifications. You will need to import the sound file into home assistant. + default: default + selector: + select: + options: + - default + - none + custom_value: true + sort: false + multiple: false + ios_live_view: + name: Live View - iOS only (Optional) + description: Attach a live view to the notification for iOS devices + default: false + selector: + boolean: {} + channel: + name: Notification Channel - Android only (Optional) + description: Create a new channel for notifications to allow custom notification + sounds, vibration patterns and overide of Do Not Disturb mode. Configured + directly on the device. + default: '' + zone_filter: + name: Zone Filter on/off (Optional) + description: '# Filters + + + Enable to only notify if object has entered a zone listed below. + + ' + default: false + selector: + boolean: {} + zones: + name: Required Zones (Optional - Enabled Above) + description: 'Enter the name of one zone at a time. Include underscores as per + your frigate config. + + By default any zone is acceptable. if you desire ALL listed zones to be entered + before getting a notification, enable the multi toggle below. + + + Note: If the text entry is glitching (dissapearing every few seconds) select + one of the examples, then edit in yaml mode. + + ' + default: [] + selector: + select: + options: + - examples + - porch + - front_door + - side + - garden + multiple: true + custom_value: true + sort: false + zone_multi: + name: Multi Zone (Optional) + description: Require all zones specified above to be entered, instead of any + listed zone. Zone Filter must be enabled also. + default: false + selector: + boolean: {} + labels: + name: Object Filter (Optional) + description: 'Enter or select one object at a time. + + + Note: If the text entry is glitching (dissapearing every few seconds) select + one of the examples, then edit in yaml mode. + + ' + default: '' + selector: + select: + options: + - person + - dog + - cat + - car + - package + - bird + multiple: true + custom_value: true + sort: false + presence_filter: + name: Presence Filter (Optional) + description: Only notify if selected presence entity is not "home". + default: '' + selector: + entity: + domain: + - device_tracker + - person + - group + multiple: false + state_filter: + name: State Filter on/off (Optional) + description: Enable the two State Filter settings below. Only notify if selected + entity is in the specified states. + default: false + selector: + boolean: {} + state_entity: + name: State Filter Entity (Optional) + description: Only notify if selected entity is in the below state. You must + enable State Filter above to use this. + default: '' + selector: + entity: {} + state_filter_states: + name: State Filter States (Optional) + description: Enter the states that the above entity must be in, one at a time. + default: [] + selector: + select: + options: [] + multiple: true + custom_value: true + sort: false + disable_times: + name: Time Filter (Optional) + description: Prevent notifications from being sent during the specified hours + default: [] + selector: + select: + multiple: true + options: + - label: 00:00 - 00:59 + value: '0' + - label: 01:00 - 01:59 + value: '1' + - label: 02:00 - 02:59 + value: '2' + - label: 03:00 - 03:59 + value: '3' + - label: 04:00 - 04:59 + value: '4' + - label: 05:00 - 05:59 + value: '5' + - label: 06:00 - 06:59 + value: '6' + - label: 07:00 - 07:59 + value: '7' + - label: 08:00 - 08:59 + value: '8' + - label: 09:00 - 09:59 + value: '9' + - label: 10:00 - 10:59 + value: '10' + - label: 11:00 - 11:59 + value: '11' + - label: 12:00 - 12:59 + value: '12' + - label: 13:00 - 13:59 + value: '13' + - label: 14:00 - 14:59 + value: '14' + - label: 15:00 - 15:59 + value: '15' + - label: 16:00 - 16:59 + value: '16' + - label: 17:00 - 17:59 + value: '17' + - label: 18:00 - 18:59 + value: '18' + - label: 19:00 - 19:59 + value: '19' + - label: 20:00 - 20:59 + value: '20' + - label: 21:00 - 21:59 + value: '21' + - label: 22:00 - 22:59 + value: '22' + - label: 23:00 - 23:59 + value: '23' + custom_value: false + sort: false + cooldown: + name: Cooldown (Optional) + description: Delay before sending another notification for this camera after + the last event. + default: 30 + selector: + number: + max: 300.0 + min: 0.0 + unit_of_measurement: seconds + step: 1.0 + mode: slider + silence_timer: + name: Silence New Object Notifications (Optional) + description: "How long to silence notifications for this camera when requested + as part of the actionable notification. \nNote: This only applies to new objects. + Existing tracked objects will not be affected.\n" + default: 30 + selector: + number: + max: 300.0 + min: 0.0 + unit_of_measurement: minutes + step: 1.0 + mode: slider + loiter_timer: + name: Loitering Notifications (Optional) + description: 'Sends new loitering notification if a stationary object is detected + for longer than the specified time. 0 is off and will not send notifications. + + ' + default: 0 + selector: + number: + max: 300.0 + min: 0.0 + unit_of_measurement: minutes + step: 1.0 + mode: slider + tap_action: + name: Tap Action URL + description: "# Action Buttons and URLs\n\nThe url to open when tapping on the + notification. Some presets are provided, you can also set you own. \n\nThese + 7 options define the text and urls associated with the three action buttons + at the bottom of the notification.\n" + default: '{{base_url}}/api/frigate/notifications/{{id}}/{{camera}}/clip.mp4' + selector: + select: + options: + - label: View Clip + value: '{{base_url}}/api/frigate/notifications/{{id}}/{{camera}}/clip.mp4' + - label: View Snapshot + value: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' + - label: View Stream + value: '{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json[''after''][''camera''].lower()}}?token={{state_attr( + ''camera.'' ~ camera, ''access_token'')}}' + - label: Open Home Assistant (web) + value: '{{base_url}}/lovelace' + - label: Open Home Assistant (app) + value: /lovelace + - label: Open Frigate + value: /ccab4aaf_frigate/dashboard + - label: Open Frigate (Full Access) + value: /ccab4aaf_frigate-fa/dashboard + - label: Open Frigate (proxy) + value: /ccab4aaf_frigate-proxy/dashboard + - label: Open Reolink App (Android) + value: app://com.mcu.reolink + custom_value: true + sort: false + multiple: false + button_1: + name: Action Button 1 Text + description: The text used on the first Action button at the bottom of the notification. + Set the URL below. Default is View Clip + default: View Clip + url_1: + name: Action Button 1 URL + description: Customise what happens when you press the first Action Button. + Select from one of the preconfigured options or enter your own custom URL. + default: '{{base_url}}/api/frigate/notifications/{{id}}/{{camera}}/clip.mp4' + selector: + select: + options: + - label: View Clip + value: '{{base_url}}/api/frigate/notifications/{{id}}/{{camera}}/clip.mp4' + - label: View Snapshot + value: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' + - label: View Stream + value: '{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json[''after''][''camera''].lower()}}?token={{state_attr( + ''camera.'' ~ camera, ''access_token'')}}' + - label: Open Home Assistant + value: '{{base_url}}/lovelace' + custom_value: true + sort: false + multiple: false + button_2: + name: Action Button 2 Text + description: The text used on the second Action button at the bottom of the + notification. Set the URL below. + default: View Snapshot + url_2: + name: Action Button 2 URL + description: Customise what happens when you press the second Action Button. + Select from one of the preconfigured options or enter your own custom URL. + default: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' + selector: + select: + options: + - label: View Clip + value: '{{base_url}}/api/frigate/notifications/{{id}}/{{camera}}/clip.mp4' + - label: View Snapshot + value: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' + - label: View Stream + value: '{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json[''after''][''camera''].lower()}}?token={{state_attr( + ''camera.'' ~ camera, ''access_token'')}}' + - label: Open Home Assistant + value: '{{base_url}}/lovelace' + custom_value: true + sort: false + multiple: false + button_3: + name: Action Button 3 Text + description: The text used on the third Action button at the bottom of the notification. + Set the URL below. + default: Silence New Notifications + url_3: + name: Action Button 3 URL + description: Customise what happens when you press the third Action Button. + Select from one of the preconfigured options or enter your own custom URL." + default: silence-{{ camera }} + selector: + select: + options: + - label: Silence New Notifications + value: silence-{{ camera }} + - label: View Clip + value: '{{base_url}}/api/frigate/notifications/{{id}}/{{camera}}/clip.mp4' + - label: View Snapshot + value: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' + - label: View Stream + value: '{{base_url}}/api/camera_proxy_stream/camera.{{trigger.payload_json[''after''][''camera''].lower()}}?token={{state_attr( + ''camera.'' ~ camera, ''access_token'')}}' + - label: Open Home Assistant + value: '{{base_url}}/lovelace' + custom_value: true + sort: false + multiple: false + tv: + name: TV Notification (Optional) + description: '# TV Notifications + + + Set to true if you are notifying an Android/Fire TV + + Can also be used to prioritise snapshots on the TV over android mobile apps + when notifying a mixed device group. + + Base URL must be set + + + The below settings are for TV notifications only + + ' + default: false + selector: + boolean: {} + tv_position: + name: TV Notification Position (Optional) + description: Set the position of the notification on your TV + default: center + selector: + select: + mode: dropdown + options: + - bottom-right + - bottom-left + - top-right + - top-left + - center + custom_value: false + sort: false + multiple: false + tv_size: + name: TV Notification Size (Optional) + description: Set the size of the notification on your TV. + default: large + selector: + select: + mode: dropdown + options: + - small + - medium + - large + - max + custom_value: false + sort: false + multiple: false + tv_duration: + name: TV Notification Duration (Optional) + description: The duration (in seconds) the notification will display on your + TV. + default: 10 + selector: + number: + max: 300.0 + min: 0.0 + unit_of_measurement: seconds + step: 1.0 + mode: slider + tv_transparency: + name: TV notification Transaparency (Optional) + description: Set the transparency of the notification on your TV. + default: 0% + selector: + select: + mode: dropdown + options: + - 0% + - 25% + - 50% + - 75% + - 100% + custom_value: false + sort: false + multiple: false + tv_interrupt: + name: TV Notification Interrupt (Optional) + description: If set to true the notification is interactive and can be dismissed + or selected to display more details. Depending on the running app (e.g., Netflix), + this may stop playback. + default: false + selector: + boolean: {} + debug: + name: Debug + description: '# DEBUG + + + Enable to send debug messsages to the home assistant logbook. + + ' + default: false + selector: + boolean: {} +mode: parallel +trigger_variables: + input_camera: !input camera + camera: '{{ input_camera | replace(''camera.'', '''') }}' +trigger: +- platform: event + event_type: mobile_app_notification_action + event_data: + action: silence-{{ camera }} + id: silence +- platform: mqtt + topic: frigate/events + payload: '{{ camera }}/new' + value_template: '{{ value_json[''after''][''camera''] | lower | replace(''-'',''_'') + }}/{{ value_json[''type'']}}' + id: frigate-event +variables: + input_camera: !input camera + camera: '{{ input_camera | replace(''camera.'', '''') }}' + camera_name: '{{ camera | replace(''_'', '' '') | title }}' + input_base_url: !input base_url + base_url: '{{ input_base_url.rstrip(''/'')}}' + attachment: !input attachment + critical_input: !input critical + critical: '{{ true if critical_input == ''true'' else false }}' + alert_once: !input alert_once + update_thumbnail: !input update_thumbnail + ios_live_view: !input ios_live_view + group: !input notify_group + group_target: '{{ group | lower | replace(''notify.'', '''') | replace('' '',''_'') + }}' + zone_only: !input zone_filter + input_zones: !input zones + zones: '{{ input_zones | list | lower }}' + zone_multi: !input zone_multi + input_labels: !input labels + labels: '{{ input_labels | list | lower }}' + presence_entity: !input presence_filter + disable_times: !input disable_times + cooldown: !input cooldown + loitering: false + loiter_timer: !input loiter_timer + fps: '{{ states(''sensor.'' + camera + ''_camera_fps'')|int(5) }}' + state_only: !input state_filter + input_entity: !input state_entity + input_states: !input state_filter_states + states_filter: '{{ input_states | list | lower }}' + color: !input color + sound: !input sound + tv: !input tv + tv_position: !input tv_position + tv_size: !input tv_size + tv_duration: !input tv_duration + tv_transparency: !input tv_transparency + tv_interrupt: !input tv_interrupt + debug: !input debug +action: +- choose: + - alias: Silence New Object Notifications + conditions: + - condition: trigger + id: silence + sequence: + - service: automation.turn_off + target: + entity_id: '{{ this.entity_id }}' + data: + stop_actions: false + - delay: + minutes: !input silence_timer + - service: automation.turn_on + target: + entity_id: '{{ this.entity_id }}' + - alias: Frigate Event + conditions: + - condition: trigger + id: frigate-event + - '{{ is_state(this.entity_id, ''on'') }}' + - '{{ not this.attributes.last_triggered or (now() - this.attributes.last_triggered).seconds + > cooldown }}' + - '{{ not disable_times|length or not now().hour in disable_times|map(''int'')|list + }}' + sequence: + - variables: + id: '{{ trigger.payload_json[''after''][''id''] }}' + object: '{{ trigger.payload_json[''after''][''label''] }}' + label: '{{ object | title }}' + initial_home: '{{ presence_entity != '''' and is_state(presence_entity, ''home'') + }}' + initial_entered_zones: '{{ trigger.payload_json[''after''][''entered_zones''] + |lower}}' + zone_multi_filter: '{{zone_only and zone_multi and initial_entered_zones|length + and zones and zones |reject(''in'', initial_entered_zones) |list |length + == 0 }}' + title: !input title + message: !input message + tap_action: !input tap_action + button_1: !input button_1 + button_2: !input button_2 + button_3: !input button_3 + url_1: !input url_1 + url_2: !input url_2 + url_3: !input url_3 + icon: !input icon + channel: !input channel + - alias: 'Debug: write to Log' + choose: + - conditions: + - '{{debug}}' + sequence: + - service: logbook.log + data_template: + name: Frigate Notification + message: "DEBUG: \n Info:\n fps: {{fps}}, \n frigate event id: + {{id}}, \n object (formatted): {{object}} ({{label}}),\n Config: + \n camera(formatted): {{camera}}({{camera_name}}), \n Base URL: + {{base_url}}, \n critical: {{critical}}, \n alert once: {{alert_once}}, + \n Update Thumbnails: {{update_thumbnail}}, \n Target: {{'group + (input/formatted): ' + group + '/' + group_target + ', ' if group else + 'Mobile Device'}}\n cooldown: {{cooldown}}s, \n loiter timer: + {{loiter_timer}}s,\n color: {{color}}, \n sound: {{sound}},\n + \ Title: {{title}}, \n Message: {{message}},\n tap_action: {{tap_action}}, + \n button 1 Text/URL: {{iif(button_1, button_1, 'unset')}} ({{url_1}}), + \n button 2 Text/URL: {{button_2}} ({{url_2}}), \n button 3 Text/URL: + {{button_3}} ({{url_3}}), \n icon: {{icon}}\n tv: {{tv}}, \n tv_position: + {{tv_position}}, \n tv_size: {{tv_size}}, \n tv_duration: {{tv_duration}}, + \n tv_transparency: {{tv_transparency}}, \n tv_interrupt: {{tv_interrupt}}, + \n Filters: \n Zones: \n zone filter toggle on: {{zone_only}}, + \n Multi Zone toggle on: {{zone_multi}}, \n Required zones: + {{input_zones}}, \n Entered Zones: {{initial_entered_zones}}, \n + \ Zone Filter TEST: {{'PASS (Multi)' if zone_multi_filter else 'PASS' + if ( not zone_only or not zone_multi and zones|select('in', initial_entered_zones)|list|length + ) else 'FAIL (Multi)' if zone_multi else 'FAIL' }}, \n Required objects + TEST: \n Input: {{input_labels}}, \n TEST: {{'PASS' if not + labels|length or object in labels else 'FAIL'}}\n presence entity + (not home):\n Entity: {{presence_entity}}\n TEST: {{'PASS' + if not initial_home else 'FAIL'}}, \n disabled times: {{disable_times}}, + \n State Filter: \n state filter toggle on: {{state_only}}, + \n state filter entity: {{input_entity}}, \n required states: + {{input_states}}, \n State Filter TEST: {{'PASS' if not state_only + or states(input_entity) in states_filter else 'FAIL' }},\n" + - alias: Notifications enabled for object label + condition: template + value_template: '{{ not labels|length or object in labels }}' + - alias: Notify on new object + choose: + - conditions: + - '{{ not zone_only or (not zone_multi and zones|select(''in'', initial_entered_zones)|list|length + > 0) or (zone_multi and initial_entered_zones|length > 0 and zones |reject(''in'', + initial_entered_zones) |list |length == 0) }}' + - '{{ not initial_home }}' + - '{{ not state_only or states(input_entity) in states_filter }}' + sequence: + - choose: + - conditions: '{{ not group_target }}' + sequence: + - device_id: !input notify_device + domain: mobile_app + type: notify + title: '{{title}}' + message: '{{message}}' + data: + tag: '{{ id }}' + group: '{{ camera }}-frigate-notification' + color: '{{color}}' + image: /api/frigate/notifications/{{id}}/{{attachment}}.jpg?format=android + clickAction: '{{tap_action}}' + ttl: '{{ iif(critical, 0, 3600000) }}' + priority: '{{ iif(critical, ''high'', ''normal'') }}' + notification_icon: '{{icon}}' + channel: '{{channel}}' + url: '{{tap_action}}' + attachment: + url: /api/frigate/notifications/{{id}}/{{attachment}}.jpg + push: + sound: '{{sound}}' + interruption-level: '{{ iif(critical, ''critical'', ''active'') + }}' + entity_id: '{{ iif(ios_live_view, input_camera, '''' ) }}' + actions: + - action: URI + title: '{{button_1}}' + uri: '{{url_1}}' + - action: URI + title: '{{button_2}}' + uri: '{{url_2}}' + - action: '{{ ''URI'' if ''/'' in url_3 else url_3 }}' + title: '{{button_3}}' + uri: '{{url_3}}' + destructive: true + - conditions: '{{ tv }}' + sequence: + - service: notify.{{ group_target }} + data: + title: '{{title}}' + message: '{{message}}' + data: + tag: '{{ id }}' + group: '{{ camera }}-frigate-notification' + color: '{{color}}' + clickAction: '{{tap_action}}' + ttl: '{{ iif(critical, 0, 3600000) }}' + priority: '{{ iif(critical, ''high'', ''normal'') }}' + notification_icon: '{{icon}}' + channel: '{{channel}}' + image: + url: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' + fontsize: '{{tv_size}}' + position: '{{tv_position}}' + duration: '{{tv_duration}}' + transparency: '{{tv_transparency}}' + interrupt: '{{tv_interrupt}}' + timeout: 30 + url: '{{tap_action}}' + attachment: + url: /api/frigate/notifications/{{id}}/{{attachment}}.jpg + push: + sound: '{{sound}}' + interruption-level: '{{ iif(critical, ''critical'', ''active'') + }}' + entity_id: '{{ iif(ios_live_view, input_camera, '''' ) }}' + actions: + - action: URI + title: '{{button_1}}' + uri: '{{url_1}}' + - action: URI + title: '{{button_2}}' + uri: '{{url_2}}' + - action: '{{ ''URI'' if ''/'' in url_3 else url_3 }}' + title: '{{button_3}}' + uri: '{{url_3}}' + destructive: true + default: + - service: notify.{{ group_target }} + data: + title: '{{title}}' + message: '{{message}}' + data: + tag: '{{ id }}{{''-loitering'' if loitering}}' + group: '{{ camera }}-frigate-notification{{''-loitering'' if loitering}}' + color: '{{color}}' + image: /api/frigate/notifications/{{id}}/{{attachment}}.jpg?format=android + clickAction: '{{tap_action}}' + ttl: '{{ iif(critical, 0, 3600000) }}' + priority: '{{ iif(critical, ''high'', ''normal'') }}' + notification_icon: '{{icon}}' + channel: '{{channel}}' + fontsize: '{{tv_size}}' + position: '{{tv_position}}' + duration: '{{tv_duration}}' + transparency: '{{tv_transparency}}' + interrupt: '{{tv_interrupt}}' + url: '{{tap_action}}' + attachment: + url: /api/frigate/notifications/{{id}}/{{attachment}}.jpg + push: + sound: '{{sound}}' + interruption-level: '{{ iif(critical, ''critical'', ''active'') + }}' + entity_id: '{{ iif(ios_live_view, input_camera, '''' ) }}' + actions: + - action: URI + title: '{{button_1}}' + uri: '{{url_1}}' + - action: URI + title: '{{button_2}}' + uri: '{{url_2}}' + - action: '{{ ''URI'' if ''/'' in url_3 else url_3 }}' + title: '{{button_3}}' + uri: '{{url_3}}' + destructive: true + - repeat: + sequence: + - wait_for_trigger: + - platform: mqtt + topic: frigate/events + payload: '{{ id }}' + value_template: '{{ value_json[''after''][''id''] }}' + timeout: + minutes: 2 + continue_on_timeout: false + - variables: + event: '{{ wait.trigger.payload_json }}' + loitering: '{{ loiter_timer and event[''before''][''motionless_count'']/fps/60 + < loiter_timer and event[''after''][''motionless_count'']/fps/60 >= + loiter_timer }}' + new_snapshot: '{{ update_thumbnail and event[''before''][''snapshot_time''] + != event[''after''][''snapshot_time''] }}' + home: '{{ presence_entity != '''' and is_state(presence_entity, ''home'') + }}' + presence_changed: '{{ presence_entity != '''' and as_datetime(event[''before''][''frame_time'']) + < states[presence_entity].last_changed }}' + last_zones: '{{ event[''before''][''entered_zones''] |lower}}' + entered_zones: '{{ event[''after''][''entered_zones''] |lower}}' + zone_filter: '{{ not zone_only or zones|select(''in'', entered_zones)|list|length + > 0 }}' + zone_multi_filter: '{{not zone_only or not zone_multi or ( entered_zones|list|length + > 0 and zones and zones|reject(''in'', entered_zones)|list|length == + 0 ) }}' + stationary_moved: '{{ event[''after''][''position_changes''] > event[''before''][''position_changes''] + }}' + zone_only_changed: '{{ zone_only and (entered_zones|length > 0 and not + last_zones|length) }}' + entered_zones_changed: '{{ zones|length > 0 and (zones|select(''in'', + entered_zones)|list|length > 0 and not zones|select(''in'', last_zones)|list|length) + }}' + state_true: '{{ not state_only or states(input_entity) in states_filter + }}' + sub_label: '{{ event[''after''][''sub_label'']}}' + sub_label_changed: '{{ sub_label != event[''before''][''sub_label''] }}' + update: '{{ alert_once or (new_snapshot and not loitering and not presence_changed + and not zone_only_changed and not entered_zones_changed and not sub_label_changed) + }}' + title: "{% if sub_label %} \n {{title | replace('A Person', sub_label|title) + | replace('Person', sub_label|title)}}\n{%else%}\n {{title}}\n{%endif%}\n" + message: "{% if sub_label %} \n {{message | replace('A Person', sub_label|title) + | replace('Person', sub_label|title)}}\n{%else%}\n {{message}}\n{%endif%}\n" + - alias: 'Debug: write to Log' + choose: + - conditions: + - '{{debug}}' + sequence: + - service: logbook.log + data_template: + name: Frigate Notification + message: "DEBUG (in loop): \n Info: \n Last Zones: {{last_zones}}, + \n Current zones: {{entered_zones}}, \n sublabel: {{sub_label}},\n + \ iOS sound: {{update if not critical else 'yes due critical notifications'}}, + \n Android Sound: {{'disabled by alert once' if alert_once else + 'enabled'}}, \n Triggers: \n New Snapshot: {{new_snapshot}}, + \n Presence Changed: {{presence_changed}}, \n stationary moved: + {{stationary_moved}}, \n entered zones changed: {{entered_zones_changed}}, + \n sublabel changed: {{sub_label_changed}}, \n Conditions: \n + \ Loitering: {{loitering}}\n or \n Presence Entity not + home: {{'ON' if presence_entity != '' else 'OFF'}} - {{'PASS' if + not home else 'FAIL'}}, \n zone filter TEST: {{'ON' if zone_only + else 'OFF'}} - {{'PASS' if zone_filter else 'FAIL'}}, \n multi-zone + filter: {{'OFF' if not zone_only or not zone_multi else 'ON'}} - + {{'PASS' if not zone_only or not zone_multi or ( entered_zones|length + and zones and zones |reject('in', entered_zones) |list |length == + 0 ) else 'FAIL'}}, \n state filter TEST: {{'ON' if state_only + else 'OFF'}} - {{'PASS' if state_true else 'FAIL'}}\n\n image: + \"/api/frigate/notifications/{{id}}/{{attachment}}.jpg\"\n" + - alias: Notify on loitering or significant change + choose: + - conditions: '{{ loitering or (not home and zone_filter and zone_multi_filter + and state_true and (new_snapshot or presence_changed or stationary_moved + or zone_only_changed or entered_zones_changed or sub_label_changed)) + }}' + sequence: + - choose: + - conditions: '{{ not group_target }}' + sequence: + - device_id: !input notify_device + domain: mobile_app + type: notify + title: '{{title}}' + message: '{{message}}' + data: + tag: '{{ id }}{{''-loitering'' if loitering}}' + group: '{{ camera }}-frigate-notification{{''-loitering'' if loitering}}' + color: '{{color}}' + image: /api/frigate/notifications/{{id}}/{{attachment}}.jpg?format=android + clickAction: '{{tap_action}}' + ttl: '{{ iif(critical, 0, 3600000) }}' + priority: '{{ iif(critical, ''high'', ''normal'') }}' + alert_once: '{{ alert_once }}' + notification_icon: '{{icon}}' + channel: '{{channel}}' + url: '{{tap_action}}' + attachment: + url: /api/frigate/notifications/{{id}}/{{attachment}}.jpg + push: + sound: '{{ iif(update, ''none'', sound) }}' + interruption-level: '{{ iif(critical, ''critical'', ''active'') + }}' + entity_id: '{{ iif(ios_live_view, input_camera, '''' ) }}' + actions: + - action: URI + title: '{{button_1}}' + uri: '{{url_1}}' + - action: URI + title: '{{button_2}}' + uri: '{{url_2}}' + - action: '{{ ''URI'' if ''/'' in url_3 else url_3 }}' + title: '{{button_3}}' + uri: '{{url_3}}' + destructive: true + - conditions: '{{ tv }}' + sequence: + - service: notify.{{ group_target }} + data: + title: '{{title}}' + message: '{{message}}' + data: + tag: '{{ id }}{{''-loitering'' if loitering}}' + group: '{{ camera }}-frigate-notification{{''-loitering'' if + loitering}}' + color: '{{color}}' + clickAction: '{{tap_action}}' + ttl: '{{ iif(critical, 0, 3600000) }}' + priority: '{{ iif(critical, ''high'', ''normal'') }}' + alert_once: '{{ alert_once }}' + notification_icon: '{{icon}}' + channel: '{{channel}}' + image: + url: '{{base_url}}/api/frigate/notifications/{{id}}/snapshot.jpg' + fontsize: '{{tv_size}}' + position: '{{tv_position}}' + duration: '{{tv_duration}}' + transparency: '{{tv_transparency}}' + interrupt: '{{tv_interrupt}}' + timeout: 30 + url: '{{tap_action}}' + attachment: + url: /api/frigate/notifications/{{id}}/{{attachment}}.jpg + push: + sound: '{{ iif(update, ''none'', sound) }}' + interruption-level: '{{ iif(critical, ''critical'', ''active'') + }}' + entity_id: '{{ iif(ios_live_view, input_camera, '''' ) }}' + actions: + - action: URI + title: '{{button_1}}' + uri: '{{url_1}}' + - action: URI + title: '{{button_2}}' + uri: '{{url_2}}' + - action: '{{ ''URI'' if ''/'' in url_3 else url_3 }}' + title: '{{button_3}}' + uri: '{{url_3}}' + destructive: true + default: + - service: notify.{{ group_target }} + data: + title: '{{title}}' + message: '{{message}}' + data: + tag: '{{ id }}{{''-loitering'' if loitering}}' + group: '{{ camera }}-frigate-notification{{''-loitering'' if loitering}}' + color: '{{color}}' + image: /api/frigate/notifications/{{id}}/{{attachment}}.jpg?format=android + clickAction: '{{tap_action}}' + ttl: '{{ iif(critical, 0, 3600000) }}' + priority: '{{ iif(critical, ''high'', ''normal'') }}' + alert_once: '{{ alert_once }}' + notification_icon: '{{icon}}' + channel: '{{channel}}' + fontsize: '{{tv_size}}' + position: '{{tv_position}}' + duration: '{{tv_duration}}' + transparency: '{{tv_transparency}}' + interrupt: '{{tv_interrupt}}' + url: '{{tap_action}}' + attachment: + url: /api/frigate/notifications/{{id}}/{{attachment}}.jpg + push: + sound: '{{ iif(update, ''none'', sound) }}' + interruption-level: '{{ iif(critical, ''critical'', ''active'') + }}' + entity_id: '{{ iif(ios_live_view, input_camera, '''' ) }}' + actions: + - action: URI + title: '{{button_1}}' + uri: '{{url_1}}' + - action: URI + title: '{{button_2}}' + uri: '{{url_2}}' + - action: '{{ ''URI'' if ''/'' in url_3 else url_3 }}' + title: '{{button_3}}' + uri: '{{url_3}}' + destructive: true + until: '{{ not wait.trigger or wait.trigger.payload_json[''type''] == ''end'' + }}' diff --git a/config/blueprints/automation/Twanne/smart_dimmer_version_3.yaml b/config/blueprints/automation/Twanne/smart_dimmer_version_3.yaml new file mode 100644 index 0000000..1a2ef1c --- /dev/null +++ b/config/blueprints/automation/Twanne/smart_dimmer_version_3.yaml @@ -0,0 +1,213 @@ +blueprint: + name: Smart Light Dimmer V3.0 + description: 'Version 3.0 ! BETA ! + + Switch or dim lights based on the value of a light sensor. + + Light color and temperature can be specified (fixed value). + + Settings for brightness, temperature and color above and below min and max values + of the sensor can be set. + + ' + source_url: https://gist.github.com/Twanne/56791e1917c751de7a72b16ee5e067cd + domain: automation + input: + schedule_start: + name: Schedule start time + description: Automation only runs after this time. + selector: + time: {} + schedule_stop: + name: Schedule stop time + description: Automation does not run after this time. + selector: + time: {} + schedule_days: + name: Run on these days + description: 'Days on which the automation will run. + + Write days in short form, seperated by punctuation marks and/or spaces. + + (i.e.: mon, tue, wed,...) + + ' + selector: + text: {} + light_sensor_entity: + name: Light Sensor + selector: + entity: + domain: sensor + device_class: illuminance + multiple: false + max_brightness_value: + name: Maximum ambient light value + description: Maximum ambient light value measured. + default: 500 + selector: + number: + min: 0.0 + max: 1000.0 + step: 10.0 + unit_of_measurement: lx + mode: slider + min_brightness_value: + name: Minimum ambient light value + description: Minimum ambient light value measured. + default: 0 + selector: + number: + min: 0.0 + max: 1000.0 + step: 10.0 + unit_of_measurement: lx + mode: slider + light_value_1: + name: Dimming value 1 + description: Brightness of the light at maximum ambient light. + default: 0 + selector: + number: + min: 0.0 + max: 100.0 + step: 1.0 + mode: slider + unit_of_measurement: '%' + light_value_2: + name: Dimming value 2 + description: Brightness of the light at minimum ambient light. + default: 100 + selector: + number: + min: 0.0 + max: 100.0 + step: 1.0 + mode: slider + unit_of_measurement: '%' + light_brightness_over_max: + name: Brightness when over max ambient. + description: Brightness of the light when the sensor measures over the maximum + ambient light value. + default: 0 + selector: + number: + min: 0.0 + max: 100.0 + step: 1.0 + mode: slider + unit_of_measurement: '%' + light_temp_over_max: + name: Light temperature when over max ambient. + description: Temperature of the light when over maximum ambient light value. + default: 2000 + selector: + color_temp: {} + light_color_over_max: + name: Light color when over max ambient. + description: Color of the light when over maximum ambient light value. + selector: + color_rgb: {} + light_brightness_under_min: + name: Brightness when under min ambient. + description: Brightness of the light when the sensor measures under the minimum + ambient light value. + default: 100 + selector: + number: + min: 0.0 + max: 100.0 + step: 1.0 + mode: slider + unit_of_measurement: '%' + light_temp_under_min: + name: Light temperature when under min ambient. + description: Temperature of the light when under minimum ambient light value. + default: 2000 + selector: + color_temp: {} + light_color_under_min: + name: Light color when under min ambient. + description: Color of the light when over under minimum light value. + selector: + color_rgb: {} + light_temp: + name: Light temperature + description: Temperature of the light when between minimum and maximum measured + light values. + default: 2000 + selector: + color_temp: {} + light_color: + name: Light color + description: Color of the light when between minimum and maximum measured light + values. + selector: + color_rgb: {} + target_light: + name: Target lights + description: which lights do you want to control? + selector: + target: + entity: + domain: light +mode: single +variables: + light_sensor: !input light_sensor_entity + maxB: !input max_brightness_value + minB: !input min_brightness_value + light1: !input light_value_1 + light2: !input light_value_2 + slope: '{{ ( light1 - light2 ) / ( maxB - minB ) }}' + constant: '{{ light1 - ( slope * maxB ) }}' + days: !input schedule_days +trigger: + platform: state + entity_id: !input light_sensor_entity +condition: +- condition: numeric_state + entity_id: !input light_sensor_entity + above: !input min_brightness_value +- condition: time + after: !input schedule_start + before: !input schedule_stop +- condition: template + value_template: '{{ now().strftime(''%a'') | lower in days }}' +action: +- choose: + - conditions: + - condition: numeric_state + entity_id: !input light_sensor_entity + above: maxB + sequence: + - service: light.turn_on + data: + brightness_pct: !input light_brightness_over_max + color_temp: !input light_temp_over_max + color_rgb: !input light_color_over_max + target: !input target_light + - conditions: + - condition: numeric_state + entity_id: !input light_sensor_entity + below: minB + sequence: + - service: light.turn_on + data: + brightness_pct: !input light_brightness_under_min + color_temp: !input light_temp_under_min + color_rgb: !input light_color_under_min + target: !input target_light + - conditions: + - condition: numeric_state + entity_id: !input light_sensor_entity + below: maxB + above: minB + sequence: + - service: light.turn_on + data: + brightness_pct: '{{ (( slope_brightness * states(light_sensor)|int ) + constant_brightness)|round + }}' + color_temp: '{{ (( slope_temperature * states(light_sensor)|int ) + constant_temperature)|round + }}' + color_rgb: !input light_color + target: !input target_light diff --git a/config/blueprints/automation/Zkaning/turn-on-a-switch-when-motion-is-detected.yaml b/config/blueprints/automation/Zkaning/turn-on-a-switch-when-motion-is-detected.yaml new file mode 100644 index 0000000..810325f --- /dev/null +++ b/config/blueprints/automation/Zkaning/turn-on-a-switch-when-motion-is-detected.yaml @@ -0,0 +1,84 @@ +blueprint: + name: Motion-activated Switch + description: Turn on a switch when motion is detected. + domain: automation + input: + motion_entity: + name: Motion Sensor + selector: + entity: + domain: binary_sensor + device_class: motion + lightsensor_entity: + name: Illuminance Sensor + selector: + entity: + domain: sensor + device_class: illuminance + illuminace_level: + name: Max Illuminance + description: Maximal immuminance level in lux. If illuminance is higher, light + will not be enabled + default: 300 + selector: + number: + min: 0.0 + max: 5000.0 + unit_of_measurement: lux + mode: slider + step: 1.0 + switch_target: + name: Switch + selector: + target: + entity: + domain: switch + time_from: + name: Active from + description: A time input which defines the time from which motion is detected + selector: + time: {} + time_to: + name: Active to + description: A time input which defines the time to which motion is detected + selector: + time: {} + no_motion_wait: + name: Wait time + description: Time to leave the light on after last motion is detected. + default: 120 + selector: + number: + min: 0.0 + max: 3600.0 + unit_of_measurement: seconds + mode: slider + step: 1.0 + source_url: https://community.home-assistant.io/t/turn-on-a-switch-when-motion-is-detected/255709 +mode: restart +max_exceeded: silent +trigger: +- platform: state + entity_id: !input 'motion_entity' + from: 'off' + to: 'on' +condition: +- condition: and + conditions: + - condition: time + after: !input 'time_from' + before: !input 'time_to' + - condition: numeric_state + entity_id: !input 'lightsensor_entity' + below: !input 'illuminace_level' +action: +- service: switch.turn_on + target: !input 'switch_target' +- wait_for_trigger: + platform: state + entity_id: !input 'motion_entity' + from: 'on' + to: 'off' +- delay: !input 'no_motion_wait' +- service: switch.turn_off + target: !input 'switch_target' diff --git a/config/blueprints/automation/argonaute199/thermostat_tpi.yaml b/config/blueprints/automation/argonaute199/thermostat_tpi.yaml new file mode 100644 index 0000000..c34dc7b --- /dev/null +++ b/config/blueprints/automation/argonaute199/thermostat_tpi.yaml @@ -0,0 +1,121 @@ +blueprint: + name: Thermostat TPI + description: Thermostat TPI (Time Propertional & Integral) + domain: automation + input: + coeff_c: + name: Coefficient C + description: coefficient multiplicateur de la différence entre la consigne et + éa température intérieure pour le calcul de la puissance (0.6 conseillé) + selector: + number: + min: 0.0 + max: 1.0 + step: 0.01 + mode: slider + coeff_t: + name: Coefficient T + description: coefficient multiplicateur de la différence entre la consigne et + éa température extérieure pour le calcul de la puissance (0.01 conseillé) + selector: + number: + min: 0.0 + max: 0.1 + step: 0.001 + mode: slider + entity_consigne: + name: Consigne + description: Champs d'entrée de la température de consigne (input number). + selector: + entity: + domain: input_number + entity_temp_ext: + name: Température extérieure + description: Sonde de mesure de la température extérieure (sensor) + selector: + entity: + domain: sensor + device_class: temperature + entity_temp_int: + name: Température intérieure + description: Sonde de mesure de la température intérieure (sensor) + selector: + entity: + domain: sensor + device_class: temperature + entity_fenetre: + name: Fenètre + description: Capteur d'ouverture de fenêtre (sensor) + selector: + entity: + domain: binary_sensor + device_class: opening + entity_puissance: + name: Puissance + description: Champs d'affichage de la puissance (input_number) + selector: + entity: + domain: input_number + entity_chauffage: + name: Chauffage + description: Interrupteur marche / arrêt du chauffage (switch) + selector: + entity: + domain: switch + source_url: https://github.com/argonaute199/chauffage-home-assistant/blob/main/blueprint/thermostat_tpi.yaml +variables: + coeff_c: !input 'coeff_c' + coeff_t: !input 'coeff_t' + entity_temp_int: !input 'entity_temp_int' + entity_temp_ext: !input 'entity_temp_ext' + entity_fenetre: !input 'entity_fenetre' +trigger: +- platform: time_pattern + minutes: /10 +- platform: state + entity_id: !input 'entity_consigne' +- platform: state + entity_id: !input 'entity_fenetre' +action: +- alias: récupération des données + variables: + entity_consigne: !input 'entity_consigne' + consigne: '{{states(entity_consigne)}}' + temp_ext: '{{ states(entity_temp_ext) }}' + temp_int: '{{ states(entity_temp_int) }}' + fenetre: '{{states(entity_fenetre)}}' + puissance: '{%set val = coeff_c * (consigne - temp_int) + coeff_t * (consigne + - temp_ext) %} {% if val > 1 and fenetre == ''off'' %} {% set val = 100 %} {% + elif val < 0 or fenetre == ''on'' %} {% set val = 0 %} {% else %} {% set val + = ( (val * 100) | round(0)) %} {% endif %} {{val}}' + temps_chauffe: '{{ puissance * 6 }}' +- alias: Met à jour l'indicateur de puissance + service: input_number.set_value + target: + entity_id: !input 'entity_puissance' + data: + value: '{{puissance}}' +- choose: + - conditions: + - condition: template + value_template: '{{puissance == 0}}' + sequence: + - service: switch.turn_off + target: + entity_id: !input 'entity_chauffage' + - conditions: + - condition: template + value_template: '{{ puissance > 99}}' + sequence: + - service: switch.turn_on + target: + entity_id: !input 'entity_chauffage' + default: + - service: switch.turn_on + target: + entity_id: !input 'entity_chauffage' + - delay: '{{temps_chauffe}}' + - service: switch.turn_off + target: + entity_id: !input 'entity_chauffage' +mode: restart diff --git a/config/blueprints/automation/bergstrom/door-sensor-turn-on-off-light.yaml b/config/blueprints/automation/bergstrom/door-sensor-turn-on-off-light.yaml new file mode 100644 index 0000000..5ea027f --- /dev/null +++ b/config/blueprints/automation/bergstrom/door-sensor-turn-on-off-light.yaml @@ -0,0 +1,46 @@ +blueprint: + name: Door Sensor-activated Light + description: Turn on a light when door is opened. + domain: automation + input: + doorsensor_entity: + name: Door Sensor + selector: + entity: + domain: binary_sensor + light_target: + name: Light + selector: + target: + entity: + domain: light + door_closed_wait: + name: Wait time + description: Time to leave the light on after door is closed + default: 120 + selector: + number: + min: 0.0 + max: 3600.0 + unit_of_measurement: seconds + mode: slider + step: 1.0 + source_url: https://community.home-assistant.io/t/door-sensor-turn-on-off-light/255657 +mode: single +max_exceeded: silent +trigger: + platform: state + entity_id: !input 'doorsensor_entity' + from: 'off' + to: 'on' +action: +- service: light.turn_on + target: !input 'light_target' +- wait_for_trigger: + platform: state + entity_id: !input 'doorsensor_entity' + from: 'on' + to: 'off' +- delay: !input 'door_closed_wait' +- service: light.turn_off + target: !input 'light_target' diff --git a/config/blueprints/automation/brent/zha-aqara-magic-cube-57-actions.yaml b/config/blueprints/automation/brent/zha-aqara-magic-cube-57-actions.yaml new file mode 100644 index 0000000..a90c02b --- /dev/null +++ b/config/blueprints/automation/brent/zha-aqara-magic-cube-57-actions.yaml @@ -0,0 +1,617 @@ +blueprint: + name: Aqara Magic Cube + description: Control anything using Aqara Magic Cube. + domain: automation + input: + remote: + name: Magic Cube + description: Select the Aqara Magic Cube device + selector: + device: + integration: zha + manufacturer: LUMI + flip_90: + name: Flip 90 degrees + description: 'Actions to run when cube flips 90 degrees. + + This cancels all specific 90 degrees functions. + + e.g From side 1 to side 2 will be the same as from side 6 to side 2' + default: false + selector: + boolean: {} + cube_flip_90: + name: Flip cube 90 degrees + description: Action to run when cube flips 90 degrees. This only works if 'Flip + 90 degrees' is toggled + default: [] + selector: + action: {} + flip_180: + name: Flip 180 degrees + description: 'Actions to run when cube flips 180 degrees. + + This cancels all specific 180 degrees functions + + e.g From side 1 to side 4 will be the same as from side 5 to side 2' + default: false + selector: + boolean: {} + cube_flip_180: + name: Flip cube 180 degrees + description: Action to run when cube flips 180 degrees. This only works if 'Flip + 180 degrees' is toggled + default: [] + selector: + action: {} + flip_any: + name: Flip to any side + description: 'Actions to run when cube flips to any side. + + This cares about the end side, but cancels all specific flip functions. + + e.g From side 1 to side 2 will be the same as from side 6 to side 2 + + but different than side 1 to side 5' + default: false + selector: + boolean: {} + cube_flip_1: + name: Flip cube to side 1 + description: Action to run when cube flips to side 1. This only works if 'Flip + any' is toggled + default: [] + selector: + action: {} + cube_flip_2: + name: Flip cube to side 2 + description: Action to run when cube flips to side 2. This only works if 'Flip + any' is toggled + default: [] + selector: + action: {} + cube_flip_3: + name: Flip cube to side 3 + description: Action to run when cube flips to side 3. This only works if 'Flip + any' is toggled + default: [] + selector: + action: {} + cube_flip_4: + name: Flip cube to side 4 + description: Action to run when cube flips to side 4. This only works if 'Flip + any' is toggled + default: [] + selector: + action: {} + cube_flip_5: + name: Flip cube to side 5 + description: Action to run when cube flips to side 5. This only works if 'Flip + any' is toggled + default: [] + selector: + action: {} + cube_flip_6: + name: Flip cube to side 6 + description: Action to run when cube flips to side 6. This only works if 'Flip + any' is toggled + default: [] + selector: + action: {} + slide_any_side: + name: Slide any side + description: 'Actions to run when cube slides on any side. + + This cancels all specific ''slide'' functions + + e.g Slide on side 1 will be the same as slide on side 2' + default: false + selector: + boolean: {} + cube_slide_any: + name: Slide cube on any side + description: Action to run when cube slides on any slide. This only works if + 'Slide any side' is toggled + default: [] + selector: + action: {} + knock_any_side: + name: Knock on any side + description: 'Actions to run when knocking cube regardless of the side. + + This cancels all specific ''knock'' functions + + e.g Knock on side 1 will be the same as knocking side 2' + default: false + selector: + boolean: {} + cube_knock_any: + name: Knock cube on any side + description: Action to run when knocking cube on any side. This only works if + 'Knock on any side' is toggled + default: [] + selector: + action: {} + one_to_two: + name: From side 1 to side 2 + description: Action to run when cube goes from side 1 to side 2 + default: [] + selector: + action: {} + one_to_three: + name: From side 1 to side 3 + description: Action to run when cube goes from side 1 to side 3 + default: [] + selector: + action: {} + one_to_four: + name: From side 1 to side 4 + description: Action to run when cube goes from side 1 to side 4 + default: [] + selector: + action: {} + one_to_five: + name: From side 1 to side 5 + description: Action to run when cube goes from side 1 to side 5 + default: [] + selector: + action: {} + one_to_six: + name: From side 1 to side 6 + description: Action to run when cube goes from side 1 to side 6 + default: [] + selector: + action: {} + two_to_one: + name: From side 2 to side 1 + description: Action to run when cube goes from side 2 to side 1 + default: [] + selector: + action: {} + two_to_three: + name: From side 2 to side 3 + description: Action to run when cube goes from side 2 to side 3 + default: [] + selector: + action: {} + two_to_four: + name: From side 2 to side 4 + description: Action to run when cube goes from side 2 to side 4 + default: [] + selector: + action: {} + two_to_five: + name: From side 2 to side 5 + description: Action to run when cube goes from side 2 to side 5 + default: [] + selector: + action: {} + two_to_six: + name: From side 2 to side 6 + description: Action to run when cube goes from side 2 to side 6 + default: [] + selector: + action: {} + three_to_one: + name: From side 3 to side 1 + description: Action to run when cube goes from side 3 to side 1 + default: [] + selector: + action: {} + three_to_two: + name: From side 3 to side 2 + description: Action to run when cube goes from side 3 to side 2 + default: [] + selector: + action: {} + three_to_four: + name: From side 3 to side 4 + description: Action to run when cube goes from side 3 to side 4 + default: [] + selector: + action: {} + three_to_five: + name: From side 3 to side 5 + description: Action to run when cube goes from side 3 to side 5 + default: [] + selector: + action: {} + three_to_six: + name: From side 3 to side 6 + description: Action to run when cube goes from side 3 to side 6 + default: [] + selector: + action: {} + four_to_one: + name: From side 4 to side 1 + description: Action to run when cube goes from side 4 to side 1 + default: [] + selector: + action: {} + four_to_two: + name: From side 4 to side 2 + description: Action to run when cube goes from side 4 to side 2 + default: [] + selector: + action: {} + four_to_three: + name: From side 4 to side 3 + description: Action to run when cube goes from side 4 to side 3 + default: [] + selector: + action: {} + four_to_five: + name: From side 4 to side 5 + description: Action to run when cube goes from side 4 to side 5 + default: [] + selector: + action: {} + four_to_six: + name: From side 4 to side 6 + description: Action to run when cube goes from side 4 to side 6 + default: [] + selector: + action: {} + five_to_one: + name: From side 5 to side 1 + description: Action to run when cube goes from side 5 to side 1 + default: [] + selector: + action: {} + five_to_two: + name: From side 5 to side 2 + description: Action to run when cube goes from side 5 to side 2 + default: [] + selector: + action: {} + five_to_three: + name: From side 5 to side 3 + description: Action to run when cube goes from side 5 to side 3 + default: [] + selector: + action: {} + five_to_four: + name: From side 5 to side 4 + description: Action to run when cube goes from side 5 to side 4 + default: [] + selector: + action: {} + five_to_six: + name: From side 5 to side 6 + description: Action to run when cube goes from side 5 to side 6 + default: [] + selector: + action: {} + six_to_one: + name: From side 6 to side 1 + description: Action to run when cube goes from side 6 to side 1 + default: [] + selector: + action: {} + six_to_two: + name: From side 6 to side 2 + description: Action to run when cube goes from side 6 to side 2 + default: [] + selector: + action: {} + six_to_three: + name: From side 6 to side 3 + description: Action to run when cube goes from side 6 to side 3 + default: [] + selector: + action: {} + six_to_four: + name: From side 6 to side 4 + description: Action to run when cube goes from side 6 to side 4 + default: [] + selector: + action: {} + six_to_five: + name: From side 6 to side 5 + description: Action to run when cube goes from side 6 to side 5 + default: [] + selector: + action: {} + one_to_one: + name: Knock - Side 1 + description: Action to run when knocking on side 1 + default: [] + selector: + action: {} + two_to_two: + name: Knock - Side 2 + description: Action to run when knocking on side 2 + default: [] + selector: + action: {} + three_to_three: + name: Knock - Side 3 + description: Action to run when knocking on side 3 + default: [] + selector: + action: {} + four_to_four: + name: Knock - Side 4 + description: Action to run when knocking on side 4 + default: [] + selector: + action: {} + five_to_five: + name: Knock - Side 5 + description: Action to run when knocking on side 5 + default: [] + selector: + action: {} + six_to_six: + name: Knock - Side 6 + description: Action to run when knocking on side 6 + default: [] + selector: + action: {} + slide_on_one: + name: Slide - Side 1 up + description: Action to run when slides with Side 1 up + default: [] + selector: + action: {} + slide_on_two: + name: Slide - Side 2 up + description: Action to run when slides with Side 2 up + default: [] + selector: + action: {} + slide_on_three: + name: Slide - Side 3 up + description: Action to run when slides with Side 3 up + default: [] + selector: + action: {} + slide_on_four: + name: Slide - Side 4 up + description: Action to run when slides with Side 4 up + default: [] + selector: + action: {} + slide_on_five: + name: Slide - Side 5 up + description: Action to run when slides with Side 5 up + default: [] + selector: + action: {} + slide_on_six: + name: Slide - Side 6 up + description: Action to run when slides with Side 6 up + default: [] + selector: + action: {} + cube_wake: + name: Wake up the cube + description: Action to run when cube wakes up + default: [] + selector: + action: {} + cube_drop: + name: Cube drops + description: Action to run when cube drops + default: [] + selector: + action: {} + cube_shake: + name: Shake cube + description: Action to run when you shake the cube + default: [] + selector: + action: {} + rotate_right: + name: Rotate right + description: Action to run when cube rotates right + default: [] + selector: + action: {} + rotate_left: + name: Rotate left + description: Action to run when cube rotates left + default: [] + selector: + action: {} + source_url: https://community.home-assistant.io/t/zha-aqara-magic-cube-57-actions/297012 +mode: restart +max_exceeded: silent +trigger: +- platform: event + event_type: zha_event + event_data: + device_id: !input 'remote' +action: +- variables: + command: '{{ trigger.event.data.command }}' + value: '{{ trigger.event.data.args.value | default(0) }}' + flip_degrees: '{{ trigger.event.data.args.flip_degrees | default(0) }}' + flip_any: !input 'flip_any' + flip_90: !input 'flip_90' + flip_180: !input 'flip_180' + slide_any_side: !input 'slide_any_side' + knock_any_side: !input 'knock_any_side' + flip90: 64 + flip180: 128 + slide: 256 + knock: 512 + shake: 0 + drop: 3 + activated_face: "\n{% if command == \"slide\" or command == \"knock\" %}\n\n \ + \ {% if trigger.event.data.args.activated_face == 1 %} 1\n\n {% elif trigger.event.data.args.activated_face\ + \ == 2 %} 5\n\n {% elif trigger.event.data.args.activated_face == 3 %} 6\n\n\ + \ {% elif trigger.event.data.args.activated_face == 4 %} 4\n\n {% elif trigger.event.data.args.activated_face\ + \ == 5 %} 2\n\n {% elif trigger.event.data.args.activated_face == 6 %} 3\n\n\ + \ {% endif %}\n\n{% elif command == 'flip' %}\n\n {{ trigger.event.data.args.activated_face\ + \ | int }}\n\n{% endif %}\n" + from_face: "\n{% if command == \"flip\" and flip_degrees == 90 %}\n\n {{ ((value\ + \ - flip90 - (trigger.event.data.args.activated_face - 1)) / 8) + 1 | int }}\n\ + \n{% endif %}\n" + relative_degrees: "\n{% if command == \"rotate_right\" or command == \"rotate_left\"\ + \ %}\n\n {{ trigger.event.data.args.relative_degrees | float }}\n\n{% endif\ + \ %}\n" +- choose: + - conditions: + - '{{ command == ''rotate_right'' }}' + sequence: !input 'rotate_right' + - conditions: + - '{{ command == ''rotate_left'' }}' + sequence: !input 'rotate_left' + - conditions: + - '{{ command == ''checkin'' }}' + sequence: !input 'cube_wake' + - conditions: + - '{{ value == shake }}' + sequence: !input 'cube_shake' + - conditions: + - '{{ value == drop }}' + sequence: !input 'cube_drop' + - conditions: + - '{{ command == ''knock'' and knock_any_side }}' + sequence: !input 'cube_knock_any' + - conditions: + - '{{ command == ''slide'' and slide_any_side }}' + sequence: !input 'cube_slide_any' + - conditions: + - '{{ command == ''flip'' and flip_any }}' + sequence: + - choose: + - conditions: '{{ activated_face == 1 }}' + sequence: !input 'cube_flip_1' + - conditions: '{{ activated_face == 2 }}' + sequence: !input 'cube_flip_2' + - conditions: '{{ activated_face == 3 }}' + sequence: !input 'cube_flip_3' + - conditions: '{{ activated_face == 4 }}' + sequence: !input 'cube_flip_4' + - conditions: '{{ activated_face == 5 }}' + sequence: !input 'cube_flip_5' + - conditions: '{{ activated_face == 6 }}' + sequence: !input 'cube_flip_6' + - conditions: + - '{{ flip_degrees == 90 and flip_90 }}' + sequence: !input 'cube_flip_90' + - conditions: + - '{{ flip_degrees == 180 and flip_180 }}' + sequence: !input 'cube_flip_180' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 1 }}' + sequence: + - choose: + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_one' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_one' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_one' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_one' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 2 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_two' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_two' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_two' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_two' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 3 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_three' + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_three' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_three' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_three' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 4 }}' + sequence: + - choose: + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_four' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_four' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_four' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_four' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 5 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_five' + - conditions: '{{ from_face == 3 }}' + sequence: !input 'three_to_five' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_five' + - conditions: '{{ from_face == 6 }}' + sequence: !input 'six_to_five' + - conditions: + - '{{ flip_degrees == 90 and activated_face == 6 }}' + sequence: + - choose: + - conditions: '{{ from_face == 1 }}' + sequence: !input 'one_to_six' + - conditions: '{{ from_face == 2 }}' + sequence: !input 'two_to_six' + - conditions: '{{ from_face == 4 }}' + sequence: !input 'four_to_six' + - conditions: '{{ from_face == 5 }}' + sequence: !input 'five_to_six' + - conditions: + - '{{ value == flip180 + activated_face - 1 }}' + sequence: + - choose: + - conditions: '{{ activated_face == 1 }}' + sequence: !input 'four_to_one' + - conditions: '{{ activated_face == 2 }}' + sequence: !input 'five_to_two' + - conditions: '{{ activated_face == 3 }}' + sequence: !input 'six_to_three' + - conditions: '{{ activated_face == 4 }}' + sequence: !input 'one_to_four' + - conditions: '{{ activated_face == 5 }}' + sequence: !input 'two_to_five' + - conditions: '{{ activated_face == 6 }}' + sequence: !input 'three_to_six' + - conditions: + - '{{ command == ''knock'' and not knock_any_side }}' + sequence: + - choose: + - conditions: '{{ activated_face == 1 }}' + sequence: !input 'one_to_one' + - conditions: '{{ activated_face == 2 }}' + sequence: !input 'two_to_two' + - conditions: '{{ activated_face == 3 }}' + sequence: !input 'three_to_three' + - conditions: '{{ activated_face == 4 }}' + sequence: !input 'four_to_four' + - conditions: '{{ activated_face == 5 }}' + sequence: !input 'five_to_five' + - conditions: '{{ activated_face == 6 }}' + sequence: !input 'six_to_six' + - conditions: + - '{{ value == slide + activated_face - 1 }}' + sequence: + - choose: + - conditions: '{{ activated_face == 1 }}' + sequence: !input 'slide_on_one' + - conditions: '{{ activated_face == 2 }}' + sequence: !input 'slide_on_two' + - conditions: '{{ activated_face == 3 }}' + sequence: !input 'slide_on_three' + - conditions: '{{ activated_face == 4 }}' + sequence: !input 'slide_on_four' + - conditions: '{{ activated_face == 5 }}' + sequence: !input 'slide_on_five' + - conditions: '{{ activated_face == 6 }}' + sequence: !input 'slide_on_six' diff --git a/config/blueprints/automation/chaudiere/chaudiere b/config/blueprints/automation/chaudiere/chaudiere new file mode 100644 index 0000000..d3c1d67 --- /dev/null +++ b/config/blueprints/automation/chaudiere/chaudiere @@ -0,0 +1,77 @@ +blueprint: + name: Thermostat chaudiere bois + description: Thermostat chaudiere bois + domain: automation + + input: + entity_consigne: + name: Consigne + description: Champ d'entrée de la consigne du thermostat (input number). + selector: + entity: + domain: input_number + + entity_mode: + name: mode_on_off + description: mode on off + selector: + entity: + domain: input_boolean + + entity_temp_ext: + name: Température extérieure + description: Sonde de mesure de la température extérieure (sensor) + selector: + entity: + domain: sensor + device_class: temperature + entity_temp_int: + name: Température intérieure + description: Sonde de mesure de la température intérieure (sensor) + selector: + entity: + domain: sensor + device_class: temperature + entity_temp_chaud: + name: Température chaudiere bois + description: Sonde de mesure de la température chaudiere bois (sensor) + selector: + entity: + domain: sensor + device_class: temperature + entity_temp_ballon: + name: Température ballon + description: Sonde de mesure de la température du ballon (sensor) + selector: + entity: + domain: sensor + device_class: temperature + delta_thermostat: + name: delta temperature thermostat + description: delta de temperature entre consigne et temperature salon (0.6 conseillé) + selector: + number: + min: 0.0 + max: 2.0 + step: 0.1 + + + entity_circulateur: + name: Circulateur + description: marche / arrêt du circulateur (switch) + selector: + entity: + domain: switch + +# Récupération des paramètres +variables: + entity_temp_int: !input entity_temp_int + entity_temp_ext: !input entity_temp_ext + entity_consigne: !input entity_consigne + entity_mode: !input entity_mode + entity_temp_chaud: !input entity_temp_chaud + entity_temp_ballon: !input entity_temp_ballon + entity_delta_th: !input delta_thermostat + +mode: restart + \ No newline at end of file diff --git a/config/blueprints/automation/freakshock88/motion_illuminance_activated_entity.yaml b/config/blueprints/automation/freakshock88/motion_illuminance_activated_entity.yaml new file mode 100644 index 0000000..10e648b --- /dev/null +++ b/config/blueprints/automation/freakshock88/motion_illuminance_activated_entity.yaml @@ -0,0 +1,167 @@ +blueprint: + name: Turn on light, switch, scene, script or group based on motion and illuminance. + description: "Turn on a light, switch, scene, script or group based on motion detection,\ + \ and low light level.\nThis blueprint uses helper entities you have to create\ + \ yourself for some input values, to be able to dynamically set limits. For instructions\ + \ on creating the helper entities take a look in the Home Assistant Community\ + \ forum topic: https://community.home-assistant.io/t/turn-on-light-switch-scene-or-script-based-on-motion-and-illuminance-more-conditions/257085\n\ + \nRequired entities:\n - Motion sensor (single sensor or group)\n - Target entity\ + \ (light, switch, scene or script)\n\n\nOptional features:\n- You can set a cutoff\ + \ entity of which the value determines whether the illuminance level is low and\ + \ the automation needs to trigger. - You can define a blocking entity, which blocks\ + \ the automation from running when this entity's state is on. - You van define\ + \ a turn-off blocking entity, which blocks the entity from turning off after the\ + \ set delay. - Time limits can also be defined to limit the time before and after\ + \ the automation should trigger. - If you want the entity to turn off after a\ + \ certain amount of minutes, you can use the Wait Time input. - If you want another\ + \ entity than the target_entity to turn off after the delay, you can define a\ + \ separate Turn-off entity. - If you do not enable the optional entities the automation\ + \ will skip these conditions.\n\n\nOptional entities:\n- Illuminance sensor (sensor\ + \ in illuminance class)\n- Illuminance cutoff value (input_number)\n- Blocking\ + \ entity (any entity with state on/off)\n- Time limit before (input_datetime)\n\ + - Time limit after (input_datetime)\n- Turn off wait time [in minutes!] (input_number)\ + \ - will not work with script or scene target entities.\n- Turn off entity (any\ + \ entity_id)\n" + domain: automation + input: + motion_sensor: + name: Motion Sensor + description: This sensor will trigger the turning on of the target entity. + selector: + entity: {} + target_entity: + name: Target entity. + description: The light, switch, scene to turn on (or script to run) when the + automation is triggered. + selector: + entity: {} + illuminance_sensor: + name: (OPTIONAL) Illuminance sensor + description: This sensor will be used to determine the illumination. + default: + selector: + entity: + domain: sensor + device_class: illuminance + illuminance_cutoff: + name: (OPTIONAL) Illuminance cutoff value + description: This input_number will be used to compare to the current illumination + to determine if it is low. + default: + selector: + entity: + domain: input_number + blocker_entity: + name: (OPTIONAL) Blocking entity + description: If this entity's state is on, it will prevent the automation from + running. E.g. sleepmode or away mode. + default: + selector: + entity: {} + time_limit_after: + name: (OPTIONAL) Only run after time. + description: Automation will only run when time is later than this input_datetime + value. + default: + selector: + entity: + domain: input_datetime + time_limit_before: + name: (OPTIONAL) Only run before time. + description: Automation will only run when time is earlier than this input_datetime + value. + default: + selector: + entity: + domain: input_datetime + no_motion_wait: + name: (OPTIONAL) Turn off wait time (minutes) + description: Time in minutes to leave the target entity on after last motion + is detected. If not used entity will not auto turn off. + default: + selector: + entity: + domain: input_number + turn_off_blocker_entity: + name: (OPTIONAL) Turn-off Blocking entity + description: If this entity's state is on, it will prevent the target entity + from turning off after the set delay. + default: + selector: + entity: {} + target_off_entity: + name: (OPTIONAL) Turn-off entity + description: If defined, this entity will be turned off instead of the default + target entity. This can be helpful when using target entities of type scene + or script. + default: + selector: + entity: {} + source_url: https://gist.github.com/freakshock88/2311759ba64f929f6affad4c0a67110b +mode: restart +max_exceeded: silent +variables: + target_entity: !input 'target_entity' + illuminance_currently: !input 'illuminance_sensor' + illuminance_cutoff: !input 'illuminance_cutoff' + blocker_entity: !input 'blocker_entity' + time_limit_before: !input 'time_limit_before' + time_limit_after: !input 'time_limit_after' + no_motion_wait: !input 'no_motion_wait' + entity_domain: '{{ states[target_entity].domain }}' + turn_off_blocker_entity: !input 'turn_off_blocker_entity' + target_off_entity: !input 'target_off_entity' +trigger: + platform: state + entity_id: !input 'motion_sensor' + to: 'on' +condition: +- condition: template + value_template: '{{ (states[target_entity].state == ''on'') or (illuminance_currently + == none) or (illuminance_cutoff == none) or (states[illuminance_currently].state + | int < states[illuminance_cutoff].state | int) }}' +- condition: template + value_template: '{{ (blocker_entity == none) or (states[blocker_entity].state == + ''off'') }}' +- condition: template + value_template: "{% set current_time = now().strftime(\"%H:%M\") %}\n{% if time_limit_before\ + \ == none and time_limit_after == none %} true {% endif %}\n{% if time_limit_before\ + \ != none and time_limit_after == none %} {% set current_time_is_before_limit\ + \ = current_time < states[time_limit_before].state %} {{ current_time_is_before_limit\ + \ }} {% elif time_limit_before == none and time_limit_after != none %} {% set\ + \ current_time_is_after_limit = current_time > states[time_limit_after].state\ + \ %} {{ current_time_is_after_limit }} {% endif %}\n{% if time_limit_before !=\ + \ none and time_limit_after != none %} {% set before_limit_is_tomorrow = states[time_limit_before].state\ + \ < states[time_limit_after].state %} {% set current_time_is_before_limit = current_time\ + \ < states[time_limit_before].state %} {% set current_time_is_after_limit = current_time\ + \ > states[time_limit_after].state %} {% set time_window_spans_midnight = states[time_limit_after].state\ + \ > states[time_limit_before].state %}\n {% if time_window_spans_midnight !=\ + \ none and time_window_spans_midnight and before_limit_is_tomorrow %}\n {{ current_time_is_after_limit\ + \ or current_time_is_before_limit }}\n {% elif time_window_spans_midnight !=\ + \ none and not time_window_spans_midnight %}\n {{ current_time_is_before_limit\ + \ and current_time_is_after_limit }}\n {% endif %}\n{% endif %}\n" +action: +- service: homeassistant.turn_on + entity_id: !input 'target_entity' +- condition: template + value_template: '{{ no_motion_wait != none }}' +- wait_for_trigger: + platform: state + entity_id: !input 'motion_sensor' + from: 'on' + to: 'off' +- delay: + minutes: '{{ states[no_motion_wait].state | int }}' +- condition: template + value_template: '{{ (turn_off_blocker_entity == none) or (states[turn_off_blocker_entity].state + == ''off'') }}' +- choose: + - conditions: + - condition: template + value_template: '{{ (target_off_entity != none) }}' + sequence: + - service: homeassistant.turn_off + entity_id: !input 'target_off_entity' + default: + - service: homeassistant.turn_off + entity_id: !input 'target_entity' diff --git a/config/blueprints/automation/golles/zigbee2mqtt_aqara_magic_cube.yaml b/config/blueprints/automation/golles/zigbee2mqtt_aqara_magic_cube.yaml new file mode 100644 index 0000000..2e212b4 --- /dev/null +++ b/config/blueprints/automation/golles/zigbee2mqtt_aqara_magic_cube.yaml @@ -0,0 +1,288 @@ +blueprint: + name: Zigbee2MQTT - Aqara Magic Cube (MFKZQ01LM) + description: 'This blueprint allows you to make automations for the Aqara Magic + Cube, connected to Zigbee2MQTT. + + + Limitations (these are by design): + + - There is just one trigger for flip to a side, separate actions for flip90 and + flip180 aren''t possible. + + - For fall there are no separate actions for the side, due to the nature of falling + it becomes random what will happen. + + - For shake there are no separate actions for the side, due to the nature of shaking + it isn''t always clear which side is up. + + ' + domain: automation + source_url: https://github.com/golles/Home-Assistant-Blueprints/blob/9a63ee03f1d0cf10448fb89a28528ac4105461ac/zigbee2mqtt_aqara_magic_cube.yaml + input: + remote: + name: Remote + description: Aqara Magic Cube to use, entity should end with _action + selector: + entity: + integration: mqtt + domain: sensor + fall: + name: Drop the cube + default: [] + selector: + action: {} + flip_side_0: + name: Flip the cube to side 0 + default: [] + selector: + action: {} + flip_side_1: + name: Flip the cube to side 1 + default: [] + selector: + action: {} + flip_side_2: + name: Flip the cube to side 2 + default: [] + selector: + action: {} + flip_side_3: + name: Flip the cube to side 3 + default: [] + selector: + action: {} + flip_side_4: + name: Flip the cube to side 4 + default: [] + selector: + action: {} + flip_side_5: + name: Flip the cube to side 5 + default: [] + selector: + action: {} + rotate_left_side_0: + name: Rotate cube left with side 0 up + default: [] + selector: + action: {} + rotate_left_side_1: + name: Rotate cube left with side 1 up + default: [] + selector: + action: {} + rotate_left_side_2: + name: Rotate cube left with side 2 up + default: [] + selector: + action: {} + rotate_left_side_3: + name: Rotate cube left with side 3 up + default: [] + selector: + action: {} + rotate_left_side_4: + name: Rotate cube left with side 4 up + default: [] + selector: + action: {} + rotate_left_side_5: + name: Rotate cube left with side 5 up + default: [] + selector: + action: {} + rotate_right_side_0: + name: Rotate cube right with side 0 up + default: [] + selector: + action: {} + rotate_right_side_1: + name: Rotate cube right with side 1 up + default: [] + selector: + action: {} + rotate_right_side_2: + name: Rotate cube right with side 2 up + default: [] + selector: + action: {} + rotate_right_side_3: + name: Rotate cube right with side 3 up + default: [] + selector: + action: {} + rotate_right_side_4: + name: Rotate cube right with side 4 up + default: [] + selector: + action: {} + rotate_right_side_5: + name: Rotate cube right with side 5 up + default: [] + selector: + action: {} + shake: + name: Shake the cube + default: [] + selector: + action: {} + slide_side_0: + name: Slide the cube with side 0 up + default: [] + selector: + action: {} + slide_side_1: + name: Slide the cube with side 1 up + default: [] + selector: + action: {} + slide_side_2: + name: Slide the cube with side 2 up + default: [] + selector: + action: {} + slide_side_3: + name: Slide the cube with side 3 up + default: [] + selector: + action: {} + slide_side_4: + name: Slide the cube with side 4 up + default: [] + selector: + action: {} + slide_side_5: + name: Slide the cube with side 5 up + default: [] + selector: + action: {} + tap_side_0: + name: Tap the cube with side 0 up + default: [] + selector: + action: {} + tap_side_1: + name: Tap the cube with side 1 up + default: [] + selector: + action: {} + tap_side_2: + name: Tap the cube with side 2 up + default: [] + selector: + action: {} + tap_side_3: + name: Tap the cube with side 3 up + default: [] + selector: + action: {} + tap_side_4: + name: Tap the cube with side 4 up + default: [] + selector: + action: {} + tap_side_5: + name: Tap the cube with side 5 up + default: [] + selector: + action: {} +mode: queued +max: 5 +max_exceeded: silent +trigger: +- platform: state + entity_id: !input 'remote' + attribute: action + to: + - fall + - flip180 + - flip90 + - rotate_left + - rotate_right + - shake + - slide + - tap +action: +- variables: + event: '{{ trigger.to_state.attributes.action }}' + side: '{{ trigger.to_state.attributes.side }}' +- choose: + - conditions: '{{ event == "fall" }}' + sequence: !input 'fall' + - conditions: '{{ event == "flip180" or event == "flip90" }}' + sequence: + - choose: + - conditions: '{{ side == 0 }}' + sequence: !input 'flip_side_0' + - conditions: '{{ side == 1 }}' + sequence: !input 'flip_side_1' + - conditions: '{{ side == 2 }}' + sequence: !input 'flip_side_2' + - conditions: '{{ side == 3 }}' + sequence: !input 'flip_side_3' + - conditions: '{{ side == 4 }}' + sequence: !input 'flip_side_4' + - conditions: '{{ side == 5 }}' + sequence: !input 'flip_side_5' + - conditions: '{{ event == "rotate_left" }}' + sequence: + - choose: + - conditions: '{{ side == 0 }}' + sequence: !input 'rotate_left_side_0' + - conditions: '{{ side == 1 }}' + sequence: !input 'rotate_left_side_1' + - conditions: '{{ side == 2 }}' + sequence: !input 'rotate_left_side_2' + - conditions: '{{ side == 3 }}' + sequence: !input 'rotate_left_side_3' + - conditions: '{{ side == 4 }}' + sequence: !input 'rotate_left_side_4' + - conditions: '{{ side == 5 }}' + sequence: !input 'rotate_left_side_5' + - conditions: '{{ event == "rotate_right" }}' + sequence: + - choose: + - conditions: '{{ side == 0 }}' + sequence: !input 'rotate_right_side_0' + - conditions: '{{ side == 1 }}' + sequence: !input 'rotate_right_side_1' + - conditions: '{{ side == 2 }}' + sequence: !input 'rotate_right_side_2' + - conditions: '{{ side == 3 }}' + sequence: !input 'rotate_right_side_3' + - conditions: '{{ side == 4 }}' + sequence: !input 'rotate_right_side_4' + - conditions: '{{ side == 5 }}' + sequence: !input 'rotate_right_side_5' + - conditions: '{{ event == "shake" }}' + sequence: !input 'shake' + - conditions: '{{ event == "slide" }}' + sequence: + - choose: + - conditions: '{{ side == 0 }}' + sequence: !input 'slide_side_0' + - conditions: '{{ side == 1 }}' + sequence: !input 'slide_side_1' + - conditions: '{{ side == 2 }}' + sequence: !input 'slide_side_2' + - conditions: '{{ side == 3 }}' + sequence: !input 'slide_side_3' + - conditions: '{{ side == 4 }}' + sequence: !input 'slide_side_4' + - conditions: '{{ side == 5 }}' + sequence: !input 'slide_side_5' + - conditions: '{{ event == "tap" }}' + sequence: + - choose: + - conditions: '{{ side == 0 }}' + sequence: !input 'tap_side_0' + - conditions: '{{ side == 1 }}' + sequence: !input 'tap_side_1' + - conditions: '{{ side == 2 }}' + sequence: !input 'tap_side_2' + - conditions: '{{ side == 3 }}' + sequence: !input 'tap_side_3' + - conditions: '{{ side == 4 }}' + sequence: !input 'tap_side_4' + - conditions: '{{ side == 5 }}' + sequence: !input 'tap_side_5' diff --git a/config/blueprints/automation/homeassistant/motion_light.yaml b/config/blueprints/automation/homeassistant/motion_light.yaml index 8f5d3f9..54a4a4f 100644 --- a/config/blueprints/automation/homeassistant/motion_light.yaml +++ b/config/blueprints/automation/homeassistant/motion_light.yaml @@ -3,15 +3,13 @@ blueprint: description: Turn on a light when motion is detected. domain: automation source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/motion_light.yaml - author: Home Assistant input: motion_entity: name: Motion Sensor selector: entity: - filter: - device_class: motion - domain: binary_sensor + domain: binary_sensor + device_class: motion light_target: name: Light selector: diff --git a/config/blueprints/automation/homeassistant/notify_leaving_zone.yaml b/config/blueprints/automation/homeassistant/notify_leaving_zone.yaml index e1e3bd5..71abf8f 100644 --- a/config/blueprints/automation/homeassistant/notify_leaving_zone.yaml +++ b/config/blueprints/automation/homeassistant/notify_leaving_zone.yaml @@ -3,27 +3,23 @@ blueprint: description: Send a notification to a device when a person leaves a specific zone. domain: automation source_url: https://github.com/home-assistant/core/blob/dev/homeassistant/components/automation/blueprints/notify_leaving_zone.yaml - author: Home Assistant input: person_entity: name: Person selector: entity: - filter: - domain: person + domain: person zone_entity: name: Zone selector: entity: - filter: - domain: zone + domain: zone notify_device: name: Device to notify description: Device needs to run the official Home Assistant app to receive notifications. selector: device: - filter: - integration: mobile_app + integration: mobile_app trigger: platform: state @@ -38,9 +34,7 @@ variables: condition: condition: template - # The first case handles leaving the Home zone which has a special state when zoning called 'home'. - # The second case handles leaving all other zones. - value_template: "{{ zone_entity == 'zone.home' and trigger.from_state.state == 'home' and trigger.to_state.state != 'home' or trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}" + value_template: "{{ trigger.from_state.state == zone_state and trigger.to_state.state != zone_state }}" action: - alias: "Notify that a person has left the zone" diff --git a/config/configuration.yaml b/config/configuration.yaml index 5a9ff39..7b15906 100644 --- a/config/configuration.yaml +++ b/config/configuration.yaml @@ -17,7 +17,7 @@ homeassistant: http: #si cette balise n'existe pas, ajoutez la, sinon ajouter seulement la suite (pas de duplication) use_x_forwarded_for: true trusted_proxies: - - 10.0.0.16 + - 10.0.0.202 # Loads default set of integrations. Do not remove. default_config: @@ -76,6 +76,91 @@ recorder: auto_purge: true auto_repack: true commit_interval: 5 + include: + domains: + - light + - switch + - cover + #entity_globs: + # - sensor* + entities: + - sensor.tac2100_compteur_puissance_active + - sensor.tac2100_compteur_courant + - sensor.ecowitt_tempin + - sensor.select_sql_query + - sensor.ecu_current_power + - sensor.tac2100_solar_puissance_active + - sensor.disjoncteur_domo_z_power + - sensor.tac2100_compteur_energie_active_totale + - sensor.ecu_today_energy + - sensor.ecowitt_temp + - sensor.ecowitt_dewpoint + - sensor.ecowitt_humidity + - sensor.ecowitt_humidityin + - sensor.ecowitt_baromabs + - sensor.ecowitt_dailyrain + - sensor.ecowitt_eventrain + - sensor.ecowitt_hourlyrain + - sensor.ecowitt_monthlyrain + - sensor.ecowitt_rainrate + - sensor.ecowitt_solarradiation + - sensor.ecowitt_totalrain + - sensor.ecowitt_windgust + - sensor.ecowitt_baromrel + - sensor.ecowitt_uv + - sensor.presence_cuisine_motion_state + - sensor.ecowitt_winddir + - sensor.ecowitt_maxdailygust + - sensor.ecowitt_windspeed + - sensor.ecowitt_frostpoint + - sensor.ecowitt_feelslike + - sensor.qualite_air_co2 + - sensor.geiger_wemos_geiger + - sensor.froling_s3_tdeg_fumee + - sensor.froling_s3_tdeg_board + - sensor.froling_s3_tdeg_depart_chauffage + - sensor.froling_s3_tdeg_chaudiere + - sensor.esp8266_tampon_temp_4d756f_temp_retour_chauff + - sensor.froling_s3_tampon_haut + - sensor.froling_s3_tampon_bas + - sensor.esp8266_tampon_temp_4d756f_tampon_milieu + - sensor.river2_battery_level + - sensor.tac2100_compteur_tension + - sensor.energy_pj1203_solar_power_b + - sensor.energy_pj1203_solar_power_a + - sensor.energy_pj1203_solar_current_b + - sensor.energy_pj1203_solar_current_a + - sensor.energy_pj1203_solar_energy_flow_a + - sensor.energy_pj1203_solar_energy_flow_b + - sensor.energy_pj1203_solar_energy_produced_a + - sensor.energy_pj1203_solar_energy_produced_b + - sensor.ecu_current_power + - sensor.dell_5520_battery_dell5520 + - sensor.blitzortung_lightning_counter + - sensor.compteur_eclair_mensuel + - sensor.pj1203_zb_compteur_current_a + - sensor.pj1203_zb_compteur_current_b + - sensor.pj1203_zb_compteur_power_a + - sensor.pj1203_zb_compteur_power_b + - sensor.capteur_temperature_rond_temperature + - sensor.smart_energy_meter_puissance + - sensor.disjoncteur_domo_z_energy + - sensor.prise_ecran_power + - sensor.kc868_a8_d758d0_d758d0_bh1750_illuminance + - sensor.t_chambre1 + - sensor.t_salle_de_bain + - sensor.h_salle_de_bain + - sensor.t_comble + - sensor.t_chambre2 + - sensor.esp32_4_relays_garage_5a10c8_temperature_garage_5 + - sensor.wemos_cave_temperature + - sensor.ecowitt_heatindex + - sensor.wemos_bureau0_temperature + - sensor.blitzortung_lightning_counter + - sensor.aubess_cafetiere1_power + + #event_types: + # - call_service alarm_control_panel: - platform: manual @@ -93,6 +178,18 @@ alarm_control_panel: arming_time: 0 delay_time: 0 +proxmoxve: + - host: 10.0.0.205 + username: hass@pve + password: Misstibet5* + verify_ssl: false + realm: pve + nodes: + - node: hpproliant + vms: + - 200 + - 201 + api: wake_on_lan: @@ -133,4 +230,3 @@ camera: - platform: local_file name: MF_alerte_tomorrow file_path: /config/www/weather/meteo_france_alerte_tomorrow.png - diff --git a/config/custom_components/ecoflow_cloud/devices/river2.py b/config/custom_components/ecoflow_cloud/devices/river2.py index 314248b..a8e6ad3 100644 --- a/config/custom_components/ecoflow_cloud/devices/river2.py +++ b/config/custom_components/ecoflow_cloud/devices/river2.py @@ -1,40 +1,46 @@ from homeassistant.const import Platform from . import const, BaseDevice, EntityMigration, MigrationAction +from .const import ATTR_DESIGN_CAPACITY, ATTR_FULL_CAPACITY, ATTR_REMAIN_CAPACITY, BATTERY_CHARGING_STATE, \ + MAIN_DESIGN_CAPACITY, MAIN_FULL_CAPACITY, MAIN_REMAIN_CAPACITY from ..entities import BaseSensorEntity, BaseNumberEntity, BaseSwitchEntity, BaseSelectEntity from ..mqtt.ecoflow_mqtt import EcoflowMQTTClient -from ..number import ChargingPowerEntity, MaxBatteryLevelEntity, MinBatteryLevelEntity +from ..number import ChargingPowerEntity, MaxBatteryLevelEntity, MinBatteryLevelEntity, BatteryBackupLevel from ..select import DictSelectEntity, TimeoutDictSelectEntity from ..sensor import LevelSensorEntity, RemainSensorEntity, TempSensorEntity, \ - CyclesSensorEntity, InWattsSensorEntity, OutWattsSensorEntity, VoltSensorEntity, StatusSensorEntity, \ - MilliVoltSensorEntity, InMilliVoltSensorEntity, OutMilliVoltSensorEntity, ChargingStateSensorEntity, \ - CapacitySensorEntity + CyclesSensorEntity, InWattsSensorEntity, OutWattsSensorEntity, VoltSensorEntity, InAmpSensorEntity, \ + InVoltSensorEntity, QuotasStatusSensorEntity, MilliVoltSensorEntity, InMilliVoltSensorEntity, \ + OutMilliVoltSensorEntity, ChargingStateSensorEntity, CapacitySensorEntity from ..switch import EnabledEntity class River2(BaseDevice): + def charging_power_step(self) -> int: return 50 def sensors(self, client: EcoflowMQTTClient) -> list[BaseSensorEntity]: return [ LevelSensorEntity(client, "bms_bmsStatus.soc", const.MAIN_BATTERY_LEVEL) - .attr("bms_bmsStatus.designCap", const.ATTR_DESIGN_CAPACITY, 0) - .attr("bms_bmsStatus.fullCap", const.ATTR_FULL_CAPACITY, 0) - .attr("bms_bmsStatus.remainCap", const.ATTR_REMAIN_CAPACITY, 0), - CapacitySensorEntity(client, "bms_bmsStatus.designCap", const.MAIN_DESIGN_CAPACITY, False), - CapacitySensorEntity(client, "bms_bmsStatus.fullCap", const.MAIN_FULL_CAPACITY, False), - CapacitySensorEntity(client, "bms_bmsStatus.remainCap", const.MAIN_REMAIN_CAPACITY, False), + .attr("bms_bmsStatus.designCap", ATTR_DESIGN_CAPACITY, 0) + .attr("bms_bmsStatus.fullCap", ATTR_FULL_CAPACITY, 0) + .attr("bms_bmsStatus.remainCap", ATTR_REMAIN_CAPACITY, 0), + CapacitySensorEntity(client, "bms_bmsStatus.designCap", MAIN_DESIGN_CAPACITY, False), + CapacitySensorEntity(client, "bms_bmsStatus.fullCap", MAIN_FULL_CAPACITY, False), + CapacitySensorEntity(client, "bms_bmsStatus.remainCap", MAIN_REMAIN_CAPACITY, False), LevelSensorEntity(client, "bms_bmsStatus.soh", const.SOH), LevelSensorEntity(client, "bms_emsStatus.lcdShowSoc", const.COMBINED_BATTERY_LEVEL), - ChargingStateSensorEntity(client, "bms_emsStatus.chgState", const.BATTERY_CHARGING_STATE), + ChargingStateSensorEntity(client, "bms_emsStatus.chgState", BATTERY_CHARGING_STATE), InWattsSensorEntity(client, "pd.wattsInSum", const.TOTAL_IN_POWER), OutWattsSensorEntity(client, "pd.wattsOutSum", const.TOTAL_OUT_POWER), + InAmpSensorEntity(client, "inv.dcInAmp", const.SOLAR_IN_CURRENT), + InVoltSensorEntity(client, "inv.dcInVol", const.SOLAR_IN_VOLTAGE), + InWattsSensorEntity(client, "inv.inputWatts", const.AC_IN_POWER), OutWattsSensorEntity(client, "inv.outputWatts", const.AC_OUT_POWER), @@ -44,14 +50,15 @@ class River2(BaseDevice): InWattsSensorEntity(client, "pd.typecChaWatts", const.TYPE_C_IN_POWER), InWattsSensorEntity(client, "mppt.inWatts", const.SOLAR_IN_POWER), - OutWattsSensorEntity(client, "pd.carWatts", const.DC_OUT_POWER), - OutWattsSensorEntity(client, "pd.typec1Watts", const.TYPEC_1_OUT_POWER), - # both USB-A Ports (the small RIVER 2 has only two) are being summarized under "pd.usb1Watts" - https://github.com/tolwi/hassio-ecoflow-cloud/issues/12#issuecomment-1432837393 + OutWattsSensorEntity(client, "pd.carWatts", const.DC_OUT_POWER), + OutWattsSensorEntity(client, "pd.typec1Watts", const.TYPEC_OUT_POWER), OutWattsSensorEntity(client, "pd.usb1Watts", const.USB_OUT_POWER), + # OutWattsSensorEntity(client, "pd.usb2Watts", const.USB_2_OUT_POWER), RemainSensorEntity(client, "bms_emsStatus.chgRemainTime", const.CHARGE_REMAINING_TIME), RemainSensorEntity(client, "bms_emsStatus.dsgRemainTime", const.DISCHARGE_REMAINING_TIME), + RemainSensorEntity(client, "pd.remainTime", const.REMAINING_TIME), TempSensorEntity(client, "inv.outTemp", "Inv Out Temperature"), CyclesSensorEntity(client, "bms_bmsStatus.cycles", const.CYCLES), @@ -68,8 +75,8 @@ class River2(BaseDevice): MilliVoltSensorEntity(client, "bms_bmsStatus.minCellVol", const.MIN_CELL_VOLT, False), MilliVoltSensorEntity(client, "bms_bmsStatus.maxCellVol", const.MAX_CELL_VOLT, False), + QuotasStatusSensorEntity(client), # FanSensorEntity(client, "bms_emsStatus.fanLevel", "Fan Level"), - StatusSensorEntity(client), ] @@ -86,6 +93,14 @@ class River2(BaseDevice): ChargingPowerEntity(client, "mppt.cfgChgWatts", const.AC_CHARGING_POWER, 100, 360, lambda value: {"moduleType": 5, "operateType": "acChgCfg", "params": {"chgWatts": int(value), "chgPauseFlag": 255}}), + + BatteryBackupLevel(client, "pd.bpPowerSoc", const.BACKUP_RESERVE_LEVEL, 5, 100, + "bms_emsStatus.minDsgSoc", "bms_emsStatus.maxChargeSoc", + lambda value: {"moduleType": 1, "operateType": "watthConfig", + "params": {"isConfig": 1, + "bpPowerSoc": int(value), + "minDsgSoc": 0, + "minChgSoc": 0}}), ] def switches(self, client: EcoflowMQTTClient) -> list[BaseSwitchEntity]: @@ -95,15 +110,28 @@ class River2(BaseDevice): "params": {"enabled": value, "out_voltage": -1, "out_freq": 255, "xboost": 255}}), + EnabledEntity(client, "pd.acAutoOutConfig", const.AC_ALWAYS_ENABLED, + lambda value, params: {"moduleType": 1, "operateType": "acAutoOutConfig", + "params": {"acAutoOutConfig": value, + "minAcOutSoc": int(params.get("bms_emsStatus.minDsgSoc", 0)) + 5}} + ), + EnabledEntity(client, "mppt.cfgAcXboost", const.XBOOST_ENABLED, lambda value: {"moduleType": 5, "operateType": "acOutCfg", "params": {"enabled": 255, "out_voltage": -1, "out_freq": 255, "xboost": value}}), EnabledEntity(client, "pd.carState", const.DC_ENABLED, - lambda value: {"moduleType": 5, "operateType": "mpptCar", "params": {"enabled": value}}) + lambda value: {"moduleType": 5, "operateType": "mpptCar", "params": {"enabled": value}}), + + EnabledEntity(client, "pd.bpPowerSoc", const.BP_ENABLED, + lambda value, params: {"moduleType": 1, "operateType": "watthConfig", + "params": {"isConfig": value, + "bpPowerSoc": value, + "minDsgSoc": 0, + "minChgSoc": 0}}) ] - + def selects(self, client: EcoflowMQTTClient) -> list[BaseSelectEntity]: return [ DictSelectEntity(client, "mppt.dcChgCurrent", const.DC_CHARGE_CURRENT, const.DC_CHARGE_CURRENT_OPTIONS, diff --git a/config/custom_components/ecoflow_cloud/manifest.json b/config/custom_components/ecoflow_cloud/manifest.json index 3bc99a6..9be4317 100644 --- a/config/custom_components/ecoflow_cloud/manifest.json +++ b/config/custom_components/ecoflow_cloud/manifest.json @@ -13,5 +13,5 @@ "reactivex==4.0.4", "protobuf>=4.23.0" ], - "version": "0.13.3" + "version": "0.13.4" } \ No newline at end of file diff --git a/config/custom_components/ecoflow_cloud/translations/pt-PT.json b/config/custom_components/ecoflow_cloud/translations/pt-PT.json new file mode 100644 index 0000000..955fddd --- /dev/null +++ b/config/custom_components/ecoflow_cloud/translations/pt-PT.json @@ -0,0 +1,27 @@ +{ + "title": "EcoFlow-Cloud", + "config": { + "step": { + "user": { + "data": { + "username": "Email do utilizador", + "password": "Palavra-passe do utilizador", + "type": "Tipo de dispositivo", + "name": "Nome do dispositivo", + "device_id": "SN do dispositivo" + } + } + } + }, + "options": { + "step": { + "init": { + "data": { + "power_step": "Controlo deslizante da potência", + "refresh_period_sec": "Período de atualização dos dados (segundos)" + } + } + } + } + } + \ No newline at end of file diff --git a/config/custom_components/frigate/manifest.json b/config/custom_components/frigate/manifest.json index 60119bb..11a7bab 100644 --- a/config/custom_components/frigate/manifest.json +++ b/config/custom_components/frigate/manifest.json @@ -14,5 +14,5 @@ "iot_class": "local_push", "issue_tracker": "https://github.com/blakeblackshear/frigate-hass-integration/issues", "requirements": ["pytz"], - "version": "5.2.0" + "version": "5.3.0" } diff --git a/config/custom_components/frigate/views.py b/config/custom_components/frigate/views.py index 777f9f5..0ad7e1d 100644 --- a/config/custom_components/frigate/views.py +++ b/config/custom_components/frigate/views.py @@ -276,6 +276,13 @@ class NotificationsProxyView(ProxyView): if path.endswith("clip.mp4"): return f"api/events/{event_id}/clip.mp4" + + if path.endswith("event_preview.gif"): + return f"api/events/{event_id}/preview.gif" + + if path.endswith("review_preview.gif"): + return f"api/review/{event_id}/preview" + return None def _permit_request( diff --git a/config/custom_components/irrigation_unlimited/const.py b/config/custom_components/irrigation_unlimited/const.py index d6383e4..4072530 100644 --- a/config/custom_components/irrigation_unlimited/const.py +++ b/config/custom_components/irrigation_unlimited/const.py @@ -6,7 +6,7 @@ DOMAIN = "irrigation_unlimited" DOMAIN_DATA = f"{DOMAIN}_data" COORDINATOR = "coordinator" COMPONENT = "component" -VERSION = "2024.5.0" +VERSION = "2024.8.0" ATTRIBUTION = "Data provided by http://jsonplaceholder.typicode.com/" ISSUE_URL = "https://github.com/rgc99/irrigation_unlimited/issues" @@ -141,6 +141,7 @@ SERVICE_SUSPEND = "suspend" SERVICE_SKIP = "skip" SERVICE_PAUSE = "pause" SERVICE_RESUME = "resume" +SERVICE_GET_INFO = "get_info" # Events EVENT_START = "start" @@ -161,6 +162,7 @@ STATUS_DELAY = "delay" TIMELINE_STATUS = "status" TIMELINE_START = "start" TIMELINE_END = "end" +TIMELINE_SCHEDULE = "schedule" TIMELINE_SCHEDULE_NAME = "schedule_name" TIMELINE_ADJUSTMENT = "adjustment" @@ -211,14 +213,16 @@ ATTR_VOLUME = "volume" ATTR_FLOW_RATE = "flow_rate" ATTR_SWITCH_ENTITIES = "switch_entity_id" ATTR_SEQUENCE_COUNT = "sequence_count" +ATTR_CONTROLLER_ID = "controller_id" +ATTR_ZONE_ID = "zone_id" +ATTR_CONTROLLERS = "controllers" +ATTR_SEQUENCES = "sequences" +ATTR_VERSION = "version" # Resources RES_MANUAL = "Manual" RES_NOT_RUNNING = "not running" RES_NONE = "none" -RES_CONTROLLER = "Controller" -RES_ZONE = "Zone" -RES_MASTER = "Master" RES_TIMELINE_RUNNING = "running" RES_TIMELINE_SCHEDULED = "scheduled" RES_TIMELINE_NEXT = "next" diff --git a/config/custom_components/irrigation_unlimited/history.py b/config/custom_components/irrigation_unlimited/history.py index c532232..8f256f5 100644 --- a/config/custom_components/irrigation_unlimited/history.py +++ b/config/custom_components/irrigation_unlimited/history.py @@ -1,10 +1,15 @@ """History access and caching. This module runs asynchronously collecting and caching history data""" + from datetime import datetime, timedelta from typing import Callable, OrderedDict, Any from homeassistant.core import HomeAssistant, State, CALLBACK_TYPE from homeassistant.util import dt -from homeassistant.components.recorder.const import DATA_INSTANCE as RECORDER_INSTANCE + +try: + from homeassistant.helpers.recorder import DATA_INSTANCE +except ImportError: + from homeassistant.components.recorder.const import DATA_INSTANCE from homeassistant.components.recorder import get_instance from homeassistant.helpers.event import ( async_track_point_in_utc_time, @@ -15,6 +20,7 @@ from homeassistant.const import STATE_ON from .const import ( ATTR_CURRENT_ADJUSTMENT, ATTR_CURRENT_NAME, + ATTR_CURRENT_SCHEDULE, CONF_ENABLED, CONF_HISTORY, CONF_HISTORY_REFRESH, @@ -22,6 +28,7 @@ from .const import ( CONF_REFRESH_INTERVAL, CONF_SPAN, TIMELINE_ADJUSTMENT, + TIMELINE_SCHEDULE, TIMELINE_SCHEDULE_NAME, TIMELINE_START, TIMELINE_END, @@ -166,6 +173,7 @@ class IUHistory: result = OrderedDict() result[TIMELINE_START] = round_seconds_dt(item.last_changed) result[TIMELINE_END] = round_seconds_dt(end) + result[TIMELINE_SCHEDULE] = item.attributes.get(ATTR_CURRENT_SCHEDULE) result[TIMELINE_SCHEDULE_NAME] = item.attributes.get(ATTR_CURRENT_NAME) result[TIMELINE_ADJUSTMENT] = item.attributes.get( ATTR_CURRENT_ADJUSTMENT, "" @@ -197,7 +205,7 @@ class IUHistory: return start = self._stime - self._history_span - if RECORDER_INSTANCE in self._hass.data: + if DATA_INSTANCE in self._hass.data: data = await get_instance(self._hass).async_add_executor_job( history.get_significant_states, self._hass, diff --git a/config/custom_components/irrigation_unlimited/irrigation_unlimited.py b/config/custom_components/irrigation_unlimited/irrigation_unlimited.py index aa79131..1190b2d 100644 --- a/config/custom_components/irrigation_unlimited/irrigation_unlimited.py +++ b/config/custom_components/irrigation_unlimited/irrigation_unlimited.py @@ -165,6 +165,7 @@ from .const import ( RES_TIMELINE_SCHEDULED, TIMELINE_ADJUSTMENT, TIMELINE_END, + TIMELINE_SCHEDULE, TIMELINE_SCHEDULE_NAME, TIMELINE_START, MONTHS, @@ -1064,10 +1065,10 @@ class IUVolume: self._zone = zone # Config parameters self._sensor_id: str = None - self._volume_precision: int = 3 - self._volume_scale: float = 1 - self._flow_rate_precision: int = 3 - self._flow_rate_scale: float = 3600 + self._volume_precision: int = None + self._volume_scale: float = None + self._flow_rate_precision: int = None + self._flow_rate_scale: float = None # Private variables self._callback_remove: CALLBACK_TYPE = None self._start_volume: Decimal = None @@ -1078,9 +1079,11 @@ class IUVolume: str, Callable[[datetime, "IUZone", Decimal, Decimal], None] ] = {} self._flow_rates: list[Decimal] = [] - self._flow_rate_sum = Decimal(0) + self._flow_rate_sum: Decimal = None self._flow_rate_sma: Decimal = None self._sensor_readings: list[IUVolumeSensorReading] = [] + self.reset_config() + self.reset_readings() @property def total(self) -> float | None: @@ -1096,6 +1099,25 @@ class IUVolume: return float(self._flow_rate_sma) return None + def reset_config(self) -> None: + """Reset this object""" + self.end_record(None) + self._sensor_id = None + self._volume_precision = 3 + self._volume_scale = 1 + self._flow_rate_precision = 3 + self._flow_rate_scale = 3600 + + def reset_readings(self) -> None: + """Reset reading parameters""" + self._start_volume = None + self._total_volume = None + self._start_time = None + self._sensor_readings.clear() + self._flow_rates.clear() + self._flow_rate_sum = 0 + self._flow_rate_sma = None + def load(self, config: OrderedDict, all_zones: OrderedDict) -> "IUSwitch": """Load volume data from the configuration""" @@ -1114,6 +1136,8 @@ class IUVolume: CONF_FLOW_RATE_SCALE, self._flow_rate_scale ) + self.reset_config() + self.reset_readings() if all_zones is not None: load_params(all_zones.get(CONF_VOLUME)) load_params(config.get(CONF_VOLUME)) @@ -1174,37 +1198,32 @@ class IUVolume: parameters in the event message""" return event + async def sensor_state_change(self, event: HAEvent): + event = self.event_hook(event) + stime = event.time_fired + try: + value = self.read_sensor(stime) + except ValueError as e: + self._coordinator.logger.log_invalid_meter_value(stime, e) + except IUVolumeSensorError: + self._coordinator.logger.log_invalid_meter_id(stime, self._sensor_id) + else: + self._total_volume = value - self._start_volume + + # Notifiy our trackers + for listener in list(self._listeners.values()): + await listener( + stime, + self._zone, + self._total_volume, + self._flow_rate_sma, + ) + def start_record(self, stime: datetime) -> None: """Start recording volume information""" - - def sensor_state_change(event: HAEvent): - event = self.event_hook(event) - try: - value = self.read_sensor(event.time_fired) - except ValueError as e: - self._coordinator.logger.log_invalid_meter_value(stime, e) - except IUVolumeSensorError: - self._coordinator.logger.log_invalid_meter_id(stime, self._sensor_id) - else: - self._total_volume = value - self._start_volume - - # Notifiy our trackers - for listener in list(self._listeners.values()): - listener( - event.time_fired, - self._zone, - self._total_volume, - self._flow_rate_sma, - ) - + self.reset_readings() if self._sensor_id is None: return - self._start_volume = self._total_volume = None - self._start_time = stime - self._sensor_readings.clear() - self._flow_rates.clear() - self._flow_rate_sum = 0 - self._flow_rate_sma = None try: self._start_volume = self.read_sensor(stime) @@ -1214,7 +1233,7 @@ class IUVolume: self._coordinator.logger.log_invalid_meter_id(stime, self._sensor_id) else: self._callback_remove = async_track_state_change_event( - self._hass, self._sensor_id, sensor_state_change + self._hass, self._sensor_id, self.sensor_state_change ) IUVolume.trackers += 1 @@ -1262,7 +1281,6 @@ class IURunStatus(Enum): return IURunStatus.RUNNING if stime >= end_time: return IURunStatus.EXPIRED - return IURunStatus.UNKNOWN class IURun(IUBase): @@ -1300,6 +1318,14 @@ class IURun(IUBase): self._status = self._get_status(stime) self.master_run: "IURun" = None + def __str__(self) -> str: + return ( + f"status: {self._status.name}, " + f"start: {dt2lstr(self.start_time)}, " + f"end: {dt2lstr(self.end_time)}, " + f"schedule: {self.schedule_name}" + ) + @property def expired(self) -> bool: """Indicate if run has expired""" @@ -1475,22 +1501,24 @@ class IURun(IUBase): """Update the count down timers""" if self.running: self._remaining_time = self._end_time - stime - total_duration: timedelta = self._end_time - self._start_time - time_elapsed: timedelta = stime - self._start_time - self._percent_complete = int((time_elapsed / total_duration) * 100) + duration: timedelta = self._end_time - self._start_time + elapsed: timedelta = stime - self._start_time + self._percent_complete = ( + int((elapsed / duration) * 100) if duration > timedelta(0) else 0 + ) return True return False def pause(self, stime: datetime) -> None: """Change the pause status of the run""" - if self._pause_time is not None: + if self.expired or self._pause_time is not None: return self._pause_time = stime self.update_status(stime) def resume(self, stime: datetime) -> None: """Resume a paused run""" - if self._pause_time is None: + if self.expired or self._pause_time is None: return delta = stime - self._pause_time self._start_time += delta @@ -1503,6 +1531,7 @@ class IURun(IUBase): result = OrderedDict() result[TIMELINE_START] = self._start_time result[TIMELINE_END] = self._end_time + result[TIMELINE_SCHEDULE] = self.schedule.id1 if self.schedule else 0 result[TIMELINE_SCHEDULE_NAME] = self.schedule_name result[TIMELINE_ADJUSTMENT] = self.adjustment return result @@ -1845,13 +1874,15 @@ class IUScheduleQueue(IURunQueue): """Add a manual run to the queue. Cancel any existing manual or running schedule""" - if self._current_run is not None: - self.pop_run(0) - # Remove any existing manual schedules if not queue: + if self._current_run is not None: + self.pop_run(0) + for manual in (run for run in self if run.is_manual()): self.remove_run(manual) + elif self._current_run is not None and not self._current_run.is_manual(): + self.pop_run(0) self._current_run = None self._next_run = None @@ -2651,6 +2682,15 @@ class IUSequenceZone(IUBase): result.append(run) return result + def start_time(runs: list[IURun]) -> datetime: + result: datetime = None + for run in runs: + if result == None or run.start_time < result: + result = run.start_time + return result + + runs = zone_runs() + start = start_time(runs) result = {} result[ATTR_INDEX] = self.index result[ATTR_ENABLED] = self.enabled @@ -2659,7 +2699,8 @@ class IUSequenceZone(IUBase): result[ATTR_ICON] = self.icon() result[ATTR_ADJUSTMENT] = str(self.adjustment) result[ATTR_ZONE_IDS] = self.zone_ids - result[ATTR_DURATION] = str(calc_on_time(zone_runs())) + result[ATTR_START] = dt.as_local(start) if start else None + result[ATTR_DURATION] = str(calc_on_time(runs)) return result def muster(self, stime: datetime) -> IURQStatus: @@ -2732,6 +2773,14 @@ class IUSequenceRun(IUBase): self._remaining_time = timedelta(0) self._percent_complete: int = 0 + def __str__(self) -> str: + return ( + f"status: {self._status.name}, " + f"start: {dt2lstr(self.start_time)}, " + f"end: {dt2lstr(self.end_time)}, " + f"schedule: {self.schedule_name}" + ) + @property def sequence(self) -> "IUSequence": """Return the sequence associated with this run""" @@ -2742,6 +2791,13 @@ class IUSequenceRun(IUBase): """Return the schedule associated with this run""" return self._schedule + @property + def schedule_name(self) -> str: + """Return the name of the schedule""" + if self._schedule is not None: + return self._schedule.name + return RES_MANUAL + @property def start_time(self) -> datetime: """Return the start time for this sequence""" @@ -2882,9 +2938,7 @@ class IUSequenceRun(IUBase): self._accumulated_duration += run.duration zone.request_update() self._runs_pre_allocate.clear() - self._status = IURunStatus.status( - stime, self.start_time, self.end_time, self._paused - ) + self.update_status(stime) def first_zone(self) -> IUZone: """Return the first zone""" @@ -2976,8 +3030,8 @@ class IUSequenceRun(IUBase): run.start_time = max(run.start_time + duration, stime) run.end_time = max(run.end_time + duration, run.start_time) run.duration = run.end_time - run.start_time - run.update_status(stime) run.update_time_remaining(stime) + run.update_status(stime) if self.running: if runs is None: @@ -2993,8 +3047,9 @@ class IUSequenceRun(IUBase): if end_time is None or run.end_time > end_time: end_time = run.end_time self._end_time = end_time - - self.update() + self.update_time_remaining(stime) + self.update_status(stime) + self.update(stime) def skip(self, stime: datetime) -> None: """Skip to the next sequence zone""" @@ -3066,7 +3121,9 @@ class IUSequenceRun(IUBase): return 3 return 0 - def split_run(run: IURun, start: datetime, duration=timedelta(0)) -> None: + def split_run( + run: IURun, szr: IUSequenceZoneRun, start: datetime, duration=timedelta(0) + ) -> None: split = run.zone.runs.add( stime, start, @@ -3075,17 +3132,17 @@ class IUSequenceRun(IUBase): run.schedule, self, ) - self._runs[split] = None + self._runs[split] = szr if self._paused is not None: return runs = self._runs.copy() pause_list = self._runs.copy() over_run = timedelta(0) - for run in runs: + for run, szr in runs.items(): state = run_state(run) if state == 2: - split_run(run, stime - self._controller.postamble) + split_run(run, szr, stime - self._controller.postamble) elif state == 3: # Create a master postamble run out if ( @@ -3095,15 +3152,17 @@ class IUSequenceRun(IUBase): over_run = -self._controller.postamble run.master_run.start_time = stime + over_run run.start_time = stime - split_run(run, stime, over_run) + split_run(run, szr, stime, over_run) elif state == 6: pause_list.pop(run) elif state == 5: - split_run(run, stime) + split_run(run, szr, stime) run.start_time = stime run.master_run.start_time = stime - self._controller.preamble elif state == 4: - split_run(run, run.master_run.end_time - self._controller.postamble) + split_run( + run, szr, run.master_run.end_time - self._controller.postamble + ) if over_run != timedelta(0): self.advance(stime, -over_run, runs) pause_run(stime, pause_list) @@ -3125,9 +3184,7 @@ class IUSequenceRun(IUBase): resume_run(stime, self._runs) self._end_time += stime - self._paused self._paused = None - self._status = IURunStatus.status( - stime, self._start_time, self._end_time, self._paused - ) + self.update_status(stime) next_start = min( (run.start_time for run in self._runs if not run.expired), default=None @@ -3145,30 +3202,32 @@ class IUSequenceRun(IUBase): """Cancel the sequence run""" self.advance(stime, -(self._end_time - stime)) - def update(self) -> bool: - """Update the status of the sequence""" + async def update_volume( + self, stime: datetime, zone: IUZone, volume: Decimal, rate: Decimal + ) -> None: + # pylint: disable=unused-argument + if self._active_zone not in self._volume_stats: + self._volume_stats[self._active_zone] = {} + self._volume_stats[self._active_zone][zone] = volume + self._sequence.volume = sum( + sum(sta.values()) for sta in self._volume_stats.values() + ) + if (limit := self._active_zone.sequence_zone.volume) is not None: + current_vol = sum(self._volume_stats[self._active_zone].values()) + if current_vol >= limit: + await self._coordinator._hass.services.async_call( + DOMAIN, + SERVICE_SKIP, + {ATTR_ENTITY_ID: self._sequence.entity_id}, + ) - def update_volume( - stime: datetime, zone: IUZone, volume: Decimal, rate: Decimal - ) -> None: - # pylint: disable=unused-argument - if self._active_zone not in self._volume_stats: - self._volume_stats[self._active_zone] = {} - self._volume_stats[self._active_zone][zone] = volume - self._sequence.volume = sum( - sum(sta.values()) for sta in self._volume_stats.values() - ) - if (limit := self._active_zone.sequence_zone.volume) is not None: - current_vol = sum(self._volume_stats[self._active_zone].values()) - if current_vol >= limit: - self._coordinator.service_call( - SERVICE_SKIP, self._controller, None, self._sequence, {} - ) + def update(self, stime: datetime) -> bool: + """Update the status of the sequence""" def enable_trackers(sequence_zone: IUSequenceZone) -> None: for zone in sequence_zone.zones: self._volume_trackers.append( - zone.volume.track_volume_change(self.uid, update_volume) + zone.volume.track_volume_change(self.uid, self.update_volume) ) def remove_trackers() -> None: @@ -3176,17 +3235,37 @@ class IUSequenceRun(IUBase): tracker() self._volume_trackers.clear() + def sumarise(stime: datetime) -> dict[IUSequenceZoneRun, dict]: + """Summarise the runs within each sequence zone run. A dict + is returned with start, end and status""" + result: dict[IUSequenceZoneRun, dict] = {} + for run, szr in self._runs.items(): + item = result.get(szr) + if item is None: + item = {} + item["start_time"] = run.start_time + item["end_time"] = run.end_time + result[szr] = item + else: + item["start_time"] = min(item["start_time"], run.start_time) + item["end_time"] = max(item["end_time"], run.end_time) + for item in result.values(): + item["status"] = IURunStatus.status( + stime, item["start_time"], item["end_time"], self._paused + ) + return result + if self.paused: - return False + return not self._sequence.is_paused result = False - for run, sequence_zone_run in self._runs.items(): - if sequence_zone_run is None: - continue - if run.running and not self.running: + sruns = sumarise(stime) + last_date = max((run["end_time"] for run in sruns.values()), default=None) + for szr, run in sruns.items(): + if run["status"] == IURunStatus.RUNNING and not self.running: # Sequence/sequence zone is starting self._status = IURunStatus.RUNNING - self._active_zone = sequence_zone_run - self._current_zone = sequence_zone_run + self._active_zone = szr + self._current_zone = szr self._coordinator.notify_sequence( EVENT_START, self._controller, @@ -3194,24 +3273,24 @@ class IUSequenceRun(IUBase): self._schedule, self, ) - enable_trackers(sequence_zone_run.sequence_zone) + enable_trackers(szr.sequence_zone) self._sequence.volume = None result |= True - elif run.running and sequence_zone_run != self._active_zone: + elif run["status"] == IURunStatus.RUNNING and szr != self._active_zone: # Sequence zone is changing - self._active_zone = sequence_zone_run - self._current_zone = sequence_zone_run + self._active_zone = szr + self._current_zone = szr remove_trackers() - enable_trackers(sequence_zone_run.sequence_zone) + enable_trackers(szr.sequence_zone) result |= True - elif not run.running and sequence_zone_run == self._active_zone: + elif run["status"] != IURunStatus.RUNNING and szr == self._active_zone: # Sequence zone is finishing self._active_zone = None remove_trackers() - self._current_zone = self.next_sequence_zone(sequence_zone_run) - if self.run_index(run) == len(self._runs) - 1: + self._current_zone = self.next_sequence_zone(szr) + if run["end_time"] == last_date: # Sequence is finishing self._status = IURunStatus.EXPIRED self._coordinator.notify_sequence( @@ -3227,7 +3306,7 @@ class IUSequenceRun(IUBase): def update_time_remaining(self, stime: datetime) -> bool: """Update the count down timers""" - if not self.running: + if not (self.running or self.paused): return False self._remaining_time = self._end_time - stime elapsed = stime - self._start_time @@ -3237,6 +3316,14 @@ class IUSequenceRun(IUBase): ) return True + def _get_status(self, stime: datetime) -> IURunStatus: + """Determine the state of this run""" + return IURunStatus.status(stime, self._start_time, self._end_time, self._paused) + + def update_status(self, stime: datetime) -> None: + """Update the status of the run""" + self._status = self._get_status(stime) + def as_dict(self, include_expired=False) -> dict: """Return this sequence run as a dict""" result = {} @@ -3418,7 +3505,7 @@ class IUSequenceQueue(list[IUSequenceRun]): i -= 1 return modified - def update_queue(self) -> IURQStatus: + def update_queue(self, stime: datetime) -> IURQStatus: """Update the run queue""" # pylint: disable=too-many-branches status = IURQStatus(0) @@ -3427,14 +3514,14 @@ class IUSequenceQueue(list[IUSequenceRun]): status |= IURQStatus.SORTED for run in self: - if run.update(): + if run.update(stime): self._current_run = None self._next_run = None status |= IURQStatus.CHANGED if self._current_run is None: for run in self: - if run.running and run.on_time() != timedelta(0): + if (run.running or run.paused) and run.on_time() != timedelta(0): self._current_run = run self._next_run = None status |= IURQStatus.UPDATED @@ -3442,7 +3529,7 @@ class IUSequenceQueue(list[IUSequenceRun]): if self._next_run is None: for run in self: - if not run.running and run.on_time() != timedelta(0): + if not (run.running or run.paused) and run.on_time() != timedelta(0): self._next_run = run status |= IURQStatus.UPDATED break @@ -4074,7 +4161,11 @@ class IUSequence(IUBase): if duration is not None and duration == timedelta(0): duration = None self._controller.muster_sequence( - self._controller.manual_run_start(stime, delay, queue), self, None, duration + stime, + self._controller.manual_run_start(stime, delay, queue), + self, + None, + duration, ) def service_cancel(self, data: MappingProxyType, stime: datetime) -> bool: @@ -4498,6 +4589,7 @@ class IUController(IUBase): def muster_sequence( self, stime: datetime, + earliest: datetime, sequence: IUSequence, schedule: IUSchedule, total_time: timedelta = None, @@ -4554,7 +4646,7 @@ class IUController(IUBase): total_time = sequence_run.build(duration_factor) if total_time > timedelta(0): start_time = init_run_time( - stime, sequence, schedule, sequence_run.first_zone(), total_time + earliest, sequence, schedule, sequence_run.first_zone(), total_time ) if start_time is not None: sequence_run.allocate_runs(stime, start_time) @@ -4594,34 +4686,35 @@ class IUController(IUBase): sequence.runs.clear_runs() zone_status |= sms - if not self._coordinator.tester.enabled or self._coordinator.tester.is_testing: - # pylint: disable=too-many-nested-blocks - # Process sequence schedules - for sequence in self._sequences: - if sequence.is_enabled: - for schedule in sequence.schedules: - if not schedule.enabled: - continue - next_time = stime - while True: - if self.muster_sequence( - next_time, sequence, schedule, None - ).is_empty(): - break - zone_status |= IURQStatus.EXTENDED + # Process sequence schedules + for sequence in self._sequences: + if sequence.is_enabled: + for schedule in sequence.schedules: + if not schedule.enabled: + continue + next_time = stime + while True: + if self.muster_sequence( + stime, next_time, sequence, schedule, None + ).is_empty(): + break + zone_status |= IURQStatus.EXTENDED - # Process zone schedules - for zone in self._zones: - if zone.is_enabled: - zone_status |= zone.muster_schedules(stime) + # Process zone schedules + for zone in self._zones: + if zone.is_enabled: + zone_status |= zone.muster_schedules(stime) # Post processing for sequence in self._sequences: - zone_status |= sequence.runs.update_queue() + sst = sequence.runs.update_queue(stime) + if sst.has_any(IURQStatus.UPDATED): + sequence.request_update() + zone_status |= sst for zone in self._zones: zts = zone.runs.update_queue() - if IURQStatus.CANCELED in zts: + if zts.has_any(IURQStatus.CANCELED | IURQStatus.UPDATED): zone.request_update() zone_status |= zts diff --git a/config/custom_components/irrigation_unlimited/manifest.json b/config/custom_components/irrigation_unlimited/manifest.json index 8a28815..fefe426 100644 --- a/config/custom_components/irrigation_unlimited/manifest.json +++ b/config/custom_components/irrigation_unlimited/manifest.json @@ -15,5 +15,5 @@ "requirements": [ "crontab" ], - "version": "2024.5.0" + "version": "2024.8.0" } \ No newline at end of file diff --git a/config/custom_components/irrigation_unlimited/service.py b/config/custom_components/irrigation_unlimited/service.py index 597bc87..b7985ba 100644 --- a/config/custom_components/irrigation_unlimited/service.py +++ b/config/custom_components/irrigation_unlimited/service.py @@ -1,11 +1,13 @@ """This module handles the HA service call interface""" -from homeassistant.core import ServiceCall, callback + +from homeassistant.core import ServiceCall, SupportsResponse, ServiceResponse, callback from homeassistant.util import dt from homeassistant.helpers import entity_platform from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.service import async_register_admin_service from homeassistant.const import ( SERVICE_RELOAD, + ATTR_ENTITY_ID, ) from .irrigation_unlimited import IUCoordinator @@ -35,6 +37,16 @@ from .const import ( SERVICE_SKIP, SERVICE_PAUSE, SERVICE_RESUME, + SERVICE_GET_INFO, + ATTR_VERSION, + ATTR_CONTROLLERS, + ATTR_CONTROLLER_ID, + ATTR_ZONES, + ATTR_ZONE_ID, + ATTR_SEQUENCES, + ATTR_INDEX, + ATTR_NAME, + ATTR_ZONE_IDS, ) @@ -115,9 +127,54 @@ def register_component_services( """Reload schedule.""" coordinator.service_call(call.service, None, None, None, call.data) + @callback + async def get_info_service_handler(call: ServiceCall) -> ServiceResponse: + """Return configuration""" + data = {} + data[ATTR_VERSION] = "1.0.0" + data[ATTR_CONTROLLERS] = [ + { + ATTR_INDEX: ctl.index, + ATTR_CONTROLLER_ID: ctl.controller_id, + ATTR_NAME: ctl.name, + ATTR_ENTITY_ID: ctl.entity_id, + ATTR_ZONES: [ + { + ATTR_INDEX: zone.index, + ATTR_ZONE_ID: zone.zone_id, + ATTR_NAME: zone.name, + ATTR_ENTITY_ID: zone.entity_id, + } + for zone in ctl.zones + ], + ATTR_SEQUENCES: [ + { + ATTR_INDEX: seq.index, + ATTR_NAME: seq.name, + ATTR_ENTITY_ID: seq.entity_id, + ATTR_ZONES: [ + {ATTR_INDEX: sqz.index, ATTR_ZONE_IDS: sqz.zone_ids} + for sqz in seq.zones + ], + } + for seq in ctl.sequences + ], + } + for ctl in coordinator.controllers + ] + return data + component.hass.services.async_register( DOMAIN, SERVICE_LOAD_SCHEDULE, load_schedule_service_handler, LOAD_SCHEDULE_SCHEMA, ) + + component.hass.services.async_register( + DOMAIN, + SERVICE_GET_INFO, + get_info_service_handler, + {}, + supports_response=SupportsResponse.ONLY, + ) diff --git a/config/custom_components/irrigation_unlimited/services.yaml b/config/custom_components/irrigation_unlimited/services.yaml index abf476d..30b927b 100644 --- a/config/custom_components/irrigation_unlimited/services.yaml +++ b/config/custom_components/irrigation_unlimited/services.yaml @@ -397,6 +397,10 @@ reload: name: Reload description: Reload the configuration +get_info: + name: Get Info + description: Get configuration information + load_schedule: name: Load schedule description: Load a schedule. diff --git a/config/custom_components/pyscript/__init__.py b/config/custom_components/pyscript/__init__.py new file mode 100644 index 0000000..3a96241 --- /dev/null +++ b/config/custom_components/pyscript/__init__.py @@ -0,0 +1,682 @@ +"""Component to allow running Python scripts.""" + +import asyncio +import glob +import json +import logging +import os +import time +import traceback +from typing import Any, Callable, Dict, List, Set, Union + +import voluptuous as vol +from watchdog.events import DirModifiedEvent, FileSystemEvent, FileSystemEventHandler +import watchdog.observers + +from homeassistant.config import async_hass_config_yaml +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.const import ( + EVENT_HOMEASSISTANT_STARTED, + EVENT_HOMEASSISTANT_STOP, + EVENT_STATE_CHANGED, + SERVICE_RELOAD, +) +from homeassistant.core import Config, Event as HAEvent, HomeAssistant, ServiceCall +from homeassistant.exceptions import HomeAssistantError +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.restore_state import DATA_RESTORE_STATE +from homeassistant.loader import bind_hass + +from .const import ( + CONF_ALLOW_ALL_IMPORTS, + CONF_HASS_IS_GLOBAL, + CONFIG_ENTRY, + CONFIG_ENTRY_OLD, + DOMAIN, + FOLDER, + LOGGER_PATH, + REQUIREMENTS_FILE, + SERVICE_JUPYTER_KERNEL_START, + UNSUB_LISTENERS, + WATCHDOG_TASK, +) +from .eval import AstEval +from .event import Event +from .function import Function +from .global_ctx import GlobalContext, GlobalContextMgr +from .jupyter_kernel import Kernel +from .mqtt import Mqtt +from .requirements import install_requirements +from .state import State, StateVal +from .trigger import TrigTime +from .webhook import Webhook + +_LOGGER = logging.getLogger(LOGGER_PATH) + +PYSCRIPT_SCHEMA = vol.Schema( + { + vol.Optional(CONF_ALLOW_ALL_IMPORTS, default=False): cv.boolean, + vol.Optional(CONF_HASS_IS_GLOBAL, default=False): cv.boolean, + }, + extra=vol.ALLOW_EXTRA, +) + +CONFIG_SCHEMA = vol.Schema({DOMAIN: PYSCRIPT_SCHEMA}, extra=vol.ALLOW_EXTRA) + + +async def async_setup(hass: HomeAssistant, config: Config) -> bool: + """Component setup, run import config flow for each entry in config.""" + await restore_state(hass) + if DOMAIN in config: + hass.async_create_task( + hass.config_entries.flow.async_init( + DOMAIN, context={"source": SOURCE_IMPORT}, data=config[DOMAIN] + ) + ) + + return True + + +async def restore_state(hass: HomeAssistant) -> None: + """Restores the persisted pyscript state.""" + # this is a hack accessing hass internals; should re-implement using RestoreEntity + restore_data = hass.data[DATA_RESTORE_STATE] + for entity_id, value in restore_data.last_states.items(): + if entity_id.startswith("pyscript."): + last_state = value.state + hass.states.async_set(entity_id, last_state.state, last_state.attributes) + + +async def update_yaml_config(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Update the yaml config.""" + try: + conf = await async_hass_config_yaml(hass) + except HomeAssistantError as err: + _LOGGER.error(err) + return + + config = PYSCRIPT_SCHEMA(conf.get(DOMAIN, {})) + + # + # If data in config doesn't match config entry, trigger a config import + # so that the config entry can get updated + # + if config != config_entry.data: + await hass.config_entries.flow.async_init(DOMAIN, context={"source": SOURCE_IMPORT}, data=config) + + # + # if hass_is_global or allow_all_imports have changed, we need to reload all scripts + # since they affect all scripts + # + config_save = { + param: config_entry.data.get(param, False) for param in [CONF_HASS_IS_GLOBAL, CONF_ALLOW_ALL_IMPORTS] + } + if DOMAIN not in hass.data: + hass.data.setdefault(DOMAIN, {}) + if CONFIG_ENTRY_OLD in hass.data[DOMAIN]: + old_entry = hass.data[DOMAIN][CONFIG_ENTRY_OLD] + hass.data[DOMAIN][CONFIG_ENTRY_OLD] = config_save + for param in [CONF_HASS_IS_GLOBAL, CONF_ALLOW_ALL_IMPORTS]: + if old_entry.get(param, False) != config_entry.data.get(param, False): + return True + hass.data[DOMAIN][CONFIG_ENTRY_OLD] = config_save + return False + + +def start_global_contexts(global_ctx_only: str = None) -> None: + """Start all the file and apps global contexts.""" + start_list = [] + for global_ctx_name, global_ctx in GlobalContextMgr.items(): + idx = global_ctx_name.find(".") + if idx < 0 or global_ctx_name[0:idx] not in {"file", "apps", "scripts"}: + continue + if global_ctx_only is not None and global_ctx_only != "*": + if global_ctx_name != global_ctx_only and not global_ctx_name.startswith(global_ctx_only + "."): + continue + global_ctx.set_auto_start(True) + start_list.append(global_ctx) + for global_ctx in start_list: + global_ctx.start() + + +async def watchdog_start( + hass: HomeAssistant, pyscript_folder: str, reload_scripts_handler: Callable[[None], None] +) -> None: + """Start watchdog thread to look for changed files in pyscript_folder.""" + if WATCHDOG_TASK in hass.data[DOMAIN]: + return + + class WatchDogHandler(FileSystemEventHandler): + """Class for handling watchdog events.""" + + def __init__( + self, watchdog_q: asyncio.Queue, observer: watchdog.observers.Observer, path: str + ) -> None: + self.watchdog_q = watchdog_q + self._observer = observer + self._observer.schedule(self, path, recursive=True) + if not hass.is_running: + hass.bus.listen_once(EVENT_HOMEASSISTANT_STARTED, self.startup) + else: + self.startup(None) + + hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, self.shutdown) + _LOGGER.debug("watchdog init path=%s", path) + + def startup(self, event: Event | None) -> None: + """Start the observer.""" + _LOGGER.debug("watchdog startup") + self._observer.start() + + def shutdown(self, event: Event | None) -> None: + """Stop the observer.""" + self._observer.stop() + self._observer.join() + _LOGGER.debug("watchdog shutdown") + + def process(self, event: FileSystemEvent) -> None: + """Send watchdog events to main loop task.""" + _LOGGER.debug("watchdog process(%s)", event) + hass.loop.call_soon_threadsafe(self.watchdog_q.put_nowait, event) + + def on_modified(self, event: FileSystemEvent) -> None: + """File modified.""" + self.process(event) + + def on_moved(self, event: FileSystemEvent) -> None: + """File moved.""" + self.process(event) + + def on_created(self, event: FileSystemEvent) -> None: + """File created.""" + self.process(event) + + def on_deleted(self, event: FileSystemEvent) -> None: + """File deleted.""" + self.process(event) + + async def task_watchdog(watchdog_q: asyncio.Queue) -> None: + def check_event(event, do_reload: bool) -> bool: + """Check if event should trigger a reload.""" + if event.is_directory: + # don't reload if it's just a directory modified + if isinstance(event, DirModifiedEvent): + return do_reload + return True + # only reload if it's a script, yaml, or requirements.txt file + for valid_suffix in [".py", ".yaml", "/" + REQUIREMENTS_FILE]: + if event.src_path.endswith(valid_suffix): + return True + return do_reload + + while True: + try: + # + # since some file/dir changes create multiple events, we consume all + # events in a small window; first wait indefinitely for next event + # + do_reload = check_event(await watchdog_q.get(), False) + # + # now consume all additional events with 50ms timeout or 500ms elapsed + # + t_start = time.monotonic() + while time.monotonic() - t_start < 0.5: + try: + do_reload = check_event( + await asyncio.wait_for(watchdog_q.get(), timeout=0.05), do_reload + ) + except asyncio.TimeoutError: + break + if do_reload: + await reload_scripts_handler(None) + + except asyncio.CancelledError: + raise + except Exception: + _LOGGER.error("task_watchdog: got exception %s", traceback.format_exc(-1)) + + watchdog_q = asyncio.Queue(0) + observer = watchdog.observers.Observer() + if observer is not None: + # don't run watchdog when we are testing (Observer() patches to None) + hass.data[DOMAIN][WATCHDOG_TASK] = Function.create_task(task_watchdog(watchdog_q)) + + await hass.async_add_executor_job(WatchDogHandler, watchdog_q, observer, pyscript_folder) + _LOGGER.debug("watchdog started job and task folder=%s", pyscript_folder) + + +async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Initialize the pyscript config entry.""" + global_ctx_only = None + doing_reload = False + if Function.hass: + # + # reload yaml if this isn't the first time (ie, on reload) + # + doing_reload = True + if await update_yaml_config(hass, config_entry): + global_ctx_only = "*" + + Function.init(hass) + Event.init(hass) + Mqtt.init(hass) + TrigTime.init(hass) + State.init(hass) + Webhook.init(hass) + State.register_functions() + GlobalContextMgr.init() + + pyscript_folder = hass.config.path(FOLDER) + if not await hass.async_add_executor_job(os.path.isdir, pyscript_folder): + _LOGGER.debug("Folder %s not found in configuration folder, creating it", FOLDER) + await hass.async_add_executor_job(os.makedirs, pyscript_folder) + + hass.data.setdefault(DOMAIN, {}) + hass.data[DOMAIN][CONFIG_ENTRY] = config_entry + hass.data[DOMAIN][UNSUB_LISTENERS] = [] + + State.set_pyscript_config(config_entry.data) + + await install_requirements(hass, config_entry, pyscript_folder) + await load_scripts(hass, config_entry.data, global_ctx_only=global_ctx_only) + + async def reload_scripts_handler(call: ServiceCall) -> None: + """Handle reload service calls.""" + _LOGGER.debug("reload: yaml, reloading scripts, and restarting") + + global_ctx_only = call.data.get("global_ctx", None) if call else None + + if await update_yaml_config(hass, config_entry): + global_ctx_only = "*" + State.set_pyscript_config(config_entry.data) + + await State.get_service_params() + + await install_requirements(hass, config_entry, pyscript_folder) + await load_scripts(hass, config_entry.data, global_ctx_only=global_ctx_only) + + start_global_contexts(global_ctx_only=global_ctx_only) + + hass.services.async_register(DOMAIN, SERVICE_RELOAD, reload_scripts_handler) + + async def jupyter_kernel_start(call: ServiceCall) -> None: + """Handle Jupyter kernel start call.""" + _LOGGER.debug("service call to jupyter_kernel_start: %s", call.data) + + global_ctx_name = GlobalContextMgr.new_name("jupyter_") + global_ctx = GlobalContext( + global_ctx_name, global_sym_table={"__name__": global_ctx_name}, manager=GlobalContextMgr + ) + global_ctx.set_auto_start(True) + GlobalContextMgr.set(global_ctx_name, global_ctx) + + ast_ctx = AstEval(global_ctx_name, global_ctx) + Function.install_ast_funcs(ast_ctx) + kernel = Kernel(call.data, ast_ctx, global_ctx, global_ctx_name) + await kernel.session_start() + hass.states.async_set(call.data["state_var"], json.dumps(kernel.get_ports())) + + def state_var_remove(): + hass.states.async_remove(call.data["state_var"]) + + kernel.set_session_cleanup_callback(state_var_remove) + + hass.services.async_register(DOMAIN, SERVICE_JUPYTER_KERNEL_START, jupyter_kernel_start) + + async def state_changed(event: HAEvent) -> None: + var_name = event.data["entity_id"] + if event.data.get("new_state", None): + new_val = StateVal(event.data["new_state"]) + else: + # state variable has been deleted + new_val = None + + if event.data.get("old_state", None): + old_val = StateVal(event.data["old_state"]) + else: + # no previous state + old_val = None + + new_vars = {var_name: new_val, f"{var_name}.old": old_val} + func_args = { + "trigger_type": "state", + "var_name": var_name, + "value": new_val, + "old_value": old_val, + "context": event.context, + } + await State.update(new_vars, func_args) + + async def hass_started(event: HAEvent) -> None: + _LOGGER.debug("adding state changed listener and starting global contexts") + await State.get_service_params() + hass.data[DOMAIN][UNSUB_LISTENERS].append(hass.bus.async_listen(EVENT_STATE_CHANGED, state_changed)) + start_global_contexts() + + async def hass_stop(event: HAEvent) -> None: + if WATCHDOG_TASK in hass.data[DOMAIN]: + Function.reaper_cancel(hass.data[DOMAIN][WATCHDOG_TASK]) + del hass.data[DOMAIN][WATCHDOG_TASK] + + _LOGGER.debug("stopping global contexts") + await unload_scripts(unload_all=True) + # sync with waiter, and then tell waiter and reaper tasks to exit + await Function.waiter_sync() + await Function.waiter_stop() + await Function.reaper_stop() + + # Store callbacks to event listeners so we can unsubscribe on unload + hass.data[DOMAIN][UNSUB_LISTENERS].append( + hass.bus.async_listen(EVENT_HOMEASSISTANT_STARTED, hass_started) + ) + hass.data[DOMAIN][UNSUB_LISTENERS].append(hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, hass_stop)) + + await watchdog_start(hass, pyscript_folder, reload_scripts_handler) + + if doing_reload: + start_global_contexts(global_ctx_only="*") + + return True + + +async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: + """Unload a config entry.""" + _LOGGER.info("Unloading all scripts") + await unload_scripts(unload_all=True) + + for unsub_listener in hass.data[DOMAIN][UNSUB_LISTENERS]: + unsub_listener() + hass.data[DOMAIN][UNSUB_LISTENERS] = [] + + # sync with waiter, and then tell waiter and reaper tasks to exit + await Function.waiter_sync() + await Function.waiter_stop() + await Function.reaper_stop() + + return True + + +async def unload_scripts(global_ctx_only: str = None, unload_all: bool = False) -> None: + """Unload all scripts from GlobalContextMgr with given name prefixes.""" + ctx_delete = {} + for global_ctx_name, global_ctx in GlobalContextMgr.items(): + if not unload_all: + idx = global_ctx_name.find(".") + if idx < 0 or global_ctx_name[0:idx] not in {"file", "apps", "modules", "scripts"}: + continue + if global_ctx_only is not None: + if global_ctx_name != global_ctx_only and not global_ctx_name.startswith(global_ctx_only + "."): + continue + global_ctx.stop() + ctx_delete[global_ctx_name] = global_ctx + for global_ctx_name, global_ctx in ctx_delete.items(): + GlobalContextMgr.delete(global_ctx_name) + await Function.waiter_sync() + + +@bind_hass +async def load_scripts(hass: HomeAssistant, config_data: Dict[str, Any], global_ctx_only: str = None): + """Load all python scripts in FOLDER.""" + + class SourceFile: + """Class for information about a source file.""" + + def __init__( + self, + global_ctx_name=None, + file_path=None, + rel_path=None, + rel_import_path=None, + fq_mod_name=None, + check_config=None, + app_config=None, + source=None, + mtime=None, + autoload=None, + ): + self.global_ctx_name = global_ctx_name + self.file_path = file_path + self.rel_path = rel_path + self.rel_import_path = rel_import_path + self.fq_mod_name = fq_mod_name + self.check_config = check_config + self.app_config = app_config + self.source = source + self.mtime = mtime + self.autoload = autoload + self.force = False + + pyscript_dir = hass.config.path(FOLDER) + + def glob_read_files( + load_paths: List[Set[Union[str, bool]]], apps_config: Dict[str, Any] + ) -> Dict[str, SourceFile]: + """Expand globs and read all the source files.""" + ctx2source = {} + for path, match, check_config, autoload in load_paths: + for this_path in sorted(glob.glob(os.path.join(pyscript_dir, path, match), recursive=True)): + rel_import_path = None + rel_path = this_path + if rel_path.startswith(pyscript_dir): + rel_path = rel_path[len(pyscript_dir) :] + if rel_path.startswith("/"): + rel_path = rel_path[1:] + if rel_path[0] == "#" or rel_path.find("/#") >= 0: + # skip "commented" files and directories + continue + mod_name = rel_path[0:-3] + if mod_name.endswith("/__init__"): + rel_import_path = mod_name + mod_name = mod_name[: -len("/__init__")] + mod_name = mod_name.replace("/", ".") + if path == "": + global_ctx_name = f"file.{mod_name}" + fq_mod_name = mod_name + else: + fq_mod_name = global_ctx_name = mod_name + i = fq_mod_name.find(".") + if i >= 0: + fq_mod_name = fq_mod_name[i + 1 :] + app_config = None + + if global_ctx_name in ctx2source: + # the globs result in apps/APP/__init__.py matching twice, so skip the 2nd time + # also skip apps/APP.py if apps/APP/__init__.py is present + continue + + if check_config: + app_name = fq_mod_name + i = app_name.find(".") + if i >= 0: + app_name = app_name[0:i] + if not isinstance(apps_config, dict) or app_name not in apps_config: + _LOGGER.debug( + "load_scripts: skipping %s (app_name=%s) because config not present", + this_path, + app_name, + ) + continue + app_config = apps_config[app_name] + + try: + with open(this_path, encoding="utf-8") as file_desc: + source = file_desc.read() + mtime = os.path.getmtime(this_path) + except Exception as exc: + _LOGGER.error("load_scripts: skipping %s due to exception %s", this_path, exc) + continue + + ctx2source[global_ctx_name] = SourceFile( + global_ctx_name=global_ctx_name, + file_path=this_path, + rel_path=rel_path, + rel_import_path=rel_import_path, + fq_mod_name=fq_mod_name, + check_config=check_config, + app_config=app_config, + source=source, + mtime=mtime, + autoload=autoload, + ) + + return ctx2source + + load_paths = [ + # path, glob, check_config, autoload + ["", "*.py", False, True], + ["apps", "*/__init__.py", True, True], + ["apps", "*.py", True, True], + ["apps", "*/**/*.py", False, False], + ["modules", "*/__init__.py", False, False], + ["modules", "*.py", False, False], + ["modules", "*/**/*.py", False, False], + ["scripts", "**/*.py", False, True], + ] + + # + # get current global contexts + # + ctx_all = {} + for global_ctx_name, global_ctx in GlobalContextMgr.items(): + idx = global_ctx_name.find(".") + if idx < 0 or global_ctx_name[0:idx] not in {"file", "apps", "modules", "scripts"}: + continue + ctx_all[global_ctx_name] = global_ctx + + # + # get list and contents of all source files + # + apps_config = config_data.get("apps", None) + ctx2files = await hass.async_add_executor_job(glob_read_files, load_paths, apps_config) + + # + # figure out what to reload based on global_ctx_only and what's changed + # + ctx_delete = set() + if global_ctx_only is not None and global_ctx_only != "*": + if global_ctx_only not in ctx_all and global_ctx_only not in ctx2files: + _LOGGER.error("pyscript.reload: no global context '%s' to reload", global_ctx_only) + return + if global_ctx_only not in ctx2files: + ctx_delete.add(global_ctx_only) + else: + ctx2files[global_ctx_only].force = True + elif global_ctx_only == "*": + ctx_delete = set(ctx_all.keys()) + for _, src_info in ctx2files.items(): + src_info.force = True + else: + # delete all global_ctxs that aren't present in current files + for global_ctx_name, global_ctx in ctx_all.items(): + if global_ctx_name not in ctx2files: + ctx_delete.add(global_ctx_name) + # delete all global_ctxs that have changeed source or mtime + for global_ctx_name, src_info in ctx2files.items(): + if global_ctx_name in ctx_all: + ctx = ctx_all[global_ctx_name] + if ( + src_info.source != ctx.get_source() + or src_info.app_config != ctx.get_app_config() + or src_info.mtime != ctx.get_mtime() + ): + ctx_delete.add(global_ctx_name) + src_info.force = True + else: + src_info.force = src_info.autoload + + # + # force reload if any files uses a module that is bring reloaded by + # recursively following each import; first find which modules are + # being reloaded + # + will_reload = set() + for global_ctx_name, src_info in ctx2files.items(): + if global_ctx_name.startswith("modules.") and (global_ctx_name in ctx_delete or src_info.force): + parts = global_ctx_name.split(".") + root = f"{parts[0]}.{parts[1]}" + will_reload.add(root) + + if len(will_reload) > 0: + + def import_recurse(ctx_name, visited, ctx2imports): + if ctx_name in visited or ctx_name in ctx2imports: + return ctx2imports.get(ctx_name, set()) + visited.add(ctx_name) + ctx = GlobalContextMgr.get(ctx_name) + if not ctx: + return set() + ctx2imports[ctx_name] = set() + for imp_name in ctx.get_imports(): + ctx2imports[ctx_name].add(imp_name) + ctx2imports[ctx_name].update(import_recurse(imp_name, visited, ctx2imports)) + return ctx2imports[ctx_name] + + ctx2imports = {} + for global_ctx_name, global_ctx in ctx_all.items(): + if global_ctx_name not in ctx2imports: + visited = set() + import_recurse(global_ctx_name, visited, ctx2imports) + for mod_name in ctx2imports.get(global_ctx_name, set()): + parts = mod_name.split(".") + root = f"{parts[0]}.{parts[1]}" + if root in will_reload: + ctx_delete.add(global_ctx_name) + if global_ctx_name in ctx2files: + ctx2files[global_ctx_name].force = True + + # + # if any file in an app or module has changed, then reload just the top-level + # __init__.py or module/app .py file, and delete everything else + # + done = set() + for global_ctx_name, src_info in ctx2files.items(): + if not src_info.force: + continue + if not global_ctx_name.startswith("apps.") and not global_ctx_name.startswith("modules."): + continue + parts = global_ctx_name.split(".") + root = f"{parts[0]}.{parts[1]}" + if root in done: + continue + pkg_path = f"{parts[0]}/{parts[1]}/__init__.py" + mod_path = f"{parts[0]}/{parts[1]}.py" + for ctx_name, this_src_info in ctx2files.items(): + if ctx_name == root or ctx_name.startswith(f"{root}."): + if this_src_info.rel_path in {pkg_path, mod_path}: + this_src_info.force = True + else: + this_src_info.force = False + ctx_delete.add(ctx_name) + done.add(root) + + # + # delete contexts that are no longer needed or will be reloaded + # + for global_ctx_name in ctx_delete: + if global_ctx_name in ctx_all: + global_ctx = ctx_all[global_ctx_name] + global_ctx.stop() + if global_ctx_name not in ctx2files or not ctx2files[global_ctx_name].autoload: + _LOGGER.info("Unloaded %s", global_ctx.get_file_path()) + GlobalContextMgr.delete(global_ctx_name) + await Function.waiter_sync() + + # + # now load the requested files, and files that depend on loaded files + # + for global_ctx_name, src_info in sorted(ctx2files.items()): + if not src_info.autoload or not src_info.force: + continue + global_ctx = GlobalContext( + src_info.global_ctx_name, + global_sym_table={"__name__": src_info.fq_mod_name}, + manager=GlobalContextMgr, + rel_import_path=src_info.rel_import_path, + app_config=src_info.app_config, + source=src_info.source, + mtime=src_info.mtime, + ) + reload = src_info.global_ctx_name in ctx_delete + await GlobalContextMgr.load_file( + global_ctx, src_info.file_path, source=src_info.source, reload=reload + ) diff --git a/config/custom_components/pyscript/config_flow.py b/config/custom_components/pyscript/config_flow.py new file mode 100644 index 0000000..11ff4ec --- /dev/null +++ b/config/custom_components/pyscript/config_flow.py @@ -0,0 +1,139 @@ +"""Config flow for pyscript.""" +import json +from typing import Any, Dict + +import voluptuous as vol + +from homeassistant import config_entries +from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry +from homeassistant.core import callback + +from .const import CONF_ALLOW_ALL_IMPORTS, CONF_HASS_IS_GLOBAL, CONF_INSTALLED_PACKAGES, DOMAIN + +CONF_BOOL_ALL = {CONF_ALLOW_ALL_IMPORTS, CONF_HASS_IS_GLOBAL} + +PYSCRIPT_SCHEMA = vol.Schema( + { + vol.Optional(CONF_ALLOW_ALL_IMPORTS, default=False): bool, + vol.Optional(CONF_HASS_IS_GLOBAL, default=False): bool, + }, + extra=vol.ALLOW_EXTRA, +) + + +class PyscriptOptionsConfigFlow(config_entries.OptionsFlow): + """Handle a pyscript options flow.""" + + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialize pyscript options flow.""" + self.config_entry = config_entry + self._show_form = False + + async def async_step_init(self, user_input: Dict[str, Any] = None) -> Dict[str, Any]: + """Manage the pyscript options.""" + if self.config_entry.source == SOURCE_IMPORT: + self._show_form = True + return await self.async_step_no_ui_configuration_allowed() + + if user_input is None: + return self.async_show_form( + step_id="init", + data_schema=vol.Schema( + { + vol.Optional(name, default=self.config_entry.data.get(name, False)): bool + for name in CONF_BOOL_ALL + }, + extra=vol.ALLOW_EXTRA, + ), + ) + + if any( + name not in self.config_entry.data or user_input[name] != self.config_entry.data[name] + for name in CONF_BOOL_ALL + ): + updated_data = self.config_entry.data.copy() + updated_data.update(user_input) + self.hass.config_entries.async_update_entry(entry=self.config_entry, data=updated_data) + return self.async_create_entry(title="", data={}) + + self._show_form = True + return await self.async_step_no_update() + + async def async_step_no_ui_configuration_allowed( + self, user_input: Dict[str, Any] = None + ) -> Dict[str, Any]: + """Tell user no UI configuration is allowed.""" + if self._show_form: + self._show_form = False + return self.async_show_form(step_id="no_ui_configuration_allowed", data_schema=vol.Schema({})) + + return self.async_create_entry(title="", data={}) + + async def async_step_no_update(self, user_input: Dict[str, Any] = None) -> Dict[str, Any]: + """Tell user no update to process.""" + if self._show_form: + self._show_form = False + return self.async_show_form(step_id="no_update", data_schema=vol.Schema({})) + + return self.async_create_entry(title="", data={}) + + +class PyscriptConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): + """Handle a pyscript config flow.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_PUSH + + @staticmethod + @callback + def async_get_options_flow(config_entry: ConfigEntry) -> PyscriptOptionsConfigFlow: + """Get the options flow for this handler.""" + return PyscriptOptionsConfigFlow(config_entry) + + async def async_step_user(self, user_input: Dict[str, Any] = None) -> Dict[str, Any]: + """Handle a flow initialized by the user.""" + if user_input is not None: + if len(self.hass.config_entries.async_entries(DOMAIN)) > 0: + return self.async_abort(reason="single_instance_allowed") + + await self.async_set_unique_id(DOMAIN) + return self.async_create_entry(title=DOMAIN, data=user_input) + + return self.async_show_form(step_id="user", data_schema=PYSCRIPT_SCHEMA) + + async def async_step_import(self, import_config: Dict[str, Any] = None) -> Dict[str, Any]: + """Import a config entry from configuration.yaml.""" + # Convert OrderedDict to dict + import_config = json.loads(json.dumps(import_config)) + + # Check if import config entry matches any existing config entries + # so we can update it if necessary + entries = self.hass.config_entries.async_entries(DOMAIN) + if entries: + entry = entries[0] + updated_data = entry.data.copy() + + # Update values for all keys, excluding `allow_all_imports` for entries + # set up through the UI. + for key, val in import_config.items(): + if entry.source == SOURCE_IMPORT or key not in CONF_BOOL_ALL: + updated_data[key] = val + + # Remove values for all keys in entry.data that are not in the imported config, + # excluding `allow_all_imports` for entries set up through the UI. + for key in entry.data: + if ( + (entry.source == SOURCE_IMPORT or key not in CONF_BOOL_ALL) + and key != CONF_INSTALLED_PACKAGES + and key not in import_config + ): + updated_data.pop(key) + + # Update and reload entry if data needs to be updated + if updated_data != entry.data: + self.hass.config_entries.async_update_entry(entry=entry, data=updated_data) + return self.async_abort(reason="updated_entry") + + return self.async_abort(reason="already_configured") + + return await self.async_step_user(user_input=import_config) diff --git a/config/custom_components/pyscript/const.py b/config/custom_components/pyscript/const.py new file mode 100644 index 0000000..26b675c --- /dev/null +++ b/config/custom_components/pyscript/const.py @@ -0,0 +1,63 @@ +"""Define pyscript-wide constants.""" + +# +# 2023.7 supports service response; handle older versions by defaulting enum +# Should eventually deprecate this and just use SupportsResponse import +# +try: + from homeassistant.core import SupportsResponse + + SERVICE_RESPONSE_NONE = SupportsResponse.NONE + SERVICE_RESPONSE_OPTIONAL = SupportsResponse.OPTIONAL + SERVICE_RESPONSE_ONLY = SupportsResponse.ONLY +except ImportError: + SERVICE_RESPONSE_NONE = None + SERVICE_RESPONSE_OPTIONAL = None + SERVICE_RESPONSE_ONLY = None + +DOMAIN = "pyscript" + +CONFIG_ENTRY = "config_entry" +CONFIG_ENTRY_OLD = "config_entry_old" +UNSUB_LISTENERS = "unsub_listeners" + +FOLDER = "pyscript" + +UNPINNED_VERSION = "_unpinned_version" + +ATTR_INSTALLED_VERSION = "installed_version" +ATTR_SOURCES = "sources" +ATTR_VERSION = "version" + +CONF_ALLOW_ALL_IMPORTS = "allow_all_imports" +CONF_HASS_IS_GLOBAL = "hass_is_global" +CONF_INSTALLED_PACKAGES = "_installed_packages" + +SERVICE_JUPYTER_KERNEL_START = "jupyter_kernel_start" + +LOGGER_PATH = "custom_components.pyscript" + +REQUIREMENTS_FILE = "requirements.txt" +REQUIREMENTS_PATHS = ("", "apps/*", "modules/*", "scripts/**") + +WATCHDOG_TASK = "watch_dog_task" + +ALLOWED_IMPORTS = { + "black", + "cmath", + "datetime", + "decimal", + "fractions", + "functools", + "homeassistant.const", + "isort", + "json", + "math", + "number", + "random", + "re", + "statistics", + "string", + "time", + "voluptuous", +} diff --git a/config/custom_components/pyscript/entity.py b/config/custom_components/pyscript/entity.py new file mode 100644 index 0000000..8150392 --- /dev/null +++ b/config/custom_components/pyscript/entity.py @@ -0,0 +1,19 @@ +"""Entity Classes.""" +from homeassistant.const import STATE_UNKNOWN +from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.typing import StateType + + +class PyscriptEntity(RestoreEntity): + """Generic Pyscript Entity.""" + + _attr_extra_state_attributes: dict + _attr_state: StateType = STATE_UNKNOWN + + def set_state(self, state): + """Set the state.""" + self._attr_state = state + + def set_attributes(self, attributes): + """Set Attributes.""" + self._attr_extra_state_attributes = attributes diff --git a/config/custom_components/pyscript/eval.py b/config/custom_components/pyscript/eval.py new file mode 100644 index 0000000..958dce0 --- /dev/null +++ b/config/custom_components/pyscript/eval.py @@ -0,0 +1,2313 @@ +"""Python interpreter for pyscript.""" + +import ast +import asyncio +import builtins +from collections import OrderedDict +import functools +import importlib +import inspect +import io +import keyword +import logging +import sys +import time +import weakref + +import yaml + +from homeassistant.const import SERVICE_RELOAD +from homeassistant.helpers.service import async_set_service_schema + +from .const import ( + ALLOWED_IMPORTS, + CONF_ALLOW_ALL_IMPORTS, + CONFIG_ENTRY, + DOMAIN, + LOGGER_PATH, + SERVICE_JUPYTER_KERNEL_START, + SERVICE_RESPONSE_NONE, +) +from .function import Function +from .state import State + +_LOGGER = logging.getLogger(LOGGER_PATH + ".eval") + +# +# Built-ins to exclude to improve security or avoid i/o +# +BUILTIN_EXCLUDE = { + "breakpoint", + "compile", + "input", + "memoryview", + "open", + "print", +} + +TRIG_DECORATORS = { + "time_trigger", + "state_trigger", + "event_trigger", + "mqtt_trigger", + "webhook_trigger", + "state_active", + "time_active", + "task_unique", +} + +TRIG_SERV_DECORATORS = TRIG_DECORATORS.union({"service"}) + +COMP_DECORATORS = { + "pyscript_compile", + "pyscript_executor", +} + +TRIGGER_KWARGS = { + "context", + "event_type", + "old_value", + "payload", + "payload_obj", + "qos", + "topic", + "trigger_type", + "trigger_time", + "var_name", + "value", + "webhook_id", +} + +WEBHOOK_METHODS = { + "GET", + "HEAD", + "POST", + "PUT", +} + + +def ast_eval_exec_factory(ast_ctx, mode): + """Generate a function that executes eval() or exec() with given ast_ctx.""" + + async def eval_func(arg_str, eval_globals=None, eval_locals=None): + eval_ast = AstEval(ast_ctx.name, ast_ctx.global_ctx) + eval_ast.parse(arg_str, f"{mode}()", mode=mode) + if eval_ast.exception_obj: + raise eval_ast.exception_obj + eval_ast.local_sym_table = ast_ctx.local_sym_table + if eval_globals is not None: + eval_ast.global_sym_table = eval_globals + if eval_locals is not None: + eval_ast.sym_table_stack = [eval_globals] + eval_ast.sym_table = eval_locals + else: + eval_ast.sym_table_stack = [] + eval_ast.sym_table = eval_globals + else: + eval_ast.sym_table_stack = ast_ctx.sym_table_stack.copy() + if ast_ctx.sym_table == ast_ctx.global_sym_table: + eval_ast.sym_table = ast_ctx.sym_table + else: + eval_ast.sym_table = ast_ctx.sym_table.copy() + eval_ast.sym_table.update(ast_ctx.user_locals) + to_delete = set() + for var, value in eval_ast.sym_table.items(): + if isinstance(value, EvalLocalVar): + if value.is_defined(): + eval_ast.sym_table[var] = value.get() + else: + to_delete.add(var) + for var in to_delete: + del eval_ast.sym_table[var] + + eval_ast.curr_func = None + try: + eval_result = await eval_ast.aeval(eval_ast.ast) + except Exception as err: + ast_ctx.exception_obj = err + ast_ctx.exception = f"Exception in {ast_ctx.filename} line {ast_ctx.lineno} column {ast_ctx.col_offset}: {eval_ast.exception}" + ast_ctx.exception_long = ( + ast_ctx.format_exc(err, ast_ctx.lineno, ast_ctx.col_offset, short=True) + + "\n" + + eval_ast.exception_long + ) + raise + # + # save variables only in the locals scope + # + if eval_globals is None and eval_ast.sym_table != ast_ctx.sym_table: + for var, value in eval_ast.sym_table.items(): + if var in ast_ctx.global_sym_table and value == ast_ctx.global_sym_table[var]: + continue + if var not in ast_ctx.sym_table and ( + ast_ctx.curr_func is None or var not in ast_ctx.curr_func.local_names + ): + ast_ctx.user_locals[var] = value + return eval_result + + return eval_func + + +def ast_eval_factory(ast_ctx): + """Generate a function that executes eval() with given ast_ctx.""" + return ast_eval_exec_factory(ast_ctx, "eval") + + +def ast_exec_factory(ast_ctx): + """Generate a function that executes exec() with given ast_ctx.""" + return ast_eval_exec_factory(ast_ctx, "exec") + + +def ast_globals_factory(ast_ctx): + """Generate a globals() function with given ast_ctx.""" + + async def globals_func(): + return ast_ctx.global_sym_table + + return globals_func + + +def ast_locals_factory(ast_ctx): + """Generate a locals() function with given ast_ctx.""" + + async def locals_func(): + if ast_ctx.sym_table == ast_ctx.global_sym_table: + return ast_ctx.sym_table + local_sym_table = ast_ctx.sym_table.copy() + local_sym_table.update(ast_ctx.user_locals) + to_delete = set() + for var, value in local_sym_table.items(): + if isinstance(value, EvalLocalVar): + if value.is_defined(): + local_sym_table[var] = value.get() + else: + to_delete.add(var) + for var in to_delete: + del local_sym_table[var] + return local_sym_table + + return locals_func + + +# +# Built-in functions that are also passed the ast context +# +BUILTIN_AST_FUNCS_FACTORY = { + "eval": ast_eval_factory, + "exec": ast_exec_factory, + "globals": ast_globals_factory, + "locals": ast_locals_factory, +} + + +# +# Objects returned by return, break and continue statements that change execution flow, +# or objects returned that capture particular information +# +class EvalStopFlow: + """Denotes a statement or action that stops execution flow, eg: return, break etc.""" + + _name = None + + def name(self): + """Return short name.""" + return self._name + + +class EvalReturn(EvalStopFlow): + """Return statement.""" + + _name = "return" + + def __init__(self, value): + """Initialize return statement value.""" + self.value = value + + +class EvalBreak(EvalStopFlow): + """Break statement.""" + + _name = "break" + + +class EvalContinue(EvalStopFlow): + """Continue statement.""" + + _name = "continue" + + +class EvalLocalVar: + """Wrapper for local variable symtable entry.""" + + def __init__(self, name, **kwargs): + """Initialize value of local symbol.""" + self.name = name + self.defined = False + if "value" in kwargs: + self.value = kwargs["value"] + self.defined = True + + def get(self): + """Get value of local symbol.""" + if not self.defined: + raise NameError(f"name '{self.name}' is not defined") + return self.value + + def get_name(self): + """Get name of local symbol.""" + return self.name + + def set(self, value): + """Set value of local symbol.""" + self.value = value + self.defined = True + + def is_defined(self): + """Return whether value is defined.""" + return self.defined + + def set_undefined(self): + """Set local symbol to undefined.""" + self.defined = False + + def __getattr__(self, attr): + """Get attribute of local variable.""" + if not self.defined: + raise NameError(f"name '{self.name}' is not defined") + return getattr(self.value, attr) + + def __repr__(self): + """Generate string with address and value.""" + return f"EvalLocalVar @{hex(id(self))} = {self.value if self.defined else 'undefined'}" + + +class EvalName: + """Identifier that hasn't yet been resolved.""" + + def __init__(self, name): + """Initialize identifier to name.""" + self.name = name + + def __getattr__(self, attr): + """Get attribute for EvalName.""" + raise NameError(f"name '{self.name}.{attr}' is not defined") + + +class EvalAttrSet: + """Class for object and attribute on lhs of assignment.""" + + def __init__(self, obj, attr): + """Initialize identifier to name.""" + self.obj = obj + self.attr = attr + + def setattr(self, value): + """Set the attribute value.""" + setattr(self.obj, self.attr, value) + + def getattr(self): + """Get the attribute value.""" + return getattr(self.obj, self.attr) + + +class EvalFunc: + """Class for a callable pyscript function.""" + + def __init__(self, func_def, code_list, code_str, global_ctx): + """Initialize a function calling context.""" + self.func_def = func_def + self.name = func_def.name + self.global_ctx = global_ctx + self.global_ctx_name = global_ctx.get_name() + self.logger = logging.getLogger(LOGGER_PATH + "." + self.global_ctx_name) + self.defaults = [] + self.kw_defaults = [] + self.decorators = [] + self.global_names = set() + self.nonlocal_names = set() + self.local_names = None + self.local_sym_table = {} + self.doc_string = ast.get_docstring(func_def) + self.num_posonly_arg = len(self.func_def.args.posonlyargs) + self.num_posn_arg = self.num_posonly_arg + len(self.func_def.args.args) - len(self.defaults) + self.code_list = code_list + self.code_str = code_str + self.exception = None + self.exception_obj = None + self.exception_long = None + self.trigger = [] + self.trigger_service = set() + self.has_closure = False + + def get_name(self): + """Return the function name.""" + return self.name + + def set_name(self, name): + """Set the function name.""" + self.name = name + + async def eval_defaults(self, ast_ctx): + """Evaluate the default function arguments.""" + self.defaults = [] + for val in self.func_def.args.defaults: + self.defaults.append(await ast_ctx.aeval(val)) + self.num_posn_arg = self.num_posonly_arg + len(self.func_def.args.args) - len(self.defaults) + self.kw_defaults = [] + for val in self.func_def.args.kw_defaults: + self.kw_defaults.append({"ok": bool(val), "val": None if not val else await ast_ctx.aeval(val)}) + + async def trigger_init(self, trig_ctx, func_name): + """Initialize decorator triggers for this function.""" + trig_args = {} + trig_decs = {} + trig_ctx_name = trig_ctx.get_name() + self.logger = logging.getLogger(LOGGER_PATH + "." + trig_ctx_name) + self.global_ctx.set_logger_name(trig_ctx_name) + self.global_ctx_name = trig_ctx_name + got_reqd_dec = False + exc_mesg = f"function '{func_name}' defined in {trig_ctx_name}" + trig_decorators_reqd = { + "event_trigger", + "mqtt_trigger", + "state_trigger", + "time_trigger", + "webhook_trigger", + } + arg_check = { + "event_trigger": {"arg_cnt": {1, 2, 3}, "rep_ok": True}, + "mqtt_trigger": {"arg_cnt": {1, 2, 3}, "rep_ok": True}, + "state_active": {"arg_cnt": {1}}, + "state_trigger": {"arg_cnt": {"*"}, "type": {list, set}, "rep_ok": True}, + "service": {"arg_cnt": {0, "*"}}, + "task_unique": {"arg_cnt": {1, 2}}, + "time_active": {"arg_cnt": {"*"}}, + "time_trigger": {"arg_cnt": {0, "*"}, "rep_ok": True}, + "webhook_trigger": {"arg_cnt": {1, 2}, "rep_ok": True}, + } + kwarg_check = { + "event_trigger": {"kwargs": {dict}}, + "mqtt_trigger": {"kwargs": {dict}}, + "time_trigger": {"kwargs": {dict}}, + "task_unique": {"kill_me": {bool, int}}, + "time_active": {"hold_off": {int, float}}, + "service": {"supports_response": {str}}, + "state_trigger": { + "kwargs": {dict}, + "state_hold": {int, float}, + "state_check_now": {bool, int}, + "state_hold_false": {int, float}, + "watch": {set, list}, + }, + "webhook_trigger": { + "kwargs": {dict}, + "local_only": {bool}, + "methods": {list, set}, + }, + } + + for dec in self.decorators: + dec_name, dec_args, dec_kwargs = dec[0], dec[1], dec[2] + if dec_name not in TRIG_SERV_DECORATORS: + raise SyntaxError(f"{exc_mesg}: unknown decorator @{dec_name}") + if dec_name in trig_decorators_reqd: + got_reqd_dec = True + arg_info = arg_check.get(dec_name, {}) + # + # check that we have the right number of arguments, and that they are + # strings + # + arg_cnt = arg_info["arg_cnt"] + if dec_args is None and 0 not in arg_cnt: + raise TypeError(f"{exc_mesg}: decorator @{dec_name} needs at least one argument") + if dec_args: + if "*" not in arg_cnt and len(dec_args) not in arg_cnt: + raise TypeError( + f"{exc_mesg}: decorator @{dec_name} got {len(dec_args)}" + f" argument{'s' if len(dec_args) > 1 else ''}, expected" + f" {' or '.join([str(cnt) for cnt in sorted(arg_cnt)])}" + ) + for arg_num, arg in enumerate(dec_args): + if isinstance(arg, str): + continue + mesg = "string" + if "type" in arg_info: + if type(arg) in arg_info["type"]: + for val in arg: + if not isinstance(val, str): + break + else: + continue + mesg += ", or " + ", or ".join( + sorted([ok_type.__name__ for ok_type in arg_info["type"]]) + ) + raise TypeError( + f"{exc_mesg}: decorator @{dec_name} argument {arg_num + 1} should be a {mesg}" + ) + if arg_cnt == {1}: + dec_args = dec_args[0] + + if dec_name not in kwarg_check and dec_kwargs is not None: + raise TypeError(f"{exc_mesg}: decorator @{dec_name} doesn't take keyword arguments") + if dec_kwargs is None: + dec_kwargs = {} + if dec_name in kwarg_check: + allowed = kwarg_check[dec_name] + for arg, value in dec_kwargs.items(): + if arg not in allowed: + raise TypeError( + f"{exc_mesg}: decorator @{dec_name} invalid keyword argument '{arg}'" + ) + if value is None or type(value) in allowed[arg]: + continue + ok_types = " or ".join(sorted([t.__name__ for t in allowed[arg]])) + raise TypeError( + f"{exc_mesg}: decorator @{dec_name} keyword '{arg}' should be type {ok_types}" + ) + if dec_name == "service": + desc = self.doc_string + if desc is None or desc == "": + desc = f"pyscript function {func_name}()" + desc = desc.lstrip(" \n\r") + if desc.startswith("yaml"): + try: + desc = desc[4:].lstrip(" \n\r") + file_desc = io.StringIO(desc) + service_desc = yaml.load(file_desc, Loader=yaml.BaseLoader) or OrderedDict() + file_desc.close() + except Exception as exc: + self.logger.error( + "Unable to decode yaml doc_string for %s(): %s", + func_name, + str(exc), + ) + raise exc + else: + fields = OrderedDict() + for arg in self.get_positional_args(): + fields[arg] = OrderedDict(description=f"argument {arg}") + service_desc = {"description": desc, "fields": fields} + + def pyscript_service_factory(func_name, func): + async def pyscript_service_handler(call): + """Handle python script service calls.""" + # self.logger.debug("service call to %s", func_name) + # + # use a new AstEval context so it can run fully independently + # of other instances (except for global_ctx which is common) + # + ast_ctx = AstEval(f"{trig_ctx_name}.{func_name}", self.global_ctx) + Function.install_ast_funcs(ast_ctx) + func_args = { + "trigger_type": "service", + "context": call.context, + } + func_args.update(call.data) + + async def do_service_call(func, ast_ctx, data): + retval = await func.call(ast_ctx, **data) + if ast_ctx.get_exception_obj(): + ast_ctx.get_logger().error(ast_ctx.get_exception_long()) + return retval + + task = Function.create_task(do_service_call(func, ast_ctx, func_args)) + await task + return task.result() + + return pyscript_service_handler + + for srv_name in dec_args if dec_args else [f"{DOMAIN}.{func_name}"]: + if type(srv_name) is not str or srv_name.count(".") != 1: + raise ValueError(f"{exc_mesg}: @service argument must be a string with one period") + domain, name = srv_name.split(".", 1) + if name in (SERVICE_RELOAD, SERVICE_JUPYTER_KERNEL_START): + raise SyntaxError(f"{exc_mesg}: @service conflicts with builtin service") + Function.service_register( + trig_ctx_name, + domain, + name, + pyscript_service_factory(func_name, self), + dec_kwargs.get("supports_response", SERVICE_RESPONSE_NONE), + ) + async_set_service_schema(Function.hass, domain, name, service_desc) + self.trigger_service.add(srv_name) + continue + + if dec_name == "webhook_trigger" and "methods" in dec_kwargs: + if len(bad := set(dec_kwargs["methods"]).difference(WEBHOOK_METHODS)) > 0: + raise TypeError(f"{exc_mesg}: {bad} aren't valid {dec_name} methods") + + if dec_name not in trig_decs: + trig_decs[dec_name] = [] + if len(trig_decs[dec_name]) > 0 and "rep_ok" not in arg_info: + raise SyntaxError(f"{exc_mesg}: decorator @{dec_name} can only be used once") + trig_decs[dec_name].append({"args": dec_args, "kwargs": dec_kwargs}) + + if not got_reqd_dec and len(trig_decs) > 0: + self.logger.error( + "%s defined in %s: needs at least one trigger decorator (ie: %s)", + func_name, + trig_ctx_name, + ", ".join(sorted(trig_decorators_reqd)), + ) + return + + if len(trig_decs) == 0: + if len(self.trigger_service) > 0: + trig_ctx.trigger_register(self) + return + + # + # start one or more triggers until they are all consumed + # each trigger task can handle at most one of each type of + # trigger; all get the same state_active, time_active and + # task_unique decorators + # + while True: + trig_args = { + "action": self, + "global_sym_table": self.global_ctx.global_sym_table, + } + got_trig = False + for trig in trig_decorators_reqd: + if trig not in trig_decs or len(trig_decs[trig]) == 0: + continue + trig_args[trig] = trig_decs[trig].pop(0) + got_trig = True + if not got_trig: + break + for dec_name in ["state_active", "time_active", "task_unique"]: + if dec_name in trig_decs: + trig_args[dec_name] = trig_decs[dec_name][0] + + self.trigger.append(trig_ctx.get_trig_info(f"{trig_ctx_name}.{func_name}", trig_args)) + + if trig_ctx.trigger_register(self): + self.trigger_start() + + def trigger_start(self): + """Start any triggers for this function.""" + for trigger in self.trigger: + trigger.start() + + def trigger_stop(self): + """Stop any triggers for this function.""" + for trigger in self.trigger: + trigger.stop() + self.trigger = [] + for srv_name in self.trigger_service: + domain, name = srv_name.split(".", 1) + Function.service_remove(self.global_ctx_name, domain, name) + self.trigger_service = set() + + async def eval_decorators(self, ast_ctx): + """Evaluate the function decorators arguments.""" + code_str, code_list = ast_ctx.code_str, ast_ctx.code_list + ast_ctx.code_str, ast_ctx.code_list = self.code_str, self.code_list + + dec_other = [] + dec_trig = [] + for dec in self.func_def.decorator_list: + if ( + isinstance(dec, ast.Call) + and isinstance(dec.func, ast.Name) + and dec.func.id in TRIG_SERV_DECORATORS + ): + args = await ast_ctx.eval_elt_list(dec.args) + kwargs = {keyw.arg: await ast_ctx.aeval(keyw.value) for keyw in dec.keywords} + dec_trig.append([dec.func.id, args, kwargs if len(kwargs) > 0 else None]) + elif isinstance(dec, ast.Name) and dec.id in TRIG_SERV_DECORATORS: + dec_trig.append([dec.id, None, None]) + else: + dec_other.append(await ast_ctx.aeval(dec)) + + ast_ctx.code_str, ast_ctx.code_list = code_str, code_list + return dec_trig, reversed(dec_other) + + async def resolve_nonlocals(self, ast_ctx): + """Tag local variables and resolve nonlocals.""" + + # + # determine the list of local variables, nonlocal and global + # arguments are local variables too + # + args = self.get_positional_args() + if self.func_def.args.vararg: + args.append(self.func_def.args.vararg.arg) + if self.func_def.args.kwarg: + args.append(self.func_def.args.kwarg.arg) + for kwonlyarg in self.func_def.args.kwonlyargs: + args.append(kwonlyarg.arg) + nonlocal_names = set() + global_names = set() + var_names = set(args) + self.local_names = set(args) + for stmt in self.func_def.body: + self.has_closure = self.has_closure or await self.check_for_closure(stmt) + var_names = var_names.union( + await ast_ctx.get_names( + stmt, + nonlocal_names=nonlocal_names, + global_names=global_names, + local_names=self.local_names, + ) + ) + for var_name in var_names: + got_dot = var_name.find(".") + if got_dot >= 0: + var_name = var_name[0:got_dot] + + if var_name in global_names: + continue + + if var_name in self.local_names and var_name not in nonlocal_names: + if self.has_closure: + self.local_sym_table[var_name] = EvalLocalVar(var_name) + continue + + if var_name in nonlocal_names: + sym_table_idx = 1 + else: + sym_table_idx = 0 + for sym_table in reversed(ast_ctx.sym_table_stack[sym_table_idx:] + [ast_ctx.sym_table]): + if var_name in sym_table and isinstance(sym_table[var_name], EvalLocalVar): + self.local_sym_table[var_name] = sym_table[var_name] + break + else: + if var_name in nonlocal_names: + val = await ast_ctx.ast_name(ast.Name(id=var_name, ctx=ast.Load())) + if isinstance(val, EvalName) and got_dot < 0: + raise SyntaxError(f"no binding for nonlocal '{var_name}' found") + + def get_decorators(self): + """Return the function decorators.""" + return self.decorators + + def get_doc_string(self): + """Return the function doc_string.""" + return self.doc_string + + def get_positional_args(self): + """Return the function positional arguments.""" + args = [] + for arg in self.func_def.args.posonlyargs + self.func_def.args.args: + args.append(arg.arg) + return args + + async def try_aeval(self, ast_ctx, arg): + """Call self.aeval and capture exceptions.""" + try: + return await ast_ctx.aeval(arg) + except asyncio.CancelledError: + raise + except Exception as err: + if ast_ctx.exception_long is None: + ast_ctx.exception_long = ast_ctx.format_exc(err, arg.lineno, arg.col_offset) + + async def call(self, ast_ctx, *args, **kwargs): + """Call the function with the given context and arguments.""" + sym_table = {} + if args is None: + args = [] + kwargs = kwargs.copy() if kwargs else {} + bad_kwargs = [] + for i, func_def_arg in enumerate(self.func_def.args.posonlyargs + self.func_def.args.args): + var_name = func_def_arg.arg + val = None + if i < len(args): + val = args[i] + if var_name in kwargs: + raise TypeError(f"{self.name}() got multiple values for argument '{var_name}'") + elif var_name in kwargs: + if i < self.num_posonly_arg: + bad_kwargs.append(var_name) + val = kwargs[var_name] + del kwargs[var_name] + elif self.num_posn_arg <= i < len(self.defaults) + self.num_posn_arg: + val = self.defaults[i - self.num_posn_arg] + else: + raise TypeError( + f"{self.name}() missing {self.num_posn_arg - i} required positional arguments" + ) + sym_table[var_name] = val + if len(bad_kwargs) > 0: + raise TypeError( + f"{self.name}() got some positional-only arguments passed as keyword arguments: '{', '.join(bad_kwargs)}'" + ) + + for i, kwonlyarg in enumerate(self.func_def.args.kwonlyargs): + var_name = kwonlyarg.arg + if var_name in kwargs: + val = kwargs[var_name] + del kwargs[var_name] + elif i < len(self.kw_defaults) and self.kw_defaults[i]["ok"]: + val = self.kw_defaults[i]["val"] + else: + raise TypeError(f"{self.name}() missing required keyword-only arguments") + sym_table[var_name] = val + if self.func_def.args.kwarg: + sym_table[self.func_def.args.kwarg.arg] = kwargs + elif not set(kwargs.keys()).issubset(TRIGGER_KWARGS): + # don't raise an exception for extra trigger keyword parameters; + # it's difficult to apply this exception to just trigger functions + # since they could have non-trigger decorators too + unexpected = ", ".join(sorted(set(kwargs.keys()) - TRIGGER_KWARGS)) + raise TypeError(f"{self.name}() called with unexpected keyword arguments: {unexpected}") + num_posn = self.num_posonly_arg + len(self.func_def.args.args) + if self.func_def.args.vararg: + if len(args) > num_posn: + sym_table[self.func_def.args.vararg.arg] = tuple(args[num_posn:]) + else: + sym_table[self.func_def.args.vararg.arg] = () + elif len(args) > num_posn: + raise TypeError(f"{self.name}() called with too many positional arguments") + for name, value in self.local_sym_table.items(): + if name in sym_table: + sym_table[name] = EvalLocalVar(name, value=sym_table[name]) + elif value.is_defined(): + sym_table[name] = value + else: + sym_table[name] = EvalLocalVar(name) + if ast_ctx.global_ctx != self.global_ctx: + # + # switch to the global symbol table in the global context + # where the function was defined + # + prev_sym_table = [ + ast_ctx.global_sym_table, + ast_ctx.sym_table, + ast_ctx.sym_table_stack, + ast_ctx.global_ctx, + ] + ast_ctx.global_sym_table = self.global_ctx.get_global_sym_table() + ast_ctx.sym_table_stack = [ast_ctx.global_sym_table] + ast_ctx.global_ctx = self.global_ctx + else: + ast_ctx.sym_table_stack.append(ast_ctx.sym_table) + prev_sym_table = None + ast_ctx.sym_table = sym_table + code_str, code_list = ast_ctx.code_str, ast_ctx.code_list + ast_ctx.code_str, ast_ctx.code_list = self.code_str, self.code_list + self.exception = None + self.exception_obj = None + self.exception_long = None + prev_func = ast_ctx.curr_func + save_user_locals = ast_ctx.user_locals + ast_ctx.user_locals = {} + ast_ctx.curr_func = self + del args, kwargs + for arg1 in self.func_def.body: + val = await self.try_aeval(ast_ctx, arg1) + if isinstance(val, EvalReturn): + val = val.value + break + # return None at end if there isn't a return + val = None + if ast_ctx.get_exception_obj(): + break + ast_ctx.curr_func = prev_func + ast_ctx.user_locals = save_user_locals + ast_ctx.code_str, ast_ctx.code_list = code_str, code_list + if prev_sym_table is not None: + ( + ast_ctx.global_sym_table, + ast_ctx.sym_table, + ast_ctx.sym_table_stack, + ast_ctx.global_ctx, + ) = prev_sym_table + else: + ast_ctx.sym_table = ast_ctx.sym_table_stack.pop() + return val + + async def check_for_closure(self, arg): + """Recursively check ast tree arg and return True if there is an inner function or class.""" + if isinstance(arg, (ast.FunctionDef, ast.ClassDef, ast.AsyncFunctionDef)): + return True + for child in ast.iter_child_nodes(arg): + if await self.check_for_closure(child): + return True + return False + + +class EvalFuncVar: + """Class for a callable pyscript function.""" + + def __init__(self, func): + """Initialize instance with given EvalFunc function.""" + self.func = func + self.ast_ctx = None + + def get_func(self): + """Return the EvalFunc function.""" + return self.func + + def remove_func(self): + """Remove and return the EvalFunc function.""" + func = self.func + self.func = None + return func + + async def call(self, ast_ctx, *args, **kwargs): + """Call the EvalFunc function.""" + return await self.func.call(ast_ctx, *args, **kwargs) + + def get_name(self): + """Return the function name.""" + return self.func.get_name() + + def set_name(self, name): + """Set the function name.""" + self.func.set_name(name) + + def set_ast_ctx(self, ast_ctx): + """Set the ast context.""" + self.ast_ctx = ast_ctx + + def get_ast_ctx(self): + """Return the ast context.""" + return self.ast_ctx + + def __del__(self): + """On deletion, stop any triggers for this function.""" + if self.func: + self.func.trigger_stop() + + async def __call__(self, *args, **kwargs): + """Call the EvalFunc function using our saved ast ctx.""" + return await self.func.call(self.ast_ctx, *args, **kwargs) + + +class EvalFuncVarClassInst(EvalFuncVar): + """Class for a callable pyscript class instance function.""" + + def __init__(self, func, ast_ctx, class_inst_weak): + """Initialize instance with given EvalFunc function.""" + super().__init__(func) + self.ast_ctx = ast_ctx + self.class_inst_weak = class_inst_weak + + async def call(self, ast_ctx, *args, **kwargs): + """Call the EvalFunc function.""" + return await self.func.call(ast_ctx, self.class_inst_weak(), *args, **kwargs) + + async def __call__(self, *args, **kwargs): + """Call the function using our saved ast ctx and class instance.""" + return await self.func.call(self.ast_ctx, self.class_inst_weak(), *args, **kwargs) + + +class AstEval: + """Python interpreter AST object evaluator.""" + + def __init__(self, name, global_ctx, logger_name=None): + """Initialize an interpreter execution context.""" + self.name = name + self.str = None + self.ast = None + self.global_ctx = global_ctx + self.global_sym_table = global_ctx.get_global_sym_table() if global_ctx else {} + self.sym_table_stack = [] + self.sym_table = self.global_sym_table + self.local_sym_table = {} + self.user_locals = {} + self.curr_func = None + self.filename = name + self.code_str = None + self.code_list = None + self.exception = None + self.exception_obj = None + self.exception_long = None + self.exception_curr = None + self.lineno = 1 + self.col_offset = 0 + self.logger_handlers = set() + self.logger = None + self.set_logger_name(logger_name if logger_name is not None else self.name) + self.config_entry = Function.hass.data.get(DOMAIN, {}).get(CONFIG_ENTRY, {}) + self.dec_eval_depth = 0 + + async def ast_not_implemented(self, arg, *args): + """Raise NotImplementedError exception for unimplemented AST types.""" + name = "ast_" + arg.__class__.__name__.lower() + raise NotImplementedError(f"{self.name}: not implemented ast " + name) + + async def aeval(self, arg, undefined_check=True): + """Vector to specific function based on ast class type.""" + name = "ast_" + arg.__class__.__name__.lower() + try: + if hasattr(arg, "lineno"): + self.lineno = arg.lineno + self.col_offset = arg.col_offset + val = await getattr(self, name, self.ast_not_implemented)(arg) + if undefined_check and isinstance(val, EvalName): + raise NameError(f"name '{val.name}' is not defined") + return val + except Exception as err: + if not self.exception_obj: + func_name = self.curr_func.get_name() + "(), " if self.curr_func else "" + self.exception_obj = err + self.exception = f"Exception in {func_name}{self.filename} line {self.lineno} column {self.col_offset}: {err}" + self.exception_long = self.format_exc(err, self.lineno, self.col_offset) + raise + + # Statements return NONE, EvalBreak, EvalContinue, EvalReturn + async def ast_module(self, arg): + """Execute ast_module - a list of statements.""" + val = None + for arg1 in arg.body: + val = await self.aeval(arg1) + if isinstance(val, EvalReturn): + raise SyntaxError(f"{val.name()} statement outside function") + if isinstance(val, EvalStopFlow): + raise SyntaxError(f"{val.name()} statement outside loop") + return val + + async def ast_import(self, arg): + """Execute import.""" + for imp in arg.names: + mod, error_ctx = await self.global_ctx.module_import(imp.name, 0) + if error_ctx: + self.exception_obj = error_ctx.exception_obj + self.exception = error_ctx.exception + self.exception_long = error_ctx.exception_long + raise self.exception_obj + if not mod: + if ( + not self.config_entry.data.get(CONF_ALLOW_ALL_IMPORTS, False) + and imp.name not in ALLOWED_IMPORTS + ): + raise ModuleNotFoundError(f"import of {imp.name} not allowed") + if imp.name not in sys.modules: + mod = await Function.hass.async_add_executor_job(importlib.import_module, imp.name) + else: + mod = sys.modules[imp.name] + self.sym_table[imp.name if imp.asname is None else imp.asname] = mod + + async def ast_importfrom(self, arg): + """Execute from X import Y.""" + if arg.module is None: + # handle: "from . import xyz" + for imp in arg.names: + mod, error_ctx = await self.global_ctx.module_import(imp.name, arg.level) + if error_ctx: + self.exception_obj = error_ctx.exception_obj + self.exception = error_ctx.exception + self.exception_long = error_ctx.exception_long + raise self.exception_obj + if not mod: + raise ModuleNotFoundError(f"module '{imp.name}' not found") + self.sym_table[imp.name if imp.asname is None else imp.asname] = mod + return + mod, error_ctx = await self.global_ctx.module_import(arg.module, arg.level) + if error_ctx: + self.exception_obj = error_ctx.exception_obj + self.exception = error_ctx.exception + self.exception_long = error_ctx.exception_long + raise self.exception_obj + if not mod: + if ( + not self.config_entry.data.get(CONF_ALLOW_ALL_IMPORTS, False) + and arg.module not in ALLOWED_IMPORTS + ): + raise ModuleNotFoundError(f"import from {arg.module} not allowed") + if arg.module not in sys.modules: + mod = await Function.hass.async_add_executor_job(importlib.import_module, arg.module) + else: + mod = sys.modules[arg.module] + for imp in arg.names: + if imp.name == "*": + for name, value in mod.__dict__.items(): + if name[0] != "_": + self.sym_table[name] = value + else: + self.sym_table[imp.name if imp.asname is None else imp.asname] = getattr(mod, imp.name) + + async def ast_if(self, arg): + """Execute if statement.""" + val = None + if await self.aeval(arg.test): + for arg1 in arg.body: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + return val + else: + for arg1 in arg.orelse: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + return val + return val + + async def ast_for(self, arg): + """Execute for statement.""" + for loop_var in await self.aeval(arg.iter): + await self.recurse_assign(arg.target, loop_var) + for arg1 in arg.body: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + break + if isinstance(val, EvalBreak): + break + if isinstance(val, EvalReturn): + return val + else: + for arg1 in arg.orelse: + val = await self.aeval(arg1) + if isinstance(val, EvalReturn): + return val + return None + + async def ast_asyncfor(self, arg): + """Execute async for statement.""" + return await self.ast_for(arg) + + async def ast_while(self, arg): + """Execute while statement.""" + while await self.aeval(arg.test): + for arg1 in arg.body: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + break + if isinstance(val, EvalBreak): + break + if isinstance(val, EvalReturn): + return val + else: + for arg1 in arg.orelse: + val = await self.aeval(arg1) + if isinstance(val, EvalReturn): + return val + return None + + async def ast_classdef(self, arg): + """Evaluate class definition.""" + bases = [(await self.aeval(base)) for base in arg.bases] + if self.curr_func and arg.name in self.curr_func.global_names: + sym_table_assign = self.global_sym_table + else: + sym_table_assign = self.sym_table + sym_table_assign[arg.name] = EvalLocalVar(arg.name) + sym_table = {} + self.sym_table_stack.append(self.sym_table) + self.sym_table = sym_table + for arg1 in arg.body: + val = await self.aeval(arg1) + if isinstance(val, EvalReturn): + raise SyntaxError(f"{val.name()} statement outside function") + if isinstance(val, EvalStopFlow): + raise SyntaxError(f"{val.name()} statement outside loop") + self.sym_table = self.sym_table_stack.pop() + + sym_table["__init__evalfunc_wrap__"] = None + if "__init__" in sym_table: + sym_table["__init__evalfunc_wrap__"] = sym_table["__init__"] + del sym_table["__init__"] + sym_table_assign[arg.name].set(type(arg.name, tuple(bases), sym_table)) + + async def ast_functiondef(self, arg): + """Evaluate function definition.""" + other_dec = [] + dec_name = None + pyscript_compile = None + for dec in arg.decorator_list: + if isinstance(dec, ast.Name) and dec.id in COMP_DECORATORS: + dec_name = dec.id + elif ( + isinstance(dec, ast.Call) + and isinstance(dec.func, ast.Name) + and dec.func.id in COMP_DECORATORS + ): + dec_name = dec.func.id + else: + other_dec.append(dec) + continue + if pyscript_compile: + raise SyntaxError( + f"can only specify single decorator of {', '.join(sorted(COMP_DECORATORS))}" + ) + pyscript_compile = dec + + if pyscript_compile: + if isinstance(pyscript_compile, ast.Call): + if len(pyscript_compile.args) > 0: + raise TypeError(f"@{dec_name}() takes 0 positional arguments") + if len(pyscript_compile.keywords) > 0: + raise TypeError(f"@{dec_name}() takes no keyword arguments") + arg.decorator_list = other_dec + local_var = None + if arg.name in self.sym_table and isinstance(self.sym_table[arg.name], EvalLocalVar): + local_var = self.sym_table[arg.name] + code = compile(ast.Module(body=[arg], type_ignores=[]), filename=self.filename, mode="exec") + exec(code, self.global_sym_table, self.sym_table) # pylint: disable=exec-used + + func = self.sym_table[arg.name] + if dec_name == "pyscript_executor": + if not asyncio.iscoroutinefunction(func): + + def executor_wrap_factory(func): + async def executor_wrap(*args, **kwargs): + return await Function.hass.async_add_executor_job( + functools.partial(func, **kwargs), *args + ) + + return executor_wrap + + self.sym_table[arg.name] = executor_wrap_factory(func) + else: + raise TypeError("@pyscript_executor() needs a regular, not async, function") + if local_var: + self.sym_table[arg.name] = local_var + self.sym_table[arg.name].set(func) + return + + func = EvalFunc(arg, self.code_list, self.code_str, self.global_ctx) + await func.eval_defaults(self) + await func.resolve_nonlocals(self) + name = func.get_name() + dec_trig, dec_other = await func.eval_decorators(self) + self.dec_eval_depth += 1 + for dec_func in dec_other: + func = await self.call_func(dec_func, None, func) + if isinstance(func, EvalFuncVar): + # set the function name back to its original instead of the decorator function we just called + func.set_name(name) + func = func.remove_func() + dec_trig += func.decorators + elif isinstance(func, EvalFunc): + func.set_name(name) + self.dec_eval_depth -= 1 + if isinstance(func, EvalFunc): + func.decorators = dec_trig + if self.dec_eval_depth == 0: + func.trigger_stop() + await func.trigger_init(self.global_ctx, name) + func_var = EvalFuncVar(func) + func_var.set_ast_ctx(self) + else: + func_var = EvalFuncVar(func) + func_var.set_ast_ctx(self) + else: + func_var = func + + if self.curr_func and name in self.curr_func.global_names: + sym_table = self.global_sym_table + else: + sym_table = self.sym_table + if name in sym_table and isinstance(sym_table[name], EvalLocalVar): + sym_table[name].set(func_var) + else: + sym_table[name] = func_var + + async def ast_lambda(self, arg): + """Evaluate lambda definition by compiling a regular function.""" + name = "__lambda_defn_temp__" + await self.aeval( + ast.FunctionDef( + args=arg.args, + body=[ast.Return(value=arg.body, lineno=arg.body.lineno, col_offset=arg.body.col_offset)], + name=name, + decorator_list=[ast.Name(id="pyscript_compile", ctx=ast.Load())], + lineno=arg.col_offset, + col_offset=arg.col_offset, + ) + ) + func = self.sym_table[name] + del self.sym_table[name] + return func + + async def ast_asyncfunctiondef(self, arg): + """Evaluate async function definition.""" + return await self.ast_functiondef(arg) + + async def ast_try(self, arg): + """Execute try...except statement.""" + try: + for arg1 in arg.body: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + return val + if self.exception_obj is not None: + raise self.exception_obj + except Exception as err: + curr_exc = self.exception_curr + self.exception_curr = err + for handler in arg.handlers: + match = False + if handler.type: + exc_list = await self.aeval(handler.type) + if not isinstance(exc_list, tuple): + exc_list = [exc_list] + for exc in exc_list: + if isinstance(err, exc): + match = True + break + else: + match = True + if match: + save_obj = self.exception_obj + save_exc_long = self.exception_long + save_exc = self.exception + self.exception_obj = None + self.exception = None + self.exception_long = None + if handler.name is not None: + if handler.name in self.sym_table and isinstance( + self.sym_table[handler.name], EvalLocalVar + ): + self.sym_table[handler.name].set(err) + else: + self.sym_table[handler.name] = err + for arg1 in handler.body: + try: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + if handler.name is not None: + del self.sym_table[handler.name] + self.exception_curr = curr_exc + return val + except Exception: + if self.exception_obj is not None: + if handler.name is not None: + del self.sym_table[handler.name] + self.exception_curr = curr_exc + if self.exception_obj == save_obj: + self.exception_long = save_exc_long + self.exception = save_exc + else: + self.exception_long = ( + save_exc_long + + "\n\nDuring handling of the above exception, another exception occurred:\n\n" + + self.exception_long + ) + raise self.exception_obj # pylint: disable=raise-missing-from + if handler.name is not None: + del self.sym_table[handler.name] + break + else: + self.exception_curr = curr_exc + raise err + else: + for arg1 in arg.orelse: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + return val + finally: + for arg1 in arg.finalbody: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + return val # pylint: disable=lost-exception,return-in-finally + return None + + async def ast_raise(self, arg): + """Execute raise statement.""" + if not arg.exc: + if not self.exception_curr: + raise RuntimeError("No active exception to reraise") + exc = self.exception_curr + else: + exc = await self.aeval(arg.exc) + if self.exception_curr: + exc.__cause__ = self.exception_curr + if arg.cause: + cause = await self.aeval(arg.cause) + raise exc from cause + raise exc + + async def ast_with(self, arg, async_attr=""): + """Execute with statement.""" + hit_except = False + ctx_list = [] + val = None + enter_attr = f"__{async_attr}enter__" + exit_attr = f"__{async_attr}exit__" + try: + for item in arg.items: + manager = await self.aeval(item.context_expr) + ctx_list.append( + { + "manager": manager, + "enter": getattr(type(manager), enter_attr), + "exit": getattr(type(manager), exit_attr), + "target": item.optional_vars, + } + ) + for ctx in ctx_list: + value = await self.call_func(ctx["enter"], enter_attr, ctx["manager"]) + if ctx["target"]: + await self.recurse_assign(ctx["target"], value) + for arg1 in arg.body: + val = await self.aeval(arg1) + if isinstance(val, EvalStopFlow): + break + except Exception: + hit_except = True + exit_ok = True + for ctx in reversed(ctx_list): + ret = await self.call_func(ctx["exit"], exit_attr, ctx["manager"], *sys.exc_info()) + exit_ok = exit_ok and ret + if not exit_ok: + raise + finally: + if not hit_except: + for ctx in reversed(ctx_list): + await self.call_func(ctx["exit"], exit_attr, ctx["manager"], None, None, None) + return val + + async def ast_asyncwith(self, arg): + """Execute async with statement.""" + return await self.ast_with(arg, async_attr="a") + + async def ast_pass(self, arg): + """Execute pass statement.""" + + async def ast_expression(self, arg): + """Execute expression statement.""" + return await self.aeval(arg.body) + + async def ast_expr(self, arg): + """Execute expression statement.""" + return await self.aeval(arg.value) + + async def ast_break(self, arg): + """Execute break statement - return special class.""" + return EvalBreak() + + async def ast_continue(self, arg): + """Execute continue statement - return special class.""" + return EvalContinue() + + async def ast_return(self, arg): + """Execute return statement - return special class.""" + return EvalReturn(await self.aeval(arg.value) if arg.value else None) + + async def ast_global(self, arg): + """Execute global statement.""" + if not self.curr_func: + raise SyntaxError("global statement outside function") + for var_name in arg.names: + self.curr_func.global_names.add(var_name) + + async def ast_nonlocal(self, arg): + """Execute nonlocal statement.""" + if not self.curr_func: + raise SyntaxError("nonlocal statement outside function") + for var_name in arg.names: + self.curr_func.nonlocal_names.add(var_name) + + async def recurse_assign(self, lhs, val): + """Recursive assignment.""" + if isinstance(lhs, ast.Tuple): + try: + vals = [*(iter(val))] + except Exception: + raise TypeError("cannot unpack non-iterable object") # pylint: disable=raise-missing-from + got_star = 0 + for lhs_elt in lhs.elts: + if isinstance(lhs_elt, ast.Starred): + got_star = 1 + break + if len(lhs.elts) > len(vals) + got_star: + if got_star: + err_msg = f"at least {len(lhs.elts) - got_star}" + else: + err_msg = f"{len(lhs.elts)}" + raise ValueError(f"too few values to unpack (expected {err_msg})") + if len(lhs.elts) < len(vals) and got_star == 0: + raise ValueError(f"too many values to unpack (expected {len(lhs.elts)})") + val_idx = 0 + for lhs_elt in lhs.elts: + if isinstance(lhs_elt, ast.Starred): + star_len = len(vals) - len(lhs.elts) + 1 + star_name = lhs_elt.value.id + await self.recurse_assign( + ast.Name(id=star_name, ctx=ast.Store()), + vals[val_idx : val_idx + star_len], + ) + val_idx += star_len + else: + await self.recurse_assign(lhs_elt, vals[val_idx]) + val_idx += 1 + elif isinstance(lhs, ast.Subscript): + var = await self.aeval(lhs.value) + if isinstance(lhs.slice, ast.Index): + ind = await self.aeval(lhs.slice.value) + var[ind] = val + elif isinstance(lhs.slice, ast.Slice): + lower = await self.aeval(lhs.slice.lower) if lhs.slice.lower else None + upper = await self.aeval(lhs.slice.upper) if lhs.slice.upper else None + step = await self.aeval(lhs.slice.step) if lhs.slice.step else None + var[slice(lower, upper, step)] = val + else: + var[await self.aeval(lhs.slice)] = val + else: + var_name = await self.aeval(lhs) + if isinstance(var_name, EvalAttrSet): + var_name.setattr(val) + return + if not isinstance(var_name, str): + raise NotImplementedError(f"unknown lhs type {lhs} (got {var_name}) in assign") + dot_count = var_name.count(".") + if dot_count == 1: + State.set(var_name, val) + return + if dot_count == 2: + State.setattr(var_name, val) + return + if dot_count > 0: + raise NameError( + f"invalid name '{var_name}' (should be 'domain.entity' or 'domain.entity.attr')" + ) + if self.curr_func and var_name in self.curr_func.global_names: + self.global_sym_table[var_name] = val + return + if var_name in self.sym_table and isinstance(self.sym_table[var_name], EvalLocalVar): + self.sym_table[var_name].set(val) + else: + self.sym_table[var_name] = val + + async def ast_assign(self, arg): + """Execute assignment statement.""" + rhs = await self.aeval(arg.value) + for target in arg.targets: + await self.recurse_assign(target, rhs) + + async def ast_augassign(self, arg): + """Execute augmented assignment statement (lhs = value).""" + arg.target.ctx = ast.Load() + new_val = await self.aeval(ast.BinOp(left=arg.target, op=arg.op, right=arg.value)) + arg.target.ctx = ast.Store() + await self.recurse_assign(arg.target, new_val) + + async def ast_annassign(self, arg): + """Execute type hint assignment statement (just ignore the type hint).""" + if arg.value is not None: + rhs = await self.aeval(arg.value) + await self.recurse_assign(arg.target, rhs) + + async def ast_namedexpr(self, arg): + """Execute named expression.""" + val = await self.aeval(arg.value) + await self.recurse_assign(arg.target, val) + return val + + async def ast_delete(self, arg): + """Execute del statement.""" + for arg1 in arg.targets: + if isinstance(arg1, ast.Subscript): + var = await self.aeval(arg1.value) + if isinstance(arg1.slice, ast.Index): + ind = await self.aeval(arg1.slice.value) + for elt in ind if isinstance(ind, list) else [ind]: + del var[elt] + elif isinstance(arg1.slice, ast.Slice): + lower, upper, step = None, None, None + if arg1.slice.lower: + lower = await self.aeval(arg1.slice.lower) + if arg1.slice.upper: + upper = await self.aeval(arg1.slice.upper) + if arg1.slice.step: + step = await self.aeval(arg1.slice.step) + del var[slice(lower, upper, step)] + else: + del var[await self.aeval(arg1.slice)] + elif isinstance(arg1, ast.Name): + if self.curr_func and arg1.id in self.curr_func.global_names: + if arg1.id in self.global_sym_table: + del self.global_sym_table[arg1.id] + elif arg1.id in self.sym_table: + if isinstance(self.sym_table[arg1.id], EvalLocalVar): + if self.sym_table[arg1.id].is_defined(): + self.sym_table[arg1.id].set_undefined() + else: + raise NameError(f"name '{arg1.id}' is not defined") + else: + del self.sym_table[arg1.id] + else: + raise NameError(f"name '{arg1.id}' is not defined") + elif isinstance(arg1, ast.Attribute): + var_name = await self.ast_attribute_collapse(arg1, check_undef=False) + if not isinstance(var_name, str): + raise NameError("state name should be 'domain.entity' or 'domain.entity.attr'") + State.delete(var_name) + else: + raise NotImplementedError(f"unknown target type {arg1} in del") + + async def ast_assert(self, arg): + """Execute assert statement.""" + if not await self.aeval(arg.test): + if arg.msg: + raise AssertionError(await self.aeval(arg.msg)) + raise AssertionError + + async def ast_attribute_collapse(self, arg, check_undef=True): + """Combine dotted attributes to allow variable names to have dots.""" + # collapse dotted names, eg: + # Attribute(value=Attribute(value=Name(id='i', ctx=Load()), attr='j', ctx=Load()), attr='k', ctx=Store()) + name = arg.attr + val = arg.value + while isinstance(val, ast.Attribute): + name = val.attr + "." + name + val = val.value + if isinstance(val, ast.Name): + name = val.id + "." + name + # ensure the first portion of name is undefined + if check_undef and not isinstance( + await self.ast_name(ast.Name(id=val.id, ctx=ast.Load())), EvalName + ): + return None + return name + return None + + async def ast_attribute(self, arg): + """Apply attributes.""" + full_name = await self.ast_attribute_collapse(arg) + if full_name is not None: + if isinstance(arg.ctx, ast.Store): + return full_name + val = await self.ast_name(ast.Name(id=full_name, ctx=arg.ctx)) + if not isinstance(val, EvalName): + return val + val = await self.aeval(arg.value) + if isinstance(arg.ctx, ast.Store): + return EvalAttrSet(val, arg.attr) + return getattr(val, arg.attr) + + async def ast_name(self, arg): + """Look up value of identifier on load, or returns name on set.""" + if isinstance(arg.ctx, ast.Load): + # + # check other scopes if required by global declarations + # + if self.curr_func and arg.id in self.curr_func.global_names: + if arg.id in self.global_sym_table: + return self.global_sym_table[arg.id] + raise NameError(f"global name '{arg.id}' is not defined") + # + # now check in our current symbol table, and then some other places + # + if arg.id in self.sym_table: + if isinstance(self.sym_table[arg.id], EvalLocalVar): + return self.sym_table[arg.id].get() + return self.sym_table[arg.id] + if arg.id in self.local_sym_table: + return self.local_sym_table[arg.id] + if arg.id in self.global_sym_table: + if self.curr_func and arg.id in self.curr_func.local_names: + raise UnboundLocalError(f"local variable '{arg.id}' referenced before assignment") + return self.global_sym_table[arg.id] + if arg.id in BUILTIN_AST_FUNCS_FACTORY: + return BUILTIN_AST_FUNCS_FACTORY[arg.id](self) + if hasattr(builtins, arg.id) and arg.id not in BUILTIN_EXCLUDE and arg.id[0] != "_": + return getattr(builtins, arg.id) + if Function.get(arg.id): + return Function.get(arg.id) + num_dots = arg.id.count(".") + # + # any single-dot name could be a state variable + # a two-dot name for state.attr needs to exist + # + if num_dots == 1 or (num_dots == 2 and State.exist(arg.id)): + return State.get(arg.id) + # + # Couldn't find it, so return just the name wrapped in EvalName to + # distinguish from a string variable value. This is to support + # names with ".", which are joined by ast_attribute + # + return EvalName(arg.id) + return arg.id + + async def ast_binop(self, arg): + """Evaluate binary operators by calling function based on class.""" + name = "ast_binop_" + arg.op.__class__.__name__.lower() + return await getattr(self, name, self.ast_not_implemented)(arg.left, arg.right) + + async def ast_binop_add(self, arg0, arg1): + """Evaluate binary operator: +.""" + return (await self.aeval(arg0)) + (await self.aeval(arg1)) + + async def ast_binop_sub(self, arg0, arg1): + """Evaluate binary operator: -.""" + return (await self.aeval(arg0)) - (await self.aeval(arg1)) + + async def ast_binop_mult(self, arg0, arg1): + """Evaluate binary operator: *.""" + return (await self.aeval(arg0)) * (await self.aeval(arg1)) + + async def ast_binop_div(self, arg0, arg1): + """Evaluate binary operator: /.""" + return (await self.aeval(arg0)) / (await self.aeval(arg1)) + + async def ast_binop_mod(self, arg0, arg1): + """Evaluate binary operator: %.""" + return (await self.aeval(arg0)) % (await self.aeval(arg1)) + + async def ast_binop_pow(self, arg0, arg1): + """Evaluate binary operator: **.""" + return (await self.aeval(arg0)) ** (await self.aeval(arg1)) + + async def ast_binop_lshift(self, arg0, arg1): + """Evaluate binary operator: <<.""" + return (await self.aeval(arg0)) << (await self.aeval(arg1)) + + async def ast_binop_rshift(self, arg0, arg1): + """Evaluate binary operator: >>.""" + return (await self.aeval(arg0)) >> (await self.aeval(arg1)) + + async def ast_binop_bitor(self, arg0, arg1): + """Evaluate binary operator: |.""" + return (await self.aeval(arg0)) | (await self.aeval(arg1)) + + async def ast_binop_bitxor(self, arg0, arg1): + """Evaluate binary operator: ^.""" + return (await self.aeval(arg0)) ^ (await self.aeval(arg1)) + + async def ast_binop_bitand(self, arg0, arg1): + """Evaluate binary operator: &.""" + return (await self.aeval(arg0)) & (await self.aeval(arg1)) + + async def ast_binop_floordiv(self, arg0, arg1): + """Evaluate binary operator: //.""" + return (await self.aeval(arg0)) // (await self.aeval(arg1)) + + async def ast_unaryop(self, arg): + """Evaluate unary operators by calling function based on class.""" + name = "ast_unaryop_" + arg.op.__class__.__name__.lower() + return await getattr(self, name, self.ast_not_implemented)(arg.operand) + + async def ast_unaryop_not(self, arg0): + """Evaluate unary operator: not.""" + return not (await self.aeval(arg0)) + + async def ast_unaryop_invert(self, arg0): + """Evaluate unary operator: ~.""" + return ~(await self.aeval(arg0)) + + async def ast_unaryop_uadd(self, arg0): + """Evaluate unary operator: +.""" + return await self.aeval(arg0) + + async def ast_unaryop_usub(self, arg0): + """Evaluate unary operator: -.""" + return -(await self.aeval(arg0)) + + async def ast_compare(self, arg): + """Evaluate comparison operators by calling function based on class.""" + left = arg.left + for cmp_op, right in zip(arg.ops, arg.comparators): + name = "ast_cmpop_" + cmp_op.__class__.__name__.lower() + val = await getattr(self, name, self.ast_not_implemented)(left, right) + if not val: + return False + left = right + return True + + async def ast_cmpop_eq(self, arg0, arg1): + """Evaluate comparison operator: ==.""" + return (await self.aeval(arg0)) == (await self.aeval(arg1)) + + async def ast_cmpop_noteq(self, arg0, arg1): + """Evaluate comparison operator: !=.""" + return (await self.aeval(arg0)) != (await self.aeval(arg1)) + + async def ast_cmpop_lt(self, arg0, arg1): + """Evaluate comparison operator: <.""" + return (await self.aeval(arg0)) < (await self.aeval(arg1)) + + async def ast_cmpop_lte(self, arg0, arg1): + """Evaluate comparison operator: <=.""" + return (await self.aeval(arg0)) <= (await self.aeval(arg1)) + + async def ast_cmpop_gt(self, arg0, arg1): + """Evaluate comparison operator: >.""" + return (await self.aeval(arg0)) > (await self.aeval(arg1)) + + async def ast_cmpop_gte(self, arg0, arg1): + """Evaluate comparison operator: >=.""" + return (await self.aeval(arg0)) >= (await self.aeval(arg1)) + + async def ast_cmpop_is(self, arg0, arg1): + """Evaluate comparison operator: is.""" + return (await self.aeval(arg0)) is (await self.aeval(arg1)) + + async def ast_cmpop_isnot(self, arg0, arg1): + """Evaluate comparison operator: is not.""" + return (await self.aeval(arg0)) is not (await self.aeval(arg1)) + + async def ast_cmpop_in(self, arg0, arg1): + """Evaluate comparison operator: in.""" + return (await self.aeval(arg0)) in (await self.aeval(arg1)) + + async def ast_cmpop_notin(self, arg0, arg1): + """Evaluate comparison operator: not in.""" + return (await self.aeval(arg0)) not in (await self.aeval(arg1)) + + async def ast_boolop(self, arg): + """Evaluate boolean operators and and or.""" + if isinstance(arg.op, ast.And): + val = True + for arg1 in arg.values: + val = await self.aeval(arg1) + if not val: + return val + return val + val = False + for arg1 in arg.values: + val = await self.aeval(arg1) + if val: + return val + return val + + async def eval_elt_list(self, elts): + """Evaluate and star list elements.""" + val = [] + for arg in elts: + if isinstance(arg, ast.Starred): + val += await self.aeval(arg.value) + else: + val.append(await self.aeval(arg)) + return val + + async def ast_list(self, arg): + """Evaluate list.""" + if isinstance(arg.ctx, ast.Load): + return await self.eval_elt_list(arg.elts) + + async def loopvar_scope_save(self, generators): + """Return current scope variables that match looping target vars.""" + # + # looping variables are in their own implicit nested scope, so save/restore + # variables in the current scope with the same names + # + lvars = set() + for gen in generators: + await self.get_names( + ast.Assign(targets=[gen.target], value=ast.Constant(value=None)), local_names=lvars + ) + return lvars, {var: self.sym_table[var] for var in lvars if var in self.sym_table} + + async def loopvar_scope_restore(self, var_names, save_vars): + """Restore current scope variables that match looping target vars.""" + for var_name in var_names: + if var_name in save_vars: + self.sym_table[var_name] = save_vars[var_name] + else: + try: + del self.sym_table[var_name] + except KeyError: + # If the iterator was empty, the loop variables were never + # assigned to, so deleting them will fail. + pass + + async def listcomp_loop(self, generators, elt): + """Recursive list comprehension.""" + out = [] + gen = generators[0] + for loop_var in await self.aeval(gen.iter): + await self.recurse_assign(gen.target, loop_var) + for cond in gen.ifs: + if not await self.aeval(cond): + break + else: + if len(generators) == 1: + out.append(await self.aeval(elt)) + else: + out += await self.listcomp_loop(generators[1:], elt) + return out + + async def ast_listcomp(self, arg): + """Evaluate list comprehension.""" + target_vars, save_values = await self.loopvar_scope_save(arg.generators) + result = await self.listcomp_loop(arg.generators, arg.elt) + await self.loopvar_scope_restore(target_vars, save_values) + return result + + async def ast_tuple(self, arg): + """Evaluate Tuple.""" + return tuple(await self.eval_elt_list(arg.elts)) + + async def ast_dict(self, arg): + """Evaluate dict.""" + val = {} + for key_ast, val_ast in zip(arg.keys, arg.values): + this_val = await self.aeval(val_ast) + if key_ast is None: + val.update(this_val) + else: + val[await self.aeval(key_ast)] = this_val + return val + + async def dictcomp_loop(self, generators, key, value): + """Recursive dict comprehension.""" + out = {} + gen = generators[0] + for loop_var in await self.aeval(gen.iter): + await self.recurse_assign(gen.target, loop_var) + for cond in gen.ifs: + if not await self.aeval(cond): + break + else: + if len(generators) == 1: + # + # key is evaluated before value starting in 3.8 + # + key_val = await self.aeval(key) + out[key_val] = await self.aeval(value) + else: + out.update(await self.dictcomp_loop(generators[1:], key, value)) + return out + + async def ast_dictcomp(self, arg): + """Evaluate dict comprehension.""" + target_vars, save_values = await self.loopvar_scope_save(arg.generators) + result = await self.dictcomp_loop(arg.generators, arg.key, arg.value) + await self.loopvar_scope_restore(target_vars, save_values) + return result + + async def ast_set(self, arg): + """Evaluate set.""" + ret = set() + for elt in await self.eval_elt_list(arg.elts): + ret.add(elt) + return ret + + async def setcomp_loop(self, generators, elt): + """Recursive list comprehension.""" + out = set() + gen = generators[0] + for loop_var in await self.aeval(gen.iter): + await self.recurse_assign(gen.target, loop_var) + for cond in gen.ifs: + if not await self.aeval(cond): + break + else: + if len(generators) == 1: + out.add(await self.aeval(elt)) + else: + out.update(await self.setcomp_loop(generators[1:], elt)) + return out + + async def ast_setcomp(self, arg): + """Evaluate set comprehension.""" + target_vars, save_values = await self.loopvar_scope_save(arg.generators) + result = await self.setcomp_loop(arg.generators, arg.elt) + await self.loopvar_scope_restore(target_vars, save_values) + return result + + async def ast_subscript(self, arg): + """Evaluate subscript.""" + var = await self.aeval(arg.value) + if isinstance(arg.ctx, ast.Load): + if isinstance(arg.slice, ast.Index): + return var[await self.aeval(arg.slice)] + if isinstance(arg.slice, ast.Slice): + lower = (await self.aeval(arg.slice.lower)) if arg.slice.lower else None + upper = (await self.aeval(arg.slice.upper)) if arg.slice.upper else None + step = (await self.aeval(arg.slice.step)) if arg.slice.step else None + return var[slice(lower, upper, step)] + return var[await self.aeval(arg.slice)] + return None + + async def ast_index(self, arg): + """Evaluate index.""" + return await self.aeval(arg.value) + + async def ast_slice(self, arg): + """Evaluate slice.""" + return await self.aeval(arg.value) + + async def ast_call(self, arg): + """Evaluate function call.""" + func = await self.aeval(arg.func) + kwargs = {} + for kw_arg in arg.keywords: + if kw_arg.arg is None: + kwargs.update(await self.aeval(kw_arg.value)) + else: + kwargs[kw_arg.arg] = await self.aeval(kw_arg.value) + args = await self.eval_elt_list(arg.args) + # + # try to deduce function name, although this only works in simple cases + # + func_name = None + if isinstance(arg.func, ast.Name): + func_name = arg.func.id + elif isinstance(arg.func, ast.Attribute): + func_name = arg.func.attr + if isinstance(func, EvalLocalVar): + func_name = func.get_name() + func = func.get() + return await self.call_func(func, func_name, *args, **kwargs) + + async def call_func(self, func, func_name, *args, **kwargs): + """Call a function with the given arguments.""" + if func_name is None: + try: + if isinstance(func, (EvalFunc, EvalFuncVar)): + func_name = func.get_name() + else: + func_name = func.__name__ + except Exception: + func_name = "" + arg_str = ", ".join(['"' + elt + '"' if isinstance(elt, str) else str(elt) for elt in args]) + _LOGGER.debug("%s: calling %s(%s, %s)", self.name, func_name, arg_str, kwargs) + if isinstance(func, (EvalFunc, EvalFuncVar)): + return await func.call(self, *args, **kwargs) + if inspect.isclass(func) and hasattr(func, "__init__evalfunc_wrap__"): + inst = func() + # + # we use weak references when we bind the method calls to the instance inst; + # otherwise these self references cause the object to not be deleted until + # it is later garbage collected + # + inst_weak = weakref.ref(inst) + for name in dir(inst): + value = getattr(inst, name) + if type(value) is not EvalFuncVar: + continue + setattr(inst, name, EvalFuncVarClassInst(value.get_func(), value.get_ast_ctx(), inst_weak)) + if getattr(func, "__init__evalfunc_wrap__") is not None: + # + # since our __init__ function is async, call the renamed one + # + await inst.__init__evalfunc_wrap__.call(self, *args, **kwargs) + return inst + if asyncio.iscoroutinefunction(func): + return await func(*args, **kwargs) + if callable(func): + if func == time.sleep: # pylint: disable=comparison-with-callable + _LOGGER.warning( + "%s line %s calls blocking time.sleep(); replaced with asyncio.sleep()", + self.filename, + self.lineno, + ) + return await asyncio.sleep(*args, **kwargs) + return func(*args, **kwargs) + raise TypeError(f"'{func_name}' is not callable (got {func})") + + async def ast_ifexp(self, arg): + """Evaluate if expression.""" + return await self.aeval(arg.body) if (await self.aeval(arg.test)) else await self.aeval(arg.orelse) + + async def ast_num(self, arg): + """Evaluate number.""" + return arg.n + + async def ast_str(self, arg): + """Evaluate string.""" + return arg.s + + async def ast_nameconstant(self, arg): + """Evaluate name constant.""" + return arg.value + + async def ast_constant(self, arg): + """Evaluate constant.""" + return arg.value + + async def ast_joinedstr(self, arg): + """Evaluate joined string.""" + val = "" + for arg1 in arg.values: + this_val = await self.aeval(arg1) + val = val + str(this_val) + return val + + async def ast_formattedvalue(self, arg): + """Evaluate formatted value.""" + val = await self.aeval(arg.value) + if arg.format_spec is not None: + fmt = await self.aeval(arg.format_spec) + return f"{val:{fmt}}" + return f"{val}" + + async def ast_await(self, arg): + """Evaluate await expr.""" + coro = await self.aeval(arg.value) + if coro: + return await coro + return None + + async def get_target_names(self, lhs): + """Recursively find all the target names mentioned in the AST tree.""" + names = set() + if isinstance(lhs, ast.Tuple): + for lhs_elt in lhs.elts: + if isinstance(lhs_elt, ast.Starred): + names.add(lhs_elt.value.id) + else: + names = names.union(await self.get_target_names(lhs_elt)) + elif isinstance(lhs, ast.Attribute): + var_name = await self.ast_attribute_collapse(lhs, check_undef=False) + if isinstance(var_name, str): + names.add(var_name) + elif isinstance(lhs, ast.Name): + names.add(lhs.id) + return names + + async def get_names_set(self, arg, names, nonlocal_names, global_names, local_names): + """Recursively find all the names mentioned in the AST tree.""" + + cls_name = arg.__class__.__name__ + if cls_name == "Attribute": + full_name = await self.ast_attribute_collapse(arg) + if full_name is not None: + names.add(full_name) + return + if cls_name == "Name": + names.add(arg.id) + return + if cls_name == "Nonlocal" and nonlocal_names is not None: + for var_name in arg.names: + nonlocal_names.add(var_name) + names.add(var_name) + return + if cls_name == "Global" and global_names is not None: + for var_name in arg.names: + global_names.add(var_name) + names.add(var_name) + return + if local_names is not None: + # + # find all the local variables by looking for assignments; + # also, don't recurse into function definitions + # + if cls_name == "Assign": + for target in arg.targets: + for name in await self.get_target_names(target): + local_names.add(name) + names.add(name) + elif cls_name in {"AugAssign", "For", "AsyncFor", "NamedExpr"}: + for name in await self.get_target_names(arg.target): + local_names.add(name) + names.add(name) + elif cls_name in {"With", "AsyncWith"}: + for item in arg.items: + if item.optional_vars: + for name in await self.get_target_names(item.optional_vars): + local_names.add(name) + names.add(name) + elif cls_name in {"ListComp", "DictComp", "SetComp"}: + target_vars, _ = await self.loopvar_scope_save(arg.generators) + for name in target_vars: + local_names.add(name) + elif cls_name == "Try": + for handler in arg.handlers: + if handler.name is not None: + local_names.add(handler.name) + names.add(handler.name) + elif cls_name == "Call": + await self.get_names_set(arg.func, names, nonlocal_names, global_names, local_names) + for this_arg in arg.args: + await self.get_names_set(this_arg, names, nonlocal_names, global_names, local_names) + for this_arg in arg.keywords or []: + await self.get_names_set(this_arg, names, nonlocal_names, global_names, local_names) + return + elif cls_name in {"FunctionDef", "ClassDef", "AsyncFunctionDef"}: + local_names.add(arg.name) + names.add(arg.name) + for dec in arg.decorator_list: + await self.get_names_set(dec, names, nonlocal_names, global_names, local_names) + # + # find unbound names from the body of the function or class + # + inner_global, inner_names, inner_local = set(), set(), set() + for child in arg.body: + await self.get_names_set(child, inner_names, None, inner_global, inner_local) + for name in inner_names: + if name not in inner_local and name not in inner_global: + names.add(name) + return + elif cls_name == "Delete": + for arg1 in arg.targets: + if isinstance(arg1, ast.Name): + local_names.add(arg1.id) + for child in ast.iter_child_nodes(arg): + await self.get_names_set(child, names, nonlocal_names, global_names, local_names) + + async def get_names(self, this_ast=None, nonlocal_names=None, global_names=None, local_names=None): + """Return set of all the names mentioned in our AST tree.""" + names = set() + this_ast = this_ast or self.ast + if this_ast: + await self.get_names_set(this_ast, names, nonlocal_names, global_names, local_names) + return names + + def parse(self, code_str, filename=None, mode="exec"): + """Parse the code_str source code into an AST tree.""" + self.exception = None + self.exception_obj = None + self.exception_long = None + self.ast = None + if filename is not None: + self.filename = filename + try: + if isinstance(code_str, list): + self.code_list = code_str + self.code_str = "\n".join(code_str) + elif isinstance(code_str, str): + self.code_str = code_str + self.code_list = code_str.split("\n") + else: + self.code_str = code_str + self.code_list = [] + self.ast = ast.parse(self.code_str, filename=self.filename, mode=mode) + return True + except SyntaxError as err: + self.exception_obj = err + self.lineno = err.lineno + self.col_offset = err.offset - 1 + self.exception = f"syntax error {err}" + if err.filename == self.filename: + self.exception_long = self.format_exc(err, self.lineno, self.col_offset) + else: + self.exception_long = self.format_exc(err, 1, self.col_offset, code_list=[err.text]) + return False + except asyncio.CancelledError: + raise + except Exception as err: + self.exception_obj = err + self.lineno = 1 + self.col_offset = 0 + self.exception = f"parsing error {err}" + self.exception_long = self.format_exc(err) + return False + + def format_exc(self, exc, lineno=None, col_offset=None, short=False, code_list=None): + """Format an multi-line exception message using lineno if available.""" + if code_list is None: + code_list = self.code_list + if lineno is not None and lineno <= len(code_list): + if short: + mesg = f"In <{self.filename}> line {lineno}:\n" + mesg += " " + code_list[lineno - 1] + else: + mesg = f"Exception in <{self.filename}> line {lineno}:\n" + mesg += " " + code_list[lineno - 1] + "\n" + if col_offset is not None: + mesg += " " + " " * col_offset + "^\n" + mesg += f"{type(exc).__name__}: {exc}" + else: + mesg = f"Exception in <{self.filename}>:\n" + mesg += f"{type(exc).__name__}: {exc}" + # + # to get a more detailed traceback on exception (eg, when chasing an internal + # error), add an "import traceback" above, and uncomment this next line + # + # return mesg + "\n" + traceback.format_exc(-1) + return mesg + + def get_exception(self): + """Return the last exception str.""" + return self.exception + + def get_exception_obj(self): + """Return the last exception object.""" + return self.exception_obj + + def get_exception_long(self): + """Return the last exception in a longer str form.""" + return self.exception_long + + def set_local_sym_table(self, sym_table): + """Set the local symbol table.""" + self.local_sym_table = sym_table + + def set_global_ctx(self, global_ctx): + """Set the global context.""" + self.global_ctx = global_ctx + if self.sym_table == self.global_sym_table: + self.global_sym_table = global_ctx.get_global_sym_table() + self.sym_table = self.global_sym_table + else: + self.global_sym_table = global_ctx.get_global_sym_table() + if len(self.sym_table_stack) > 0: + self.sym_table_stack[0] = self.global_sym_table + + def get_global_ctx(self): + """Return the global context.""" + return self.global_ctx + + def get_global_ctx_name(self): + """Return the global context name.""" + return self.global_ctx.get_name() + + def set_logger_name(self, name): + """Set the context's logger name.""" + if self.logger: + for handler in self.logger_handlers: + self.logger.removeHandler(handler) + self.logger_name = name + self.logger = logging.getLogger(LOGGER_PATH + "." + name) + for handler in self.logger_handlers: + self.logger.addHandler(handler) + + def get_logger_name(self): + """Get the context's logger name.""" + return self.logger_name + + def get_logger(self): + """Get the context's logger.""" + return self.logger + + def add_logger_handler(self, handler): + """Add logger handler to this context.""" + self.logger.addHandler(handler) + self.logger_handlers.add(handler) + + def remove_logger_handler(self, handler): + """Remove logger handler to this context.""" + self.logger.removeHandler(handler) + self.logger_handlers.discard(handler) + + def completions(self, root): + """Return potential variable, function or attribute matches.""" + words = set() + num_period = root.count(".") + if num_period >= 1: + last_period = root.rfind(".") + name = root[0:last_period] + attr_root = root[last_period + 1 :] + if name in self.global_sym_table: + var = self.global_sym_table[name] + try: + for attr in var.__dict__: + if attr.lower().startswith(attr_root) and (attr_root != "" or attr[0:1] != "_"): + words.add(f"{name}.{attr}") + except Exception: + pass + for keyw in set(keyword.kwlist) - {"yield"}: + if keyw.lower().startswith(root): + words.add(keyw) + sym_table = BUILTIN_AST_FUNCS_FACTORY.copy() + for name, value in builtins.__dict__.items(): + if name[0] != "_" and name not in BUILTIN_EXCLUDE: + sym_table[name] = value + sym_table.update(self.global_sym_table.items()) + for name, value in sym_table.items(): + if name.lower().startswith(root): + words.add(name) + return words + + async def eval(self, new_state_vars=None, merge_local=False): + """Execute parsed code, with the optional state variables added to the scope.""" + self.exception = None + self.exception_obj = None + self.exception_long = None + if new_state_vars: + if not merge_local: + self.local_sym_table = {} + self.local_sym_table.update(new_state_vars) + if self.ast: + try: + val = await self.aeval(self.ast) + if isinstance(val, EvalStopFlow): + return None + return val + except asyncio.CancelledError: + raise + except Exception as err: + if self.exception_long is None: + self.exception_long = self.format_exc(err, self.lineno, self.col_offset) + return None + + def dump(self, this_ast=None): + """Dump the AST tree for debugging.""" + return ast.dump(this_ast if this_ast else self.ast) diff --git a/config/custom_components/pyscript/event.py b/config/custom_components/pyscript/event.py new file mode 100644 index 0000000..bcc6703 --- /dev/null +++ b/config/custom_components/pyscript/event.py @@ -0,0 +1,76 @@ +"""Handles event firing and notification.""" + +import logging + +from .const import LOGGER_PATH + +_LOGGER = logging.getLogger(LOGGER_PATH + ".event") + + +class Event: + """Define event functions.""" + + # + # Global hass instance + # + hass = None + + # + # notify message queues by event type + # + notify = {} + notify_remove = {} + + def __init__(self): + """Warn on Event instantiation.""" + _LOGGER.error("Event class is not meant to be instantiated") + + @classmethod + def init(cls, hass): + """Initialize Event.""" + + cls.hass = hass + + @classmethod + async def event_listener(cls, event): + """Listen callback for given event which updates any notifications.""" + + func_args = { + "trigger_type": "event", + "event_type": event.event_type, + "context": event.context, + } + func_args.update(event.data) + await cls.update(event.event_type, func_args) + + @classmethod + def notify_add(cls, event_type, queue): + """Register to notify for events of given type to be sent to queue.""" + + if event_type not in cls.notify: + cls.notify[event_type] = set() + _LOGGER.debug("event.notify_add(%s) -> adding event listener", event_type) + cls.notify_remove[event_type] = cls.hass.bus.async_listen(event_type, cls.event_listener) + cls.notify[event_type].add(queue) + + @classmethod + def notify_del(cls, event_type, queue): + """Unregister to notify for events of given type for given queue.""" + + if event_type not in cls.notify or queue not in cls.notify[event_type]: + return + cls.notify[event_type].discard(queue) + if len(cls.notify[event_type]) == 0: + cls.notify_remove[event_type]() + _LOGGER.debug("event.notify_del(%s) -> removing event listener", event_type) + del cls.notify[event_type] + del cls.notify_remove[event_type] + + @classmethod + async def update(cls, event_type, func_args): + """Deliver all notifications for an event of the given type.""" + + _LOGGER.debug("event.update(%s, %s)", event_type, func_args) + if event_type in cls.notify: + for queue in cls.notify[event_type]: + await queue.put(["event", func_args.copy()]) diff --git a/config/custom_components/pyscript/function.py b/config/custom_components/pyscript/function.py new file mode 100644 index 0000000..02388a3 --- /dev/null +++ b/config/custom_components/pyscript/function.py @@ -0,0 +1,519 @@ +"""Function call handling.""" + +import asyncio +import logging +import traceback + +from homeassistant.core import Context + +from .const import LOGGER_PATH, SERVICE_RESPONSE_NONE, SERVICE_RESPONSE_ONLY + +_LOGGER = logging.getLogger(LOGGER_PATH + ".function") + + +class Function: + """Define function handler functions.""" + + # + # Global hass instance + # + hass = None + + # + # Mappings of tasks ids <-> task names + # + unique_task2name = {} + unique_name2task = {} + + # + # Mappings of task id to hass contexts + task2context = {} + + # + # Set of tasks that are running + # + our_tasks = set() + + # + # Done callbacks for each task + # + task2cb = {} + + # + # initial list of available functions + # + functions = {} + + # + # Functions that take the AstEval context as a first argument, + # which is needed by a handful of special functions that need the + # ast context + # + ast_functions = {} + + # + # task id of the task that cancels and waits for other tasks, + # + task_reaper = None + task_reaper_q = None + + # + # task id of the task that awaits for coros (used by shutdown triggers) + # + task_waiter = None + task_waiter_q = None + + # + # reference counting for service registrations; the new @service trigger + # registers the service call before the old one is removed, so we only + # remove the service registration when the reference count goes to zero + # + service_cnt = {} + + # + # save the global_ctx name where a service is registered so we can raise + # an exception if it gets registered by a different global_ctx. + # + service2global_ctx = {} + + def __init__(self): + """Warn on Function instantiation.""" + _LOGGER.error("Function class is not meant to be instantiated") + + @classmethod + def init(cls, hass): + """Initialize Function.""" + cls.hass = hass + cls.functions.update( + { + "event.fire": cls.event_fire, + "service.call": cls.service_call, + "service.has_service": cls.service_has_service, + "task.cancel": cls.user_task_cancel, + "task.current_task": cls.user_task_current_task, + "task.remove_done_callback": cls.user_task_remove_done_callback, + "task.sleep": cls.async_sleep, + "task.wait": cls.user_task_wait, + } + ) + cls.ast_functions.update( + { + "log.debug": lambda ast_ctx: ast_ctx.get_logger().debug, + "log.error": lambda ast_ctx: ast_ctx.get_logger().error, + "log.info": lambda ast_ctx: ast_ctx.get_logger().info, + "log.warning": lambda ast_ctx: ast_ctx.get_logger().warning, + "print": lambda ast_ctx: ast_ctx.get_logger().debug, + "task.name2id": cls.task_name2id_factory, + "task.unique": cls.task_unique_factory, + } + ) + + # + # start a task which is a reaper for canceled tasks, since some # functions + # like TrigInfo.stop() can't be async (it's called from a __del__ method) + # + async def task_reaper(reaper_q): + while True: + try: + cmd = await reaper_q.get() + if cmd[0] == "exit": + return + if cmd[0] == "cancel": + try: + cmd[1].cancel() + await cmd[1] + except asyncio.CancelledError: + pass + else: + _LOGGER.error("task_reaper: unknown command %s", cmd[0]) + except asyncio.CancelledError: + raise + except Exception: + _LOGGER.error("task_reaper: got exception %s", traceback.format_exc(-1)) + + if not cls.task_reaper: + cls.task_reaper_q = asyncio.Queue(0) + cls.task_reaper = cls.create_task(task_reaper(cls.task_reaper_q)) + + # + # start a task which creates tasks to run coros, and then syncs on their completion; + # this is used by the shutdown trigger + # + async def task_waiter(waiter_q): + aws = [] + while True: + try: + cmd = await waiter_q.get() + if cmd[0] == "exit": + return + if cmd[0] == "await": + aws.append(cls.create_task(cmd[1])) + elif cmd[0] == "sync": + if len(aws) > 0: + await asyncio.gather(*aws) + aws = [] + await cmd[1].put(0) + else: + _LOGGER.error("task_waiter: unknown command %s", cmd[0]) + except asyncio.CancelledError: + raise + except Exception: + _LOGGER.error("task_waiter: got exception %s", traceback.format_exc(-1)) + + if not cls.task_waiter: + cls.task_waiter_q = asyncio.Queue(0) + cls.task_waiter = cls.create_task(task_waiter(cls.task_waiter_q)) + + @classmethod + def reaper_cancel(cls, task): + """Send a task to be canceled by the reaper.""" + cls.task_reaper_q.put_nowait(["cancel", task]) + + @classmethod + async def reaper_stop(cls): + """Tell the reaper task to exit.""" + if cls.task_reaper: + cls.task_reaper_q.put_nowait(["exit"]) + await cls.task_reaper + cls.task_reaper = None + cls.task_reaper_q = None + + @classmethod + def waiter_await(cls, coro): + """Send a coro to be awaited by the waiter task.""" + cls.task_waiter_q.put_nowait(["await", coro]) + + @classmethod + async def waiter_sync(cls): + """Wait until the waiter queue is empty.""" + if cls.task_waiter: + sync_q = asyncio.Queue(0) + cls.task_waiter_q.put_nowait(["sync", sync_q]) + await sync_q.get() + + @classmethod + async def waiter_stop(cls): + """Tell the waiter task to exit.""" + if cls.task_waiter: + cls.task_waiter_q.put_nowait(["exit"]) + await cls.task_waiter + cls.task_waiter = None + cls.task_waiter_q = None + + @classmethod + async def async_sleep(cls, duration): + """Implement task.sleep().""" + await asyncio.sleep(float(duration)) + + @classmethod + async def event_fire(cls, event_type, **kwargs): + """Implement event.fire().""" + curr_task = asyncio.current_task() + if "context" in kwargs and isinstance(kwargs["context"], Context): + context = kwargs["context"] + del kwargs["context"] + else: + context = cls.task2context.get(curr_task, None) + + cls.hass.bus.async_fire(event_type, kwargs, context=context) + + @classmethod + def store_hass_context(cls, hass_context): + """Store a context against the running task.""" + curr_task = asyncio.current_task() + cls.task2context[curr_task] = hass_context + + @classmethod + def task_unique_factory(cls, ctx): + """Define and return task.unique() for this context.""" + + async def task_unique(name, kill_me=False): + """Implement task.unique().""" + name = f"{ctx.get_global_ctx_name()}.{name}" + curr_task = asyncio.current_task() + if name in cls.unique_name2task: + task = cls.unique_name2task[name] + if kill_me: + if task != curr_task: + # + # it seems we can't cancel ourselves, so we + # tell the reaper task to cancel us + # + cls.reaper_cancel(curr_task) + # wait to be canceled + await asyncio.sleep(100000) + elif task != curr_task and task in cls.our_tasks: + # only cancel tasks if they are ones we started + cls.reaper_cancel(task) + if curr_task in cls.our_tasks: + if name in cls.unique_name2task: + task = cls.unique_name2task[name] + if task in cls.unique_task2name: + cls.unique_task2name[task].discard(name) + cls.unique_name2task[name] = curr_task + if curr_task not in cls.unique_task2name: + cls.unique_task2name[curr_task] = set() + cls.unique_task2name[curr_task].add(name) + + return task_unique + + @classmethod + async def user_task_cancel(cls, task=None): + """Implement task.cancel().""" + do_sleep = False + if not task: + task = asyncio.current_task() + do_sleep = True + if task not in cls.our_tasks: + raise TypeError(f"{task} is not a user-started task") + cls.reaper_cancel(task) + if do_sleep: + # wait to be canceled + await asyncio.sleep(100000) + + @classmethod + async def user_task_current_task(cls): + """Implement task.current_task().""" + return asyncio.current_task() + + @classmethod + def task_name2id_factory(cls, ctx): + """Define and return task.name2id() for this context.""" + + def user_task_name2id(name=None): + """Implement task.name2id().""" + prefix = f"{ctx.get_global_ctx_name()}." + if name is None: + ret = {} + for task_name, task_id in cls.unique_name2task.items(): + if task_name.startswith(prefix): + ret[task_name[len(prefix) :]] = task_id + return ret + if prefix + name in cls.unique_name2task: + return cls.unique_name2task[prefix + name] + raise NameError(f"task name '{name}' is unknown") + + return user_task_name2id + + @classmethod + async def user_task_wait(cls, aws, **kwargs): + """Implement task.wait().""" + return await asyncio.wait(aws, **kwargs) + + @classmethod + def user_task_remove_done_callback(cls, task, callback): + """Implement task.remove_done_callback().""" + cls.task2cb[task]["cb"].pop(callback, None) + + @classmethod + def unique_name_used(cls, ctx, name): + """Return whether the current unique name is in use.""" + name = f"{ctx.get_global_ctx_name()}.{name}" + return name in cls.unique_name2task + + @classmethod + def service_has_service(cls, domain, name): + """Implement service.has_service().""" + return cls.hass.services.has_service(domain, name) + + @classmethod + async def service_call(cls, domain, name, **kwargs): + """Implement service.call().""" + curr_task = asyncio.current_task() + hass_args = {} + for keyword, typ, default in [ + ("context", [Context], cls.task2context.get(curr_task, None)), + ("blocking", [bool], None), + ("return_response", [bool], None), + ]: + if keyword in kwargs and type(kwargs[keyword]) in typ: + hass_args[keyword] = kwargs.pop(keyword) + elif default: + hass_args[keyword] = default + + return await cls.hass_services_async_call(domain, name, kwargs, **hass_args) + + @classmethod + async def service_completions(cls, root): + """Return possible completions of HASS services.""" + words = set() + services = cls.hass.services.async_services() + num_period = root.count(".") + if num_period == 1: + domain, svc_root = root.split(".") + if domain in services: + words |= {f"{domain}.{svc}" for svc in services[domain] if svc.lower().startswith(svc_root)} + elif num_period == 0: + words |= {domain for domain in services if domain.lower().startswith(root)} + + return words + + @classmethod + async def func_completions(cls, root): + """Return possible completions of functions.""" + funcs = {**cls.functions, **cls.ast_functions} + words = {name for name in funcs if name.lower().startswith(root)} + + return words + + @classmethod + def register(cls, funcs): + """Register functions to be available for calling.""" + cls.functions.update(funcs) + + @classmethod + def register_ast(cls, funcs): + """Register functions that need ast context to be available for calling.""" + cls.ast_functions.update(funcs) + + @classmethod + def install_ast_funcs(cls, ast_ctx): + """Install ast functions into the local symbol table.""" + sym_table = {name: func(ast_ctx) for name, func in cls.ast_functions.items()} + ast_ctx.set_local_sym_table(sym_table) + + @classmethod + def get(cls, name): + """Lookup a function locally and then as a service.""" + func = cls.functions.get(name, None) + if func: + return func + + name_parts = name.split(".") + if len(name_parts) != 2: + return None + + domain, service = name_parts + if not cls.service_has_service(domain, service): + return None + + def service_call_factory(domain, service): + async def service_call(*args, **kwargs): + curr_task = asyncio.current_task() + hass_args = {} + for keyword, typ, default in [ + ("context", [Context], cls.task2context.get(curr_task, None)), + ("blocking", [bool], None), + ("return_response", [bool], None), + ]: + if keyword in kwargs and type(kwargs[keyword]) in typ: + hass_args[keyword] = kwargs.pop(keyword) + elif default: + hass_args[keyword] = default + + if len(args) != 0: + raise TypeError(f"service {domain}.{service} takes only keyword arguments") + + return await cls.hass_services_async_call(domain, service, kwargs, **hass_args) + + return service_call + + return service_call_factory(domain, service) + + @classmethod + async def hass_services_async_call(cls, domain, service, kwargs, **hass_args): + """Call a hass async service.""" + if SERVICE_RESPONSE_ONLY is None: + # backwards compatibility < 2023.7 + await cls.hass.services.async_call(domain, service, kwargs, **hass_args) + else: + # allow service responses >= 2023.7 + if ( + "return_response" in hass_args + and hass_args["return_response"] + and "blocking" not in hass_args + ): + hass_args["blocking"] = True + elif ( + "return_response" not in hass_args + and cls.hass.services.supports_response(domain, service) == SERVICE_RESPONSE_ONLY + ): + hass_args["return_response"] = True + if "blocking" not in hass_args: + hass_args["blocking"] = True + return await cls.hass.services.async_call(domain, service, kwargs, **hass_args) + + @classmethod + async def run_coro(cls, coro, ast_ctx=None): + """Run coroutine task and update unique task on start and exit.""" + # + # Add a placeholder for the new task so we know it's one we started + # + task: asyncio.Task = None + try: + task = asyncio.current_task() + cls.our_tasks.add(task) + if ast_ctx is not None: + cls.task_done_callback_ctx(task, ast_ctx) + result = await coro + return result + except asyncio.CancelledError: + raise + except Exception: + _LOGGER.error("run_coro: got exception %s", traceback.format_exc(-1)) + finally: + if task in cls.task2cb: + for callback, info in cls.task2cb[task]["cb"].items(): + ast_ctx, args, kwargs = info + await ast_ctx.call_func(callback, None, *args, **kwargs) + if ast_ctx.get_exception_obj(): + ast_ctx.get_logger().error(ast_ctx.get_exception_long()) + break + if task in cls.unique_task2name: + for name in cls.unique_task2name[task]: + del cls.unique_name2task[name] + del cls.unique_task2name[task] + cls.task2context.pop(task, None) + cls.task2cb.pop(task, None) + cls.our_tasks.discard(task) + + @classmethod + def create_task(cls, coro, ast_ctx=None): + """Create a new task that runs a coroutine.""" + return cls.hass.loop.create_task(cls.run_coro(coro, ast_ctx=ast_ctx)) + + @classmethod + def service_register( + cls, global_ctx_name, domain, service, callback, supports_response=SERVICE_RESPONSE_NONE + ): + """Register a new service callback.""" + key = f"{domain}.{service}" + if key not in cls.service_cnt: + cls.service_cnt[key] = 0 + if key not in cls.service2global_ctx: + cls.service2global_ctx[key] = global_ctx_name + if cls.service2global_ctx[key] != global_ctx_name: + raise ValueError( + f"{global_ctx_name}: can't register service {key}; already defined in {cls.service2global_ctx[key]}" + ) + cls.service_cnt[key] += 1 + if SERVICE_RESPONSE_ONLY is None: + # backwards compatibility < 2023.7 + cls.hass.services.async_register(domain, service, callback) + else: + # allow service responses >= 2023.7 + cls.hass.services.async_register(domain, service, callback, supports_response=supports_response) + + @classmethod + def service_remove(cls, global_ctx_name, domain, service): + """Remove a service callback.""" + key = f"{domain}.{service}" + if cls.service_cnt.get(key, 0) > 1: + cls.service_cnt[key] -= 1 + return + cls.service_cnt[key] = 0 + cls.hass.services.async_remove(domain, service) + cls.service2global_ctx.pop(key, None) + + @classmethod + def task_done_callback_ctx(cls, task, ast_ctx): + """Set the ast_ctx for a task, which is needed for done callbacks.""" + if task not in cls.task2cb or "ctx" not in cls.task2cb[task]: + cls.task2cb[task] = {"ctx": ast_ctx, "cb": {}} + + @classmethod + def task_add_done_callback(cls, task, ast_ctx, callback, *args, **kwargs): + """Add a done callback to the given task.""" + if ast_ctx is None: + ast_ctx = cls.task2cb[task]["ctx"] + cls.task2cb[task]["cb"][callback] = [ast_ctx, args, kwargs] diff --git a/config/custom_components/pyscript/global_ctx.py b/config/custom_components/pyscript/global_ctx.py new file mode 100644 index 0000000..9041b3e --- /dev/null +++ b/config/custom_components/pyscript/global_ctx.py @@ -0,0 +1,352 @@ +"""Global context handling.""" + +import logging +import os +from types import ModuleType +from typing import Any, Callable, Dict, List, Optional, Set, Union + +from homeassistant.config_entries import ConfigEntry + +from .const import CONF_HASS_IS_GLOBAL, CONFIG_ENTRY, DOMAIN, FOLDER, LOGGER_PATH +from .eval import AstEval, EvalFunc +from .function import Function +from .trigger import TrigInfo + +_LOGGER = logging.getLogger(LOGGER_PATH + ".global_ctx") + + +class GlobalContext: + """Define class for global variables and trigger context.""" + + def __init__( + self, + name, + global_sym_table: Dict[str, Any] = None, + manager=None, + rel_import_path: str = None, + app_config: Dict[str, Any] = None, + source: str = None, + mtime: float = None, + ) -> None: + """Initialize GlobalContext.""" + self.name: str = name + self.global_sym_table: Dict[str, Any] = global_sym_table if global_sym_table else {} + self.triggers: Set[EvalFunc] = set() + self.triggers_delay_start: Set[EvalFunc] = set() + self.logger: logging.Logger = logging.getLogger(LOGGER_PATH + "." + name) + self.manager: GlobalContextMgr = manager + self.auto_start: bool = False + self.module: ModuleType = None + self.rel_import_path: str = rel_import_path + self.source: str = source + self.file_path: str = None + self.mtime: float = mtime + self.app_config: Dict[str, Any] = app_config + self.imports: Set[str] = set() + config_entry: ConfigEntry = Function.hass.data.get(DOMAIN, {}).get(CONFIG_ENTRY, {}) + if config_entry.data.get(CONF_HASS_IS_GLOBAL, False): + # + # expose hass as a global variable if configured + # + self.global_sym_table["hass"] = Function.hass + if app_config: + self.global_sym_table["pyscript.app_config"] = app_config.copy() + + def trigger_register(self, func: EvalFunc) -> bool: + """Register a trigger function; return True if start now.""" + self.triggers.add(func) + if self.auto_start: + return True + self.triggers_delay_start.add(func) + return False + + def trigger_unregister(self, func: EvalFunc) -> None: + """Unregister a trigger function.""" + self.triggers.discard(func) + self.triggers_delay_start.discard(func) + + def set_auto_start(self, auto_start: bool) -> None: + """Set the auto-start flag.""" + self.auto_start = auto_start + + def start(self) -> None: + """Start any unstarted triggers.""" + for func in self.triggers_delay_start: + func.trigger_start() + self.triggers_delay_start = set() + + def stop(self) -> None: + """Stop all triggers and auto_start.""" + for func in self.triggers: + func.trigger_stop() + self.triggers = set() + self.triggers_delay_start = set() + self.set_auto_start(False) + + def get_name(self) -> str: + """Return the global context name.""" + return self.name + + def set_logger_name(self, name) -> None: + """Set the global context logging name.""" + self.logger = logging.getLogger(LOGGER_PATH + "." + name) + + def get_global_sym_table(self) -> Dict[str, Any]: + """Return the global symbol table.""" + return self.global_sym_table + + def get_source(self) -> str: + """Return the source code.""" + return self.source + + def get_app_config(self) -> Dict[str, Any]: + """Return the app config.""" + return self.app_config + + def get_mtime(self) -> float: + """Return the mtime.""" + return self.mtime + + def get_file_path(self) -> str: + """Return the file path.""" + return self.file_path + + def get_imports(self) -> Set[str]: + """Return the imports.""" + return self.imports + + def get_trig_info(self, name: str, trig_args: Dict[str, Any]) -> TrigInfo: + """Return a new trigger info instance with the given args.""" + return TrigInfo(name, trig_args, self) + + async def module_import(self, module_name: str, import_level: int) -> List[Optional[str]]: + """Import a pyscript module from the pyscript/modules or apps folder.""" + + pyscript_dir = Function.hass.config.path(FOLDER) + module_path = module_name.replace(".", "/") + file_paths = [] + + def find_first_file(file_paths: List[Set[str]]) -> List[Optional[Union[str, ModuleType]]]: + for ctx_name, path, rel_path in file_paths: + abs_path = os.path.join(pyscript_dir, path) + if os.path.isfile(abs_path): + return [ctx_name, abs_path, rel_path] + return None + + # + # first build a list of potential import files + # + if import_level > 0: + if self.rel_import_path is None: + raise ImportError("attempted relative import with no known parent package") + path = self.rel_import_path + if path.endswith("/__init__"): + path = os.path.dirname(path) + ctx_name = self.name + for _ in range(import_level - 1): + path = os.path.dirname(path) + idx = ctx_name.rfind(".") + if path.find("/") < 0 or idx < 0: + raise ImportError("attempted relative import above parent package") + ctx_name = ctx_name[0:idx] + ctx_name += f".{module_name}" + module_info = [ctx_name, f"{path}/{module_path}.py", path] + path += f"/{module_path}" + file_paths.append([ctx_name, f"{path}/__init__.py", path]) + file_paths.append(module_info) + module_name = ctx_name[ctx_name.find(".") + 1 :] + + else: + if self.rel_import_path is not None and self.rel_import_path.startswith("apps/"): + ctx_name = f"apps.{module_name}" + file_paths.append([ctx_name, f"apps/{module_path}/__init__.py", f"apps/{module_path}"]) + file_paths.append([ctx_name, f"apps/{module_path}.py", f"apps/{module_path}"]) + + ctx_name = f"modules.{module_name}" + file_paths.append([ctx_name, f"modules/{module_path}/__init__.py", f"modules/{module_path}"]) + file_paths.append([ctx_name, f"modules/{module_path}.py", None]) + + # + # now see if we have loaded it already + # + for ctx_name, _, _ in file_paths: + mod_ctx = self.manager.get(ctx_name) + if mod_ctx and mod_ctx.module: + self.imports.add(mod_ctx.get_name()) + return [mod_ctx.module, None] + + # + # not loaded already, so try to find and import it + # + file_info = await Function.hass.async_add_executor_job(find_first_file, file_paths) + if not file_info: + return [None, None] + + [ctx_name, file_path, rel_import_path] = file_info + + mod = ModuleType(module_name) + global_ctx = GlobalContext( + ctx_name, global_sym_table=mod.__dict__, manager=self.manager, rel_import_path=rel_import_path + ) + global_ctx.set_auto_start(True) + _, error_ctx = await self.manager.load_file(global_ctx, file_path) + if error_ctx: + _LOGGER.error( + "module_import: failed to load module %s, ctx = %s, path = %s", + module_name, + ctx_name, + file_path, + ) + return [None, error_ctx] + global_ctx.module = mod + self.imports.add(ctx_name) + return [mod, None] + + +class GlobalContextMgr: + """Define class for all global contexts.""" + + # + # map of context names to contexts + # + contexts = {} + + # + # sequence number for sessions + # + name_seq = 0 + + def __init__(self) -> None: + """Report an error if GlobalContextMgr in instantiated.""" + _LOGGER.error("GlobalContextMgr class is not meant to be instantiated") + + @classmethod + def init(cls) -> None: + """Initialize GlobalContextMgr.""" + + def get_global_ctx_factory(ast_ctx: AstEval) -> Callable[[], str]: + """Generate a pyscript.get_global_ctx() function with given ast_ctx.""" + + async def get_global_ctx(): + return ast_ctx.get_global_ctx_name() + + return get_global_ctx + + def list_global_ctx_factory(ast_ctx: AstEval) -> Callable[[], List[str]]: + """Generate a pyscript.list_global_ctx() function with given ast_ctx.""" + + async def list_global_ctx(): + ctx_names = set(cls.contexts.keys()) + curr_ctx_name = ast_ctx.get_global_ctx_name() + ctx_names.discard(curr_ctx_name) + return [curr_ctx_name] + sorted(sorted(ctx_names)) + + return list_global_ctx + + def set_global_ctx_factory(ast_ctx: AstEval) -> Callable[[str], None]: + """Generate a pyscript.set_global_ctx() function with given ast_ctx.""" + + async def set_global_ctx(name): + global_ctx = cls.get(name) + if global_ctx is None: + raise NameError(f"global context '{name}' does not exist") + ast_ctx.set_global_ctx(global_ctx) + ast_ctx.set_logger_name(global_ctx.name) + + return set_global_ctx + + ast_funcs = { + "pyscript.get_global_ctx": get_global_ctx_factory, + "pyscript.list_global_ctx": list_global_ctx_factory, + "pyscript.set_global_ctx": set_global_ctx_factory, + } + + Function.register_ast(ast_funcs) + + @classmethod + def get(cls, name: str) -> Optional[str]: + """Return the GlobalContext given a name.""" + return cls.contexts.get(name, None) + + @classmethod + def set(cls, name: str, global_ctx: GlobalContext) -> None: + """Save the GlobalContext by name.""" + cls.contexts[name] = global_ctx + + @classmethod + def items(cls) -> List[Set[Union[str, GlobalContext]]]: + """Return all the global context items.""" + return sorted(cls.contexts.items()) + + @classmethod + def delete(cls, name: str) -> None: + """Delete the given GlobalContext.""" + if name in cls.contexts: + global_ctx = cls.contexts[name] + global_ctx.stop() + del cls.contexts[name] + + @classmethod + def new_name(cls, root: str) -> str: + """Find a unique new name by appending a sequence number to root.""" + while True: + name = f"{root}{cls.name_seq}" + cls.name_seq += 1 + if name not in cls.contexts: + return name + + @classmethod + async def load_file( + cls, global_ctx: GlobalContext, file_path: str, source: str = None, reload: bool = False + ) -> Set[Union[bool, AstEval]]: + """Load, parse and run the given script file; returns error ast_ctx on error, or None if ok.""" + + mtime = None + if source is None: + + def read_file(path: str) -> Set[Union[str, float]]: + try: + with open(path, encoding="utf-8") as file_desc: + source = file_desc.read() + return source, os.path.getmtime(path) + except Exception as exc: + _LOGGER.error("%s", exc) + return None, 0 + + source, mtime = await Function.hass.async_add_executor_job(read_file, file_path) + + if source is None: + return False, None + + ctx_curr = cls.get(global_ctx.get_name()) + if ctx_curr: + # stop triggers and destroy old global context + ctx_curr.stop() + cls.delete(global_ctx.get_name()) + + # + # create new ast eval context and parse source file + # + ast_ctx = AstEval(global_ctx.get_name(), global_ctx) + Function.install_ast_funcs(ast_ctx) + + if not ast_ctx.parse(source, filename=file_path): + exc = ast_ctx.get_exception_long() + ast_ctx.get_logger().error(exc) + global_ctx.stop() + return False, ast_ctx + await ast_ctx.eval() + exc = ast_ctx.get_exception_long() + if exc is not None: + ast_ctx.get_logger().error(exc) + global_ctx.stop() + return False, ast_ctx + global_ctx.source = source + global_ctx.file_path = file_path + if mtime is not None: + global_ctx.mtime = mtime + cls.set(global_ctx.get_name(), global_ctx) + + _LOGGER.info("%s %s", "Reloaded" if reload else "Loaded", file_path) + + return True, None diff --git a/config/custom_components/pyscript/jupyter_kernel.py b/config/custom_components/pyscript/jupyter_kernel.py new file mode 100644 index 0000000..9b63763 --- /dev/null +++ b/config/custom_components/pyscript/jupyter_kernel.py @@ -0,0 +1,921 @@ +"""Pyscript Jupyter kernel.""" + +# +# Based on simple_kernel.py by Doug Blank +# https://github.com/dsblank/simple_kernel +# license: public domain +# Thanks Doug! +# + +import asyncio +import datetime +import hashlib +import hmac +import json +import logging +import logging.handlers +import re +from struct import pack, unpack +import traceback +import uuid + +from .const import LOGGER_PATH +from .function import Function +from .global_ctx import GlobalContextMgr +from .state import State + +_LOGGER = logging.getLogger(LOGGER_PATH + ".jupyter_kernel") + +# Globals: + +DELIM = b"" + + +def msg_id(): + """Return a new uuid for message id.""" + return str(uuid.uuid4()) + + +def str_to_bytes(string): + """Encode a string in bytes.""" + return string.encode("utf-8") + + +class KernelBufferingHandler(logging.handlers.BufferingHandler): + """Memory-based handler for logging; send via stdout queue.""" + + def __init__(self, housekeep_q): + """Initialize KernelBufferingHandler instance.""" + super().__init__(0) + self.housekeep_q = housekeep_q + + def flush(self): + """Flush is a no-op.""" + + def shouldFlush(self, record): + """Write the buffer to the housekeeping queue.""" + try: + self.housekeep_q.put_nowait(["stdout", self.format(record)]) + except asyncio.QueueFull: + _LOGGER.error("housekeep_q unexpectedly full") + + +################################################################ +class ZmqSocket: + """Defines a minimal implementation of a small subset of ZMQ.""" + + # + # This allows pyscript to work with Jupyter without the real zmq + # and pyzmq packages, which might not be available or easy to + # install on the wide set of HASS platforms. + # + def __init__(self, reader, writer, sock_type): + """Initialize a ZMQ socket with the given type and reader/writer streams.""" + self.writer = writer + self.reader = reader + self.type = sock_type + + async def read_bytes(self, num_bytes): + """Read bytes from ZMQ socket.""" + data = b"" + while len(data) < num_bytes: + new_data = await self.reader.read(num_bytes - len(data)) + if len(new_data) == 0: + raise EOFError + data += new_data + return data + + async def write_bytes(self, raw_msg): + """Write bytes to ZMQ socket.""" + self.writer.write(raw_msg) + await self.writer.drain() + + async def handshake(self): + """Do initial greeting handshake on a new ZMQ connection.""" + await self.write_bytes(b"\xff\x00\x00\x00\x00\x00\x00\x00\x01\x7f") + _ = await self.read_bytes(10) + # _LOGGER.debug(f"handshake: got initial greeting {greeting}") + await self.write_bytes(b"\x03") + _ = await self.read_bytes(1) + await self.write_bytes(b"\x00" + "NULL".encode() + b"\x00" * 16 + b"\x00" + b"\x00" * 31) + _ = await self.read_bytes(53) + # _LOGGER.debug(f"handshake: got rest of greeting {greeting}") + params = [["Socket-Type", self.type]] + if self.type == "ROUTER": + params.append(["Identity", ""]) + await self.send_cmd("READY", params) + + async def recv(self, multipart=False): + """Receive a message from ZMQ socket.""" + parts = [] + while 1: + cmd = (await self.read_bytes(1))[0] + if cmd & 0x2: + msg_len = unpack(">Q", await self.read_bytes(8))[0] + else: + msg_len = (await self.read_bytes(1))[0] + msg_body = await self.read_bytes(msg_len) + if cmd & 0x4: + # _LOGGER.debug(f"recv: got cmd {msg_body}") + cmd_len = msg_body[0] + cmd = msg_body[1 : cmd_len + 1] + msg_body = msg_body[cmd_len + 1 :] + params = [] + while len(msg_body) > 0: + param_len = msg_body[0] + param = msg_body[1 : param_len + 1] + msg_body = msg_body[param_len + 1 :] + value_len = unpack(">L", msg_body[0:4])[0] + value = msg_body[4 : 4 + value_len] + msg_body = msg_body[4 + value_len :] + params.append([param, value]) + # _LOGGER.debug(f"recv: got cmd={cmd}, params={params}") + else: + parts.append(msg_body) + if cmd in (0x0, 0x2): + # _LOGGER.debug(f"recv: got msg {parts}") + if not multipart: + return b"".join(parts) + + return parts + + async def recv_multipart(self): + """Receive a multipart message from ZMQ socket.""" + return await self.recv(multipart=True) + + async def send_cmd(self, cmd, params): + """Send a command over ZMQ socket.""" + raw_msg = bytearray([len(cmd)]) + cmd.encode() + for param in params: + raw_msg += bytearray([len(param[0])]) + param[0].encode() + raw_msg += pack(">L", len(param[1])) + param[1].encode() + len_msg = len(raw_msg) + if len_msg <= 255: + raw_msg = bytearray([0x4, len_msg]) + raw_msg + else: + raw_msg = bytearray([0x6]) + pack(">Q", len_msg) + raw_msg + # _LOGGER.debug(f"send_cmd: sending {raw_msg}") + await self.write_bytes(raw_msg) + + async def send(self, msg): + """Send a message over ZMQ socket.""" + len_msg = len(msg) + if len_msg <= 255: + raw_msg = bytearray([0x1, 0x0, 0x0, len_msg]) + msg + else: + raw_msg = bytearray([0x1, 0x0, 0x2]) + pack(">Q", len_msg) + msg + # _LOGGER.debug(f"send: sending {raw_msg}") + await self.write_bytes(raw_msg) + + async def send_multipart(self, parts): + """Send multipart messages over ZMQ socket.""" + raw_msg = b"" + for i, part in enumerate(parts): + len_part = len(part) + cmd = 0x1 if i < len(parts) - 1 else 0x0 + if len_part <= 255: + raw_msg += bytearray([cmd, len_part]) + part + else: + raw_msg += bytearray([cmd + 2]) + pack(">Q", len_part) + part + # _LOGGER.debug(f"send_multipart: sending {raw_msg}") + await self.write_bytes(raw_msg) + + def close(self): + """Close the ZMQ socket.""" + self.writer.close() + + +########################################## +class Kernel: + """Define a Jupyter Kernel class.""" + + def __init__(self, config, ast_ctx, global_ctx, global_ctx_name): + """Initialize a Kernel object, one instance per session.""" + self.config = config.copy() + self.global_ctx = global_ctx + self.global_ctx_name = global_ctx_name + self.ast_ctx = ast_ctx + + self.secure_key = str_to_bytes(self.config["key"]) + self.no_connect_timeout = self.config.get("no_connect_timeout", 30) + self.signature_schemes = {"hmac-sha256": hashlib.sha256} + self.auth = hmac.HMAC( + self.secure_key, + digestmod=self.signature_schemes[self.config["signature_scheme"]], + ) + self.execution_count = 1 + self.engine_id = str(uuid.uuid4()) + + self.heartbeat_server = None + self.iopub_server = None + self.control_server = None + self.stdin_server = None + self.shell_server = None + + self.heartbeat_port = None + self.iopub_port = None + self.control_port = None + self.stdin_port = None + self.shell_port = None + # this should probably be a configuration parameter + self.avail_port = 50321 + + # there can be multiple iopub subscribers, with corresponding tasks + self.iopub_socket = set() + + self.tasks = {} + self.task_cnt = 0 + self.task_cnt_max = 0 + + self.session_cleanup_callback = None + + self.housekeep_q = asyncio.Queue(0) + + self.parent_header = None + + # + # we create a logging handler so that output from the log functions + # gets delivered back to Jupyter as stdout + # + self.console = KernelBufferingHandler(self.housekeep_q) + self.console.setLevel(logging.DEBUG) + # set a format which is just the message + formatter = logging.Formatter("%(message)s") + self.console.setFormatter(formatter) + + # match alphanum or "." at end of line + self.completion_re = re.compile(r".*?([\w.]*)$", re.DOTALL) + + # see if line ends in a ":", with optional whitespace and comment + # note: this doesn't detect if we are inside a quoted string... + self.colon_end_re = re.compile(r".*: *(#.*)?$") + + def msg_sign(self, msg_lst): + """Sign a message with a secure signature.""" + auth_hmac = self.auth.copy() + for msg in msg_lst: + auth_hmac.update(msg) + return str_to_bytes(auth_hmac.hexdigest()) + + def deserialize_wire_msg(self, wire_msg): + """Split the routing prefix and message frames from a message on the wire.""" + delim_idx = wire_msg.index(DELIM) + identities = wire_msg[:delim_idx] + m_signature = wire_msg[delim_idx + 1] + msg_frames = wire_msg[delim_idx + 2 :] + + def decode(msg): + return json.loads(msg.decode("utf-8")) + + msg = {} + msg["header"] = decode(msg_frames[0]) + msg["parent_header"] = decode(msg_frames[1]) + msg["metadata"] = decode(msg_frames[2]) + msg["content"] = decode(msg_frames[3]) + check_sig = self.msg_sign(msg_frames) + if check_sig != m_signature: + _LOGGER.error( + "signature mismatch: check_sig=%s, m_signature=%s, wire_msg=%s", + check_sig, + m_signature, + wire_msg, + ) + raise ValueError("Signatures do not match") + + return identities, msg + + def new_header(self, msg_type): + """Make a new header.""" + return { + "date": datetime.datetime.now().isoformat(), + "msg_id": msg_id(), + "username": "kernel", + "session": self.engine_id, + "msg_type": msg_type, + "version": "5.3", + } + + async def send( + self, + stream, + msg_type, + content=None, + parent_header=None, + metadata=None, + identities=None, + ): + """Send message to the Jupyter client.""" + header = self.new_header(msg_type) + + def encode(msg): + return str_to_bytes(json.dumps(msg)) + + msg_lst = [ + encode(header), + encode(parent_header if parent_header else {}), + encode(metadata if metadata else {}), + encode(content if content else {}), + ] + signature = self.msg_sign(msg_lst) + parts = [DELIM, signature, msg_lst[0], msg_lst[1], msg_lst[2], msg_lst[3]] + if identities: + parts = identities + parts + if stream: + # _LOGGER.debug("send %s: %s", msg_type, parts) + for this_stream in stream if isinstance(stream, set) else {stream}: + await this_stream.send_multipart(parts) + + async def shell_handler(self, shell_socket, wire_msg): + """Handle shell messages.""" + + identities, msg = self.deserialize_wire_msg(wire_msg) + # _LOGGER.debug("shell received %s: %s", msg.get('header', {}).get('msg_type', 'UNKNOWN'), msg) + self.parent_header = msg["header"] + + content = { + "execution_state": "busy", + } + await self.send(self.iopub_socket, "status", content, parent_header=msg["header"]) + + if msg["header"]["msg_type"] == "execute_request": + + content = { + "execution_count": self.execution_count, + "code": msg["content"]["code"], + } + await self.send(self.iopub_socket, "execute_input", content, parent_header=msg["header"]) + result = None + + code = msg["content"]["code"] + # + # replace VSCode initialization code, which depend on iPython % extensions + # + if code.startswith("%config "): + code = "None" + if code.startswith("_rwho_ls = %who_ls"): + code = "print([])" + + self.global_ctx.set_auto_start(False) + self.ast_ctx.parse(code) + exc = self.ast_ctx.get_exception_obj() + if exc is None: + result = await self.ast_ctx.eval() + exc = self.ast_ctx.get_exception_obj() + await Function.waiter_sync() + self.global_ctx.set_auto_start(True) + self.global_ctx.start() + if exc: + traceback_mesg = self.ast_ctx.get_exception_long().split("\n") + + metadata = { + "dependencies_met": True, + "engine": self.engine_id, + "status": "error", + "started": datetime.datetime.now().isoformat(), + } + content = { + "execution_count": self.execution_count, + "status": "error", + "ename": type(exc).__name__, # Exception name, as a string + "evalue": str(exc), # Exception value, as a string + "traceback": traceback_mesg, + } + _LOGGER.debug("Executing '%s' got exception: %s", code, content) + await self.send( + shell_socket, + "execute_reply", + content, + metadata=metadata, + parent_header=msg["header"], + identities=identities, + ) + del content["execution_count"], content["status"] + await self.send(self.iopub_socket, "error", content, parent_header=msg["header"]) + + content = { + "execution_state": "idle", + } + await self.send(self.iopub_socket, "status", content, parent_header=msg["header"]) + if msg["content"].get("store_history", True): + self.execution_count += 1 + return + + # if True or isinstance(self.ast_ctx.ast, ast.Expr): + _LOGGER.debug("Executing: '%s' got result %s", code, result) + if result is not None: + content = { + "execution_count": self.execution_count, + "data": {"text/plain": repr(result)}, + "metadata": {}, + } + await self.send( + self.iopub_socket, + "execute_result", + content, + parent_header=msg["header"], + ) + + metadata = { + "dependencies_met": True, + "engine": self.engine_id, + "status": "ok", + "started": datetime.datetime.now().isoformat(), + } + content = { + "status": "ok", + "execution_count": self.execution_count, + "user_variables": {}, + "payload": [], + "user_expressions": {}, + } + await self.send( + shell_socket, + "execute_reply", + content, + metadata=metadata, + parent_header=msg["header"], + identities=identities, + ) + if msg["content"].get("store_history", True): + self.execution_count += 1 + + # + # Make sure stdout gets sent before set report execution_state idle on iopub, + # otherwise VSCode doesn't display stdout. We do a handshake with the + # housekeep task to ensure any queued messages get processed. + # + handshake_q = asyncio.Queue(0) + await self.housekeep_q.put(["handshake", handshake_q, 0]) + await handshake_q.get() + + elif msg["header"]["msg_type"] == "kernel_info_request": + content = { + "protocol_version": "5.3", + "ipython_version": [1, 1, 0, ""], + "language_version": [0, 0, 1], + "language": "python", + "implementation": "python", + "implementation_version": "3.7", + "language_info": { + "name": "python", + "version": "1.0", + "mimetype": "", + "file_extension": ".py", + "codemirror_mode": "", + "nbconvert_exporter": "", + }, + "banner": "", + } + await self.send( + shell_socket, + "kernel_info_reply", + content, + parent_header=msg["header"], + identities=identities, + ) + + elif msg["header"]["msg_type"] == "complete_request": + root = "" + words = set() + code = msg["content"]["code"] + posn = msg["content"]["cursor_pos"] + match = self.completion_re.match(code[0:posn].lower()) + if match: + root = match[1].lower() + words = State.completions(root) + words = words.union(await Function.service_completions(root)) + words = words.union(await Function.func_completions(root)) + words = words.union(self.ast_ctx.completions(root)) + # _LOGGER.debug(f"complete_request code={code}, posn={posn}, root={root}, words={words}") + content = { + "status": "ok", + "matches": sorted(list(words)), + "cursor_start": msg["content"]["cursor_pos"] - len(root), + "cursor_end": msg["content"]["cursor_pos"], + "metadata": {}, + } + await self.send( + shell_socket, + "complete_reply", + content, + parent_header=msg["header"], + identities=identities, + ) + + elif msg["header"]["msg_type"] == "is_complete_request": + code = msg["content"]["code"] + self.ast_ctx.parse(code) + exc = self.ast_ctx.get_exception_obj() + + # determine indent of last line + indent = 0 + i = code.rfind("\n") + if i >= 0: + while i + 1 < len(code) and code[i + 1] == " ": + i += 1 + indent += 1 + if exc is None: + if indent == 0: + content = { + # One of 'complete', 'incomplete', 'invalid', 'unknown' + "status": "complete", + # If status is 'incomplete', indent should contain the characters to use + # to indent the next line. This is only a hint: frontends may ignore it + # and use their own autoindentation rules. For other statuses, this + # field does not exist. + # "indent": str, + } + else: + content = { + "status": "incomplete", + "indent": " " * indent, + } + else: + # + # if the syntax error is right at the end, then we label it incomplete, + # otherwise it's invalid + # + if "EOF while" in str(exc) or "expected an indented block" in str(exc): + # if error is at ":" then increase indent + if hasattr(exc, "lineno"): + line = code.split("\n")[exc.lineno - 1] + if self.colon_end_re.match(line): + indent += 4 + content = { + "status": "incomplete", + "indent": " " * indent, + } + else: + content = { + "status": "invalid", + } + # _LOGGER.debug(f"is_complete_request code={code}, exc={exc}, content={content}") + await self.send( + shell_socket, + "is_complete_reply", + content, + parent_header=msg["header"], + identities=identities, + ) + + elif msg["header"]["msg_type"] == "comm_info_request": + content = {"comms": {}} + await self.send( + shell_socket, + "comm_info_reply", + content, + parent_header=msg["header"], + identities=identities, + ) + + elif msg["header"]["msg_type"] == "history_request": + content = {"history": []} + await self.send( + shell_socket, + "history_reply", + content, + parent_header=msg["header"], + identities=identities, + ) + + elif msg["header"]["msg_type"] in {"comm_open", "comm_msg", "comm_close"}: + # _LOGGER.debug(f"ignore {msg['header']['msg_type']} message ") + ... + else: + _LOGGER.error("unknown msg_type: %s", msg["header"]["msg_type"]) + + content = { + "execution_state": "idle", + } + await self.send(self.iopub_socket, "status", content, parent_header=msg["header"]) + + async def control_listen(self, reader, writer): + """Task that listens to control messages.""" + try: + _LOGGER.debug("control_listen connected") + await self.housekeep_q.put(["register", "control", asyncio.current_task()]) + control_socket = ZmqSocket(reader, writer, "ROUTER") + await control_socket.handshake() + while 1: + wire_msg = await control_socket.recv_multipart() + identities, msg = self.deserialize_wire_msg(wire_msg) + # _LOGGER.debug("control received %s: %s", msg.get('header', {}).get('msg_type', 'UNKNOWN'), msg) + if msg["header"]["msg_type"] == "shutdown_request": + content = { + "restart": False, + } + await self.send( + control_socket, + "shutdown_reply", + content, + parent_header=msg["header"], + identities=identities, + ) + await self.housekeep_q.put(["shutdown"]) + except asyncio.CancelledError: + raise + except (EOFError, ConnectionResetError): + _LOGGER.debug("control_listen got eof") + await self.housekeep_q.put(["unregister", "control", asyncio.current_task()]) + control_socket.close() + except Exception as err: + _LOGGER.error("control_listen exception %s", err) + await self.housekeep_q.put(["shutdown"]) + + async def stdin_listen(self, reader, writer): + """Task that listens to stdin messages.""" + try: + _LOGGER.debug("stdin_listen connected") + await self.housekeep_q.put(["register", "stdin", asyncio.current_task()]) + stdin_socket = ZmqSocket(reader, writer, "ROUTER") + await stdin_socket.handshake() + while 1: + _ = await stdin_socket.recv_multipart() + # _LOGGER.debug("stdin_listen received %s", _) + except asyncio.CancelledError: + raise + except (EOFError, ConnectionResetError): + _LOGGER.debug("stdin_listen got eof") + await self.housekeep_q.put(["unregister", "stdin", asyncio.current_task()]) + stdin_socket.close() + except Exception: + _LOGGER.error("stdin_listen exception %s", traceback.format_exc(-1)) + await self.housekeep_q.put(["shutdown"]) + + async def shell_listen(self, reader, writer): + """Task that listens to shell messages.""" + try: + _LOGGER.debug("shell_listen connected") + await self.housekeep_q.put(["register", "shell", asyncio.current_task()]) + shell_socket = ZmqSocket(reader, writer, "ROUTER") + await shell_socket.handshake() + while 1: + msg = await shell_socket.recv_multipart() + await self.shell_handler(shell_socket, msg) + except asyncio.CancelledError: + shell_socket.close() + raise + except (EOFError, ConnectionResetError): + _LOGGER.debug("shell_listen got eof") + await self.housekeep_q.put(["unregister", "shell", asyncio.current_task()]) + shell_socket.close() + except Exception: + _LOGGER.error("shell_listen exception %s", traceback.format_exc(-1)) + await self.housekeep_q.put(["shutdown"]) + + async def heartbeat_listen(self, reader, writer): + """Task that listens and responds to heart beat messages.""" + try: + _LOGGER.debug("heartbeat_listen connected") + await self.housekeep_q.put(["register", "heartbeat", asyncio.current_task()]) + heartbeat_socket = ZmqSocket(reader, writer, "REP") + await heartbeat_socket.handshake() + while 1: + msg = await heartbeat_socket.recv() + # _LOGGER.debug("heartbeat_listen: got %s", msg) + await heartbeat_socket.send(msg) + except asyncio.CancelledError: + raise + except (EOFError, ConnectionResetError): + _LOGGER.debug("heartbeat_listen got eof") + await self.housekeep_q.put(["unregister", "heartbeat", asyncio.current_task()]) + heartbeat_socket.close() + except Exception: + _LOGGER.error("heartbeat_listen exception: %s", traceback.format_exc(-1)) + await self.housekeep_q.put(["shutdown"]) + + async def iopub_listen(self, reader, writer): + """Task that listens to iopub messages.""" + try: + _LOGGER.debug("iopub_listen connected") + await self.housekeep_q.put(["register", "iopub", asyncio.current_task()]) + iopub_socket = ZmqSocket(reader, writer, "PUB") + await iopub_socket.handshake() + self.iopub_socket.add(iopub_socket) + while 1: + _ = await iopub_socket.recv_multipart() + # _LOGGER.debug("iopub received %s", _) + except asyncio.CancelledError: + raise + except (EOFError, ConnectionResetError): + await self.housekeep_q.put(["unregister", "iopub", asyncio.current_task()]) + iopub_socket.close() + self.iopub_socket.discard(iopub_socket) + _LOGGER.debug("iopub_listen got eof") + except Exception: + _LOGGER.error("iopub_listen exception %s", traceback.format_exc(-1)) + await self.housekeep_q.put(["shutdown"]) + + async def housekeep_run(self): + """Housekeeping, including closing servers after startup, and doing orderly shutdown.""" + while True: + try: + msg = await self.housekeep_q.get() + if msg[0] == "stdout": + content = {"name": "stdout", "text": msg[1] + "\n"} + if self.iopub_socket: + await self.send( + self.iopub_socket, + "stream", + content, + parent_header=self.parent_header, + identities=[b"stream.stdout"], + ) + elif msg[0] == "handshake": + await msg[1].put(msg[2]) + elif msg[0] == "register": + if msg[1] not in self.tasks: + self.tasks[msg[1]] = set() + self.tasks[msg[1]].add(msg[2]) + self.task_cnt += 1 + self.task_cnt_max = max(self.task_cnt_max, self.task_cnt) + # + # now a couple of things are connected, call the session_cleanup_callback + # + if self.task_cnt > 1 and self.session_cleanup_callback: + self.session_cleanup_callback() + self.session_cleanup_callback = None + elif msg[0] == "unregister": + if msg[1] in self.tasks: + self.tasks[msg[1]].discard(msg[2]) + self.task_cnt -= 1 + # + # if there are no connection tasks left, then shutdown the kernel + # + if self.task_cnt == 0 and self.task_cnt_max >= 4: + asyncio.create_task(self.session_shutdown()) + await asyncio.sleep(10000) + elif msg[0] == "shutdown": + asyncio.create_task(self.session_shutdown()) + return + except asyncio.CancelledError: + raise + except Exception: + _LOGGER.error("housekeep task exception: %s", traceback.format_exc(-1)) + + async def startup_timeout(self): + """Shut down the session if nothing connects after 30 seconds.""" + await self.housekeep_q.put(["register", "startup_timeout", asyncio.current_task()]) + await asyncio.sleep(self.no_connect_timeout) + if self.task_cnt_max <= 1: + # + # nothing started other than us, so shut down the session + # + _LOGGER.error("No connections to session %s; shutting down", self.global_ctx_name) + if self.session_cleanup_callback: + self.session_cleanup_callback() + self.session_cleanup_callback = None + await self.housekeep_q.put(["shutdown"]) + await self.housekeep_q.put(["unregister", "startup_timeout", asyncio.current_task()]) + + async def start_one_server(self, callback): + """Start a server by finding an available port.""" + first_port = self.avail_port + for _ in range(2048): + try: + server = await asyncio.start_server(callback, "0.0.0.0", self.avail_port) + return server, self.avail_port + except OSError: + self.avail_port += 1 + _LOGGER.error( + "unable to find an available port from %d to %d", + first_port, + self.avail_port - 1, + ) + return None, None + + def get_ports(self): + """Return a dict of the port numbers this kernel session is listening to.""" + return { + "iopub_port": self.iopub_port, + "hb_port": self.heartbeat_port, + "control_port": self.control_port, + "stdin_port": self.stdin_port, + "shell_port": self.shell_port, + } + + def set_session_cleanup_callback(self, callback): + """Set a cleanup callback which is called right after the session has started.""" + self.session_cleanup_callback = callback + + async def session_start(self): + """Start the kernel session.""" + self.ast_ctx.add_logger_handler(self.console) + _LOGGER.info("Starting session %s", self.global_ctx_name) + + self.tasks["housekeep"] = {asyncio.create_task(self.housekeep_run())} + self.tasks["startup_timeout"] = {asyncio.create_task(self.startup_timeout())} + + self.iopub_server, self.iopub_port = await self.start_one_server(self.iopub_listen) + self.heartbeat_server, self.heartbeat_port = await self.start_one_server(self.heartbeat_listen) + self.control_server, self.control_port = await self.start_one_server(self.control_listen) + self.stdin_server, self.stdin_port = await self.start_one_server(self.stdin_listen) + self.shell_server, self.shell_port = await self.start_one_server(self.shell_listen) + + # + # For debugging, can use the real ZMQ library instead on certain sockets; comment out + # the corresponding asyncio.start_server() call above if you enable the ZMQ-based + # functions here. You can then turn of verbosity level 4 (-vvvv) in hass_pyscript_kernel.py + # to see all the byte data in case you need to debug the simple ZMQ implementation here. + # The two most important zmq functions are shown below. + # + # import zmq + # import zmq.asyncio + # + # def zmq_bind(socket, connection, port): + # """Bind a socket.""" + # if port <= 0: + # return socket.bind_to_random_port(connection) + # # _LOGGER.debug(f"binding to %s:%s" % (connection, port)) + # socket.bind("%s:%s" % (connection, port)) + # return port + # + # zmq_ctx = zmq.asyncio.Context() + # + # ########################################## + # # Shell using real ZMQ for debugging: + # async def shell_listen_zmq(): + # """Task that listens to shell messages using ZMQ.""" + # try: + # _LOGGER.debug("shell_listen_zmq connected") + # connection = self.config["transport"] + "://" + self.config["ip"] + # shell_socket = zmq_ctx.socket(zmq.ROUTER) + # self.shell_port = zmq_bind(shell_socket, connection, -1) + # _LOGGER.debug("shell_listen_zmq connected") + # while 1: + # msg = await shell_socket.recv_multipart() + # await self.shell_handler(shell_socket, msg) + # except asyncio.CancelledError: + # raise + # except Exception: + # _LOGGER.error("shell_listen exception %s", traceback.format_exc(-1)) + # await self.housekeep_q.put(["shutdown"]) + # + # ########################################## + # # IOPub using real ZMQ for debugging: + # # IOPub/Sub: + # async def iopub_listen_zmq(): + # """Task that listens to iopub messages using ZMQ.""" + # try: + # _LOGGER.debug("iopub_listen_zmq connected") + # connection = self.config["transport"] + "://" + self.config["ip"] + # iopub_socket = zmq_ctx.socket(zmq.PUB) + # self.iopub_port = zmq_bind(self.iopub_socket, connection, -1) + # self.iopub_socket.add(iopub_socket) + # while 1: + # wire_msg = await iopub_socket.recv_multipart() + # _LOGGER.debug("iopub received %s", wire_msg) + # except asyncio.CancelledError: + # raise + # except EOFError: + # await self.housekeep_q.put(["shutdown"]) + # _LOGGER.debug("iopub_listen got eof") + # except Exception as err: + # _LOGGER.error("iopub_listen exception %s", err) + # await self.housekeep_q.put(["shutdown"]) + # + # self.tasks["shell"] = {asyncio.create_task(shell_listen_zmq())} + # self.tasks["iopub"] = {asyncio.create_task(iopub_listen_zmq())} + # + + async def session_shutdown(self): + """Shutdown the kernel session.""" + if not self.iopub_server: + # already shutdown, so quit + return + GlobalContextMgr.delete(self.global_ctx_name) + self.ast_ctx.remove_logger_handler(self.console) + # logging.getLogger("homeassistant.components.pyscript.func.").removeHandler(self.console) + _LOGGER.info("Shutting down session %s", self.global_ctx_name) + + for server in [ + self.heartbeat_server, + self.control_server, + self.stdin_server, + self.shell_server, + self.iopub_server, + ]: + if server: + server.close() + self.heartbeat_server = None + self.iopub_server = None + self.control_server = None + self.stdin_server = None + self.shell_server = None + + for task_set in self.tasks.values(): + for task in task_set: + try: + task.cancel() + await task + except asyncio.CancelledError: + pass + self.tasks = [] + + for sock in self.iopub_socket: + try: + sock.close() + except Exception as err: + _LOGGER.error("iopub socket close exception: %s", err) + + self.iopub_socket = set() diff --git a/config/custom_components/pyscript/logbook.py b/config/custom_components/pyscript/logbook.py new file mode 100644 index 0000000..f09c846 --- /dev/null +++ b/config/custom_components/pyscript/logbook.py @@ -0,0 +1,45 @@ +"""Describe logbook events.""" +import logging + +from homeassistant.core import callback + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + + +@callback +def async_describe_events(hass, async_describe_event): # type: ignore + """Describe logbook events.""" + + @callback + def async_describe_logbook_event(event): # type: ignore + """Describe a logbook event.""" + data = event.data + func_args = data.get("func_args", {}) + ev_name = data.get("name", "unknown") + ev_entity_id = data.get("entity_id", "pyscript.unknown") + + ev_trigger_type = func_args.get("trigger_type", "unknown") + if ev_trigger_type == "event": + ev_source = f"event {func_args.get('event_type', 'unknown event')}" + elif ev_trigger_type == "state": + ev_source = f"state change {func_args.get('var_name', 'unknown entity')} == {func_args.get('value', 'unknown value')}" + elif ev_trigger_type == "time": + ev_trigger_time = func_args.get("trigger_time", "unknown") + if ev_trigger_time is None: + ev_trigger_time = "startup" + ev_source = f"time {ev_trigger_time}" + else: + ev_source = ev_trigger_type + + message = f"has been triggered by {ev_source}" + + return { + "name": ev_name, + "message": message, + "source": ev_source, + "entity_id": ev_entity_id, + } + + async_describe_event(DOMAIN, "pyscript_running", async_describe_logbook_event) diff --git a/config/custom_components/pyscript/manifest.json b/config/custom_components/pyscript/manifest.json new file mode 100644 index 0000000..7ff4d13 --- /dev/null +++ b/config/custom_components/pyscript/manifest.json @@ -0,0 +1,17 @@ +{ + "domain": "pyscript", + "name": "Pyscript Python scripting", + "codeowners": [ + "@craigbarratt" + ], + "config_flow": true, + "dependencies": [], + "documentation": "https://github.com/custom-components/pyscript", + "homekit": {}, + "iot_class": "local_push", + "issue_tracker": "https://github.com/custom-components/pyscript/issues", + "requirements": ["croniter==2.0.2", "watchdog==2.3.1"], + "ssdp": [], + "version": "1.6.1", + "zeroconf": [] +} diff --git a/config/custom_components/pyscript/mqtt.py b/config/custom_components/pyscript/mqtt.py new file mode 100644 index 0000000..25d72a1 --- /dev/null +++ b/config/custom_components/pyscript/mqtt.py @@ -0,0 +1,91 @@ +"""Handles mqtt messages and notification.""" + +import json +import logging + +from homeassistant.components import mqtt + +from .const import LOGGER_PATH + +_LOGGER = logging.getLogger(LOGGER_PATH + ".mqtt") + + +class Mqtt: + """Define mqtt functions.""" + + # + # Global hass instance + # + hass = None + + # + # notify message queues by mqtt message topic + # + notify = {} + notify_remove = {} + + def __init__(self): + """Warn on Mqtt instantiation.""" + _LOGGER.error("Mqtt class is not meant to be instantiated") + + @classmethod + def init(cls, hass): + """Initialize Mqtt.""" + + cls.hass = hass + + @classmethod + def mqtt_message_handler_maker(cls, subscribed_topic): + """Closure for mqtt_message_handler.""" + + async def mqtt_message_handler(mqttmsg): + """Listen for MQTT messages.""" + func_args = { + "trigger_type": "mqtt", + "topic": mqttmsg.topic, + "payload": mqttmsg.payload, + "qos": mqttmsg.qos, + } + + try: + func_args["payload_obj"] = json.loads(mqttmsg.payload) + except ValueError: + pass + + await cls.update(subscribed_topic, func_args) + + return mqtt_message_handler + + @classmethod + async def notify_add(cls, topic, queue): + """Register to notify for mqtt messages of given topic to be sent to queue.""" + + if topic not in cls.notify: + cls.notify[topic] = set() + _LOGGER.debug("mqtt.notify_add(%s) -> adding mqtt subscription", topic) + cls.notify_remove[topic] = await mqtt.async_subscribe( + cls.hass, topic, cls.mqtt_message_handler_maker(topic), encoding="utf-8", qos=0 + ) + cls.notify[topic].add(queue) + + @classmethod + def notify_del(cls, topic, queue): + """Unregister to notify for mqtt messages of given topic for given queue.""" + + if topic not in cls.notify or queue not in cls.notify[topic]: + return + cls.notify[topic].discard(queue) + if len(cls.notify[topic]) == 0: + cls.notify_remove[topic]() + _LOGGER.debug("mqtt.notify_del(%s) -> removing mqtt subscription", topic) + del cls.notify[topic] + del cls.notify_remove[topic] + + @classmethod + async def update(cls, topic, func_args): + """Deliver all notifications for an mqtt message on the given topic.""" + + _LOGGER.debug("mqtt.update(%s, %s, %s)", topic, vars, func_args) + if topic in cls.notify: + for queue in cls.notify[topic]: + await queue.put(["mqtt", func_args.copy()]) diff --git a/config/custom_components/pyscript/requirements.py b/config/custom_components/pyscript/requirements.py new file mode 100644 index 0000000..1f6377d --- /dev/null +++ b/config/custom_components/pyscript/requirements.py @@ -0,0 +1,323 @@ +"""Requirements helpers for pyscript.""" +import glob +import logging +import os +import sys + +from homeassistant.loader import bind_hass +from homeassistant.requirements import async_process_requirements + +from .const import ( + ATTR_INSTALLED_VERSION, + ATTR_SOURCES, + ATTR_VERSION, + CONF_ALLOW_ALL_IMPORTS, + CONF_INSTALLED_PACKAGES, + DOMAIN, + LOGGER_PATH, + REQUIREMENTS_FILE, + REQUIREMENTS_PATHS, + UNPINNED_VERSION, +) + +if sys.version_info[:2] >= (3, 8): + from importlib.metadata import ( # pylint: disable=no-name-in-module,import-error + PackageNotFoundError, + version as installed_version, + ) +else: + from importlib_metadata import ( # pylint: disable=import-error + PackageNotFoundError, + version as installed_version, + ) + +_LOGGER = logging.getLogger(LOGGER_PATH) + + +def get_installed_version(pkg_name): + """Get installed version of package. Returns None if not found.""" + try: + return installed_version(pkg_name) + except PackageNotFoundError: + return None + + +def update_unpinned_versions(package_dict): + """Check for current installed version of each unpinned package.""" + requirements_to_pop = [] + for package in package_dict: + if package_dict[package] != UNPINNED_VERSION: + continue + + package_dict[package] = get_installed_version(package) + if not package_dict[package]: + _LOGGER.error("%s wasn't able to be installed", package) + requirements_to_pop.append(package) + + for package in requirements_to_pop: + package_dict.pop(package) + + return package_dict + + +@bind_hass +def process_all_requirements(pyscript_folder, requirements_paths, requirements_file): + """ + Load all lines from requirements_file located in requirements_paths. + + Returns files and a list of packages, if any, that need to be installed. + """ + + # Re-import Version to avoid dealing with multiple flake and pylint errors + from packaging.version import Version # pylint: disable=import-outside-toplevel + + all_requirements_to_process = {} + for root in requirements_paths: + for requirements_path in glob.glob(os.path.join(pyscript_folder, root, requirements_file)): + with open(requirements_path, "r", encoding="utf-8") as requirements_fp: + all_requirements_to_process[requirements_path] = requirements_fp.readlines() + + all_requirements_to_install = {} + for requirements_path, pkg_lines in all_requirements_to_process.items(): + for pkg in pkg_lines: + # Remove inline comments which are accepted by pip but not by Home + # Assistant's installation method. + # https://rosettacode.org/wiki/Strip_comments_from_a_string#Python + i = pkg.find("#") + if i >= 0: + pkg = pkg[:i] + pkg = pkg.strip() + + if not pkg or len(pkg) == 0: + continue + + try: + # Attempt to get version of package. Do nothing if it's found since + # we want to use the version that's already installed to be safe + parts = pkg.split("==") + if len(parts) > 2 or "," in pkg or ">" in pkg or "<" in pkg: + _LOGGER.error( + ( + "Ignoring invalid requirement '%s' specified in '%s'; if a specific version" + "is required, the requirement must use the format 'pkg==version'" + ), + requirements_path, + pkg, + ) + continue + if len(parts) == 1: + new_version = UNPINNED_VERSION + else: + new_version = parts[1] + pkg_name = parts[0] + + current_pinned_version = all_requirements_to_install.get(pkg_name, {}).get(ATTR_VERSION) + current_sources = all_requirements_to_install.get(pkg_name, {}).get(ATTR_SOURCES, []) + # If a version hasn't already been recorded, record this one + if not current_pinned_version: + all_requirements_to_install[pkg_name] = { + ATTR_VERSION: new_version, + ATTR_SOURCES: [requirements_path], + ATTR_INSTALLED_VERSION: get_installed_version(pkg_name), + } + + # If the new version is unpinned and there is an existing pinned version, use existing + # pinned version + elif new_version == UNPINNED_VERSION and current_pinned_version != UNPINNED_VERSION: + _LOGGER.warning( + ( + "Unpinned requirement for package '%s' detected in '%s' will be ignored in " + "favor of the pinned version '%s' detected in '%s'" + ), + pkg_name, + requirements_path, + current_pinned_version, + str(current_sources), + ) + # If the new version is pinned and the existing version is unpinned, use the new pinned + # version + elif new_version != UNPINNED_VERSION and current_pinned_version == UNPINNED_VERSION: + _LOGGER.warning( + ( + "Unpinned requirement for package '%s' detected in '%s will be ignored in " + "favor of the pinned version '%s' detected in '%s'" + ), + pkg_name, + str(current_sources), + new_version, + requirements_path, + ) + all_requirements_to_install[pkg_name] = { + ATTR_VERSION: new_version, + ATTR_SOURCES: [requirements_path], + ATTR_INSTALLED_VERSION: get_installed_version(pkg_name), + } + # If the already recorded version is the same as the new version, append the current + # path so we can show sources + elif ( + new_version == UNPINNED_VERSION and current_pinned_version == UNPINNED_VERSION + ) or Version(current_pinned_version) == Version(new_version): + all_requirements_to_install[pkg_name][ATTR_SOURCES].append(requirements_path) + # If the already recorded version is lower than the new version, use the new one + elif Version(current_pinned_version) < Version(new_version): + _LOGGER.warning( + ( + "Version '%s' for package '%s' detected in '%s' will be ignored in " + "favor of the higher version '%s' detected in '%s'" + ), + current_pinned_version, + pkg_name, + str(current_sources), + new_version, + requirements_path, + ) + all_requirements_to_install[pkg_name].update( + {ATTR_VERSION: new_version, ATTR_SOURCES: [requirements_path]} + ) + # If the already recorded version is higher than the new version, ignore the new one + elif Version(current_pinned_version) > Version(new_version): + _LOGGER.warning( + ( + "Version '%s' for package '%s' detected in '%s' will be ignored in " + "favor of the higher version '%s' detected in '%s'" + ), + new_version, + pkg_name, + requirements_path, + current_pinned_version, + str(current_sources), + ) + except ValueError: + # Not valid requirements line so it can be skipped + _LOGGER.debug("Ignoring '%s' because it is not a valid package", pkg) + + return all_requirements_to_install + + +@bind_hass +async def install_requirements(hass, config_entry, pyscript_folder): + """Install missing requirements from requirements.txt.""" + + pyscript_installed_packages = config_entry.data.get(CONF_INSTALLED_PACKAGES, {}).copy() + + # Import packaging inside install_requirements so that we can use Home Assistant to install it + # if it can't been found + try: + from packaging.version import Version # pylint: disable=import-outside-toplevel + except ModuleNotFoundError: + await async_process_requirements(hass, DOMAIN, ["packaging"]) + from packaging.version import Version # pylint: disable=import-outside-toplevel + + all_requirements = await hass.async_add_executor_job( + process_all_requirements, pyscript_folder, REQUIREMENTS_PATHS, REQUIREMENTS_FILE + ) + + requirements_to_install = {} + + if all_requirements and not config_entry.data.get(CONF_ALLOW_ALL_IMPORTS, False): + _LOGGER.error( + ( + "Requirements detected but 'allow_all_imports' is set to False, set " + "'allow_all_imports' to True if you want packages to be installed" + ) + ) + return + + for package in all_requirements: + pkg_installed_version = all_requirements[package].get(ATTR_INSTALLED_VERSION) + version_to_install = all_requirements[package][ATTR_VERSION] + sources = all_requirements[package][ATTR_SOURCES] + # If package is already installed, we need to run some checks + if pkg_installed_version: + # If the version to install is unpinned and there is already something installed, + # defer to what is installed + if version_to_install == UNPINNED_VERSION: + _LOGGER.debug( + ( + "Skipping unpinned version of package '%s' because version '%s' is " + "already installed" + ), + package, + pkg_installed_version, + ) + # If installed package is not the same version as the one we last installed, + # that means that the package is externally managed now so we shouldn't touch it + # and should remove it from our internal tracker + if ( + package in pyscript_installed_packages + and pyscript_installed_packages[package] != pkg_installed_version + ): + pyscript_installed_packages.pop(package) + continue + + # If installed package is not the same version as the one we last installed, + # that means that the package is externally managed now so we shouldn't touch it + # and should remove it from our internal tracker + if package in pyscript_installed_packages and Version( + pyscript_installed_packages[package] + ) != Version(pkg_installed_version): + _LOGGER.warning( + ( + "Version '%s' for package '%s' detected in '%s' will be ignored in favor of" + " the version '%s' which was installed outside of pyscript" + ), + version_to_install, + package, + str(sources), + pkg_installed_version, + ) + pyscript_installed_packages.pop(package) + # If there is a version mismatch between what we want and what is installed, we + # can overwrite it since we know it was last installed by us + elif package in pyscript_installed_packages and Version(version_to_install) != Version( + pkg_installed_version + ): + requirements_to_install[package] = all_requirements[package] + # If there is an installed version that we have not previously installed, we + # should not install it + else: + _LOGGER.debug( + ( + "Version '%s' for package '%s' detected in '%s' will be ignored because it" + " is already installed" + ), + version_to_install, + package, + str(sources), + ) + # Anything not already installed in the environment can be installed + else: + requirements_to_install[package] = all_requirements[package] + + if requirements_to_install: + _LOGGER.info( + "Installing the following packages: %s", + str(requirements_to_install), + ) + await async_process_requirements( + hass, + DOMAIN, + [ + f"{package}=={pkg_info[ATTR_VERSION]}" + if pkg_info[ATTR_VERSION] != UNPINNED_VERSION + else package + for package, pkg_info in requirements_to_install.items() + ], + ) + else: + _LOGGER.debug("No new packages to install") + + # Update package tracker in config entry for next time + pyscript_installed_packages.update( + {package: pkg_info[ATTR_VERSION] for package, pkg_info in requirements_to_install.items()} + ) + + # If any requirements were unpinned, get their version now so they can be pinned later + if any(version == UNPINNED_VERSION for version in pyscript_installed_packages.values()): + pyscript_installed_packages = await hass.async_add_executor_job( + update_unpinned_versions, pyscript_installed_packages + ) + if pyscript_installed_packages != config_entry.data.get(CONF_INSTALLED_PACKAGES, {}): + new_data = config_entry.data.copy() + new_data[CONF_INSTALLED_PACKAGES] = pyscript_installed_packages + hass.config_entries.async_update_entry(entry=config_entry, data=new_data) diff --git a/config/custom_components/pyscript/services.yaml b/config/custom_components/pyscript/services.yaml new file mode 100644 index 0000000..0cf9a9f --- /dev/null +++ b/config/custom_components/pyscript/services.yaml @@ -0,0 +1,107 @@ +# Describes the format for available pyscript services + +reload: + name: Reload pyscript + description: Reloads all available pyscripts and restart triggers + fields: + global_ctx: + name: Global Context + description: Only reload this specific global context (file or app) + example: file.example + required: false + selector: + text: + +jupyter_kernel_start: + name: Start Jupyter kernel + description: Starts a jupyter kernel for interactive use; Called by Jupyter front end and should generally not be used by users + fields: + shell_port: + name: Shell Port Number + description: Shell port number + example: 63599 + required: false + selector: + number: + min: 10240 + max: 65535 + iopub_port: + name: IOPub Port Number + description: IOPub port number + example: 63598 + required: false + selector: + number: + min: 10240 + max: 65535 + stdin_port: + name: Stdin Port Number + description: Stdin port number + example: 63597 + required: false + selector: + number: + min: 10240 + max: 65535 + control_port: + name: Control Port Number + description: Control port number + example: 63596 + required: false + selector: + number: + min: 10240 + max: 65535 + hb_port: + name: Heartbeat Port Number + description: Heartbeat port number + example: 63595 + required: false + selector: + number: + min: 10240 + max: 65535 + ip: + name: IP Address + description: IP address to connect to Jupyter front end + example: 127.0.0.1 + default: 127.0.0.1 + required: false + selector: + text: + key: + name: Security Key + description: Used for signing + example: 012345678-9abcdef023456789abcdef + required: true + selector: + text: + transport: + name: Transport Type + description: Transport type + example: tcp + default: tcp + required: false + selector: + select: + options: + - tcp + - udp + signature_scheme: + name: Signing Algorithm + description: Signing algorithm + example: hmac-sha256 + required: false + default: hmac-sha256 + selector: + select: + options: + - hmac-sha256 + kernel_name: + name: Name of Kernel + description: Kernel name + example: pyscript + required: true + default: pyscript + selector: + text: diff --git a/config/custom_components/pyscript/state.py b/config/custom_components/pyscript/state.py new file mode 100644 index 0000000..c057936 --- /dev/null +++ b/config/custom_components/pyscript/state.py @@ -0,0 +1,438 @@ +"""Handles state variable access and change notification.""" + +import asyncio +import logging + +from homeassistant.core import Context +from homeassistant.helpers.restore_state import DATA_RESTORE_STATE +from homeassistant.helpers.service import async_get_all_descriptions + +from .const import LOGGER_PATH +from .entity import PyscriptEntity +from .function import Function + +_LOGGER = logging.getLogger(LOGGER_PATH + ".state") + +STATE_VIRTUAL_ATTRS = {"entity_id", "last_changed", "last_updated"} + + +class StateVal(str): + """Class for representing the value and attributes of a state variable.""" + + def __new__(cls, state): + """Create a new instance given a state variable.""" + new_var = super().__new__(cls, state.state) + new_var.__dict__ = state.attributes.copy() + new_var.entity_id = state.entity_id + new_var.last_updated = state.last_updated + new_var.last_changed = state.last_changed + return new_var + + +class State: + """Class for state functions.""" + + # + # Global hass instance + # + hass = None + + # + # notify message queues by variable + # + notify = {} + + # + # Last value of state variable notifications. We maintain this + # so that trigger evaluation can use the last notified value, + # rather than fetching the current value, which is subject to + # race conditions when multiple state variables are set quickly. + # + notify_var_last = {} + + # + # pyscript yaml configuration + # + pyscript_config = {} + + # + # pyscript vars which have already been registered as persisted + # + persisted_vars = {} + + # + # other parameters of all services that have "entity_id" as a parameter + # + service2args = {} + + def __init__(self): + """Warn on State instantiation.""" + _LOGGER.error("State class is not meant to be instantiated") + + @classmethod + def init(cls, hass): + """Initialize State.""" + cls.hass = hass + + @classmethod + async def get_service_params(cls): + """Get parameters for all services.""" + cls.service2args = {} + all_services = await async_get_all_descriptions(cls.hass) + for domain in all_services: + cls.service2args[domain] = {} + for service, desc in all_services[domain].items(): + if "entity_id" not in desc["fields"] and "target" not in desc: + continue + cls.service2args[domain][service] = set(desc["fields"].keys()) + cls.service2args[domain][service].discard("entity_id") + + @classmethod + async def notify_add(cls, var_names, queue): + """Register to notify state variables changes to be sent to queue.""" + + added = False + for var_name in var_names if isinstance(var_names, set) else {var_names}: + parts = var_name.split(".") + if len(parts) != 2 and len(parts) != 3: + continue + state_var_name = f"{parts[0]}.{parts[1]}" + if state_var_name not in cls.notify: + cls.notify[state_var_name] = {} + cls.notify[state_var_name][queue] = var_names + added = True + return added + + @classmethod + def notify_del(cls, var_names, queue): + """Unregister notify of state variables changes for given queue.""" + + for var_name in var_names if isinstance(var_names, set) else {var_names}: + parts = var_name.split(".") + if len(parts) != 2 and len(parts) != 3: + continue + state_var_name = f"{parts[0]}.{parts[1]}" + if state_var_name not in cls.notify or queue not in cls.notify[state_var_name]: + return + del cls.notify[state_var_name][queue] + + @classmethod + async def update(cls, new_vars, func_args): + """Deliver all notifications for state variable changes.""" + + notify = {} + for var_name, var_val in new_vars.items(): + if var_name in cls.notify: + cls.notify_var_last[var_name] = var_val + notify.update(cls.notify[var_name]) + + if notify: + _LOGGER.debug("state.update(%s, %s)", new_vars, func_args) + for queue, var_names in notify.items(): + await queue.put(["state", [cls.notify_var_get(var_names, new_vars), func_args.copy()]]) + + @classmethod + def notify_var_get(cls, var_names, new_vars): + """Add values of var_names to new_vars, or default to None.""" + notify_vars = new_vars.copy() + for var_name in var_names if var_names is not None else []: + if var_name in notify_vars: + continue + parts = var_name.split(".") + if var_name in cls.notify_var_last: + notify_vars[var_name] = cls.notify_var_last[var_name] + elif len(parts) == 3 and f"{parts[0]}.{parts[1]}" in cls.notify_var_last: + notify_vars[var_name] = getattr( + cls.notify_var_last[f"{parts[0]}.{parts[1]}"], parts[2], None + ) + elif len(parts) == 4 and parts[2] == "old" and f"{parts[0]}.{parts[1]}.old" in notify_vars: + notify_vars[var_name] = getattr(notify_vars[f"{parts[0]}.{parts[1]}.old"], parts[3], None) + elif 1 <= var_name.count(".") <= 3 and not cls.exist(var_name): + notify_vars[var_name] = None + return notify_vars + + @classmethod + def set(cls, var_name, value=None, new_attributes=None, **kwargs): + """Set a state variable and optional attributes in hass.""" + if var_name.count(".") != 1: + raise NameError(f"invalid name {var_name} (should be 'domain.entity')") + + if isinstance(value, StateVal): + if new_attributes is None: + # + # value is a StateVal, so extract the attributes and value + # + new_attributes = value.__dict__.copy() + for discard in STATE_VIRTUAL_ATTRS: + new_attributes.pop(discard, None) + value = str(value) + + state_value = None + if value is None or new_attributes is None: + state_value = cls.hass.states.get(var_name) + + if value is None and state_value: + value = state_value.state + + if new_attributes is None: + if state_value: + new_attributes = state_value.attributes.copy() + else: + new_attributes = {} + + curr_task = asyncio.current_task() + if "context" in kwargs and isinstance(kwargs["context"], Context): + context = kwargs["context"] + del kwargs["context"] + else: + context = Function.task2context.get(curr_task, None) + + if kwargs: + new_attributes = new_attributes.copy() + new_attributes.update(kwargs) + + _LOGGER.debug("setting %s = %s, attr = %s", var_name, value, new_attributes) + cls.hass.states.async_set(var_name, value, new_attributes, context=context) + if var_name in cls.notify_var_last or var_name in cls.notify: + # + # immediately update a variable we are monitoring since it could take a while + # for the state changed event to propagate + # + cls.notify_var_last[var_name] = StateVal(cls.hass.states.get(var_name)) + + if var_name in cls.persisted_vars: + cls.persisted_vars[var_name].set_state(value) + cls.persisted_vars[var_name].set_attributes(new_attributes) + + @classmethod + def setattr(cls, var_attr_name, value): + """Set a state variable's attribute in hass.""" + parts = var_attr_name.split(".") + if len(parts) != 3: + raise NameError(f"invalid name {var_attr_name} (should be 'domain.entity.attr')") + if not cls.exist(f"{parts[0]}.{parts[1]}"): + raise NameError(f"state {parts[0]}.{parts[1]} doesn't exist") + cls.set(f"{parts[0]}.{parts[1]}", **{parts[2]: value}) + + @classmethod + async def register_persist(cls, var_name): + """Register pyscript state variable to be persisted with RestoreState.""" + if var_name.startswith("pyscript.") and var_name not in cls.persisted_vars: + # this is a hack accessing hass internals; should re-implement using RestoreEntity + restore_data = cls.hass.data[DATA_RESTORE_STATE] + this_entity = PyscriptEntity() + this_entity.entity_id = var_name + cls.persisted_vars[var_name] = this_entity + try: + restore_data.async_restore_entity_added(this_entity) + except TypeError: + restore_data.async_restore_entity_added(var_name) + + @classmethod + async def persist(cls, var_name, default_value=None, default_attributes=None): + """Persist a pyscript domain state variable, and update with optional defaults.""" + if var_name.count(".") != 1 or not var_name.startswith("pyscript."): + raise NameError(f"invalid name {var_name} (should be 'pyscript.entity')") + + await cls.register_persist(var_name) + exists = cls.exist(var_name) + + if not exists and default_value is not None: + cls.set(var_name, default_value, default_attributes) + elif exists and default_attributes is not None: + # Patch the attributes with new values if necessary + current = cls.hass.states.get(var_name) + new_attributes = {k: v for (k, v) in default_attributes.items() if k not in current.attributes} + cls.set(var_name, current.state, **new_attributes) + + @classmethod + def exist(cls, var_name): + """Check if a state variable value or attribute exists in hass.""" + parts = var_name.split(".") + if len(parts) != 2 and len(parts) != 3: + return False + value = cls.hass.states.get(f"{parts[0]}.{parts[1]}") + if value is None: + return False + if ( + len(parts) == 2 + or (parts[0] in cls.service2args and parts[2] in cls.service2args[parts[0]]) + or parts[2] in value.attributes + or parts[2] in STATE_VIRTUAL_ATTRS + ): + return True + return False + + @classmethod + def get(cls, var_name): + """Get a state variable value or attribute from hass.""" + parts = var_name.split(".") + if len(parts) != 2 and len(parts) != 3: + raise NameError(f"invalid name '{var_name}' (should be 'domain.entity' or 'domain.entity.attr')") + state = cls.hass.states.get(f"{parts[0]}.{parts[1]}") + if not state: + raise NameError(f"name '{parts[0]}.{parts[1]}' is not defined") + # + # simplest case is just the state value + # + state = StateVal(state) + if len(parts) == 2: + return state + # + # see if this is a service that has an entity_id parameter + # + if parts[0] in cls.service2args and parts[2] in cls.service2args[parts[0]]: + params = cls.service2args[parts[0]][parts[2]] + + def service_call_factory(domain, service, entity_id, params): + async def service_call(*args, **kwargs): + curr_task = asyncio.current_task() + hass_args = {} + for keyword, typ, default in [ + ("context", [Context], Function.task2context.get(curr_task, None)), + ("blocking", [bool], None), + ("return_response", [bool], None), + ("limit", [float, int], None), + ]: + if keyword in kwargs and type(kwargs[keyword]) in typ: + hass_args[keyword] = kwargs.pop(keyword) + elif default: + hass_args[keyword] = default + + kwargs["entity_id"] = entity_id + if len(args) == 1 and len(params) == 1: + # + # with just a single parameter and positional argument, create the keyword setting + # + [param_name] = params + kwargs[param_name] = args[0] + elif len(args) != 0: + raise TypeError(f"service {domain}.{service} takes no positional arguments") + + # return await Function.hass_services_async_call(domain, service, kwargs, **hass_args) + return await cls.hass.services.async_call(domain, service, kwargs, **hass_args) + + return service_call + + return service_call_factory(parts[0], parts[2], f"{parts[0]}.{parts[1]}", params) + # + # finally see if it is an attribute + # + try: + return getattr(state, parts[2]) + except AttributeError: + raise AttributeError( # pylint: disable=raise-missing-from + f"state '{parts[0]}.{parts[1]}' has no attribute '{parts[2]}'" + ) + + @classmethod + def delete(cls, var_name, context=None): + """Delete a state variable or attribute from hass.""" + parts = var_name.split(".") + if not context: + context = Function.task2context.get(asyncio.current_task(), None) + context_arg = {"context": context} if context else {} + if len(parts) == 2: + if var_name in cls.notify_var_last or var_name in cls.notify: + # + # immediately update a variable we are monitoring since it could take a while + # for the state changed event to propagate + # + cls.notify_var_last[var_name] = None + if not cls.hass.states.async_remove(var_name, **context_arg): + raise NameError(f"name '{var_name}' not defined") + return + if len(parts) == 3: + var_name = f"{parts[0]}.{parts[1]}" + value = cls.hass.states.get(var_name) + if value is None: + raise NameError(f"state {var_name} doesn't exist") + new_attr = value.attributes.copy() + if parts[2] not in new_attr: + raise AttributeError(f"state '{var_name}' has no attribute '{parts[2]}'") + del new_attr[parts[2]] + cls.set(f"{var_name}", value.state, new_attributes=new_attr, **context_arg) + return + raise NameError(f"invalid name '{var_name}' (should be 'domain.entity' or 'domain.entity.attr')") + + @classmethod + def getattr(cls, var_name): + """Return a dict of attributes for a state variable.""" + if isinstance(var_name, StateVal): + attrs = var_name.__dict__.copy() + for discard in STATE_VIRTUAL_ATTRS: + attrs.pop(discard, None) + return attrs + if var_name.count(".") != 1: + raise NameError(f"invalid name {var_name} (should be 'domain.entity')") + value = cls.hass.states.get(var_name) + if not value: + return None + return value.attributes.copy() + + @classmethod + def get_attr(cls, var_name): + """Return a dict of attributes for a state variable - deprecated.""" + _LOGGER.warning("state.get_attr() is deprecated: use state.getattr() instead") + return cls.getattr(var_name) + + @classmethod + def completions(cls, root): + """Return possible completions of state variables.""" + words = set() + parts = root.split(".") + num_period = len(parts) - 1 + if num_period == 2: + # + # complete state attributes + # + last_period = root.rfind(".") + name = root[0:last_period] + value = cls.hass.states.get(name) + if value: + attr_root = root[last_period + 1 :] + attrs = set(value.attributes.keys()).union(STATE_VIRTUAL_ATTRS) + if parts[0] in cls.service2args: + attrs.update(set(cls.service2args[parts[0]].keys())) + for attr_name in attrs: + if attr_name.lower().startswith(attr_root): + words.add(f"{name}.{attr_name}") + elif num_period < 2: + # + # complete among all state names + # + for name in cls.hass.states.async_all(): + if name.entity_id.lower().startswith(root): + words.add(name.entity_id) + return words + + @classmethod + async def names(cls, domain=None): + """Implement names, which returns all entity_ids.""" + return cls.hass.states.async_entity_ids(domain) + + @classmethod + def register_functions(cls): + """Register state functions and config variable.""" + functions = { + "state.get": cls.get, + "state.set": cls.set, + "state.setattr": cls.setattr, + "state.names": cls.names, + "state.getattr": cls.getattr, + "state.get_attr": cls.get_attr, # deprecated form; to be removed + "state.persist": cls.persist, + "state.delete": cls.delete, + "pyscript.config": cls.pyscript_config, + } + Function.register(functions) + + @classmethod + def set_pyscript_config(cls, config): + """Set pyscript yaml config.""" + # + # have to update inplace, since dest is already used as value + # + cls.pyscript_config.clear() + for name, value in config.items(): + cls.pyscript_config[name] = value diff --git a/config/custom_components/pyscript/strings.json b/config/custom_components/pyscript/strings.json new file mode 100644 index 0000000..96a6966 --- /dev/null +++ b/config/custom_components/pyscript/strings.json @@ -0,0 +1,38 @@ +{ + "config": { + "step": { + "user": { + "title": "pyscript", + "description": "Once you have created an entry, refer to the [docs](https://hacs-pyscript.readthedocs.io/en/latest/) to learn how to create scripts and functions.", + "data": { + "allow_all_imports": "Allow All Imports?", + "hass_is_global": "Access hass as a global variable?" + } + } + }, + "abort": { + "already_configured": "Already configured.", + "single_instance_allowed": "Already configured. Only a single configuration possible.", + "updated_entry": "This entry has already been setup but the configuration has been updated." + } + }, + "options": { + "step": { + "init": { + "title": "Update pyscript configuration", + "data": { + "allow_all_imports": "Allow All Imports?", + "hass_is_global": "Access hass as a global variable?" + } + }, + "no_ui_configuration_allowed": { + "title": "No UI configuration allowed", + "description": "This entry was created via `configuration.yaml`, so all configuration parameters must be updated there. The [`pyscript.reload`](developer-tools/service) service will allow you to apply the changes you make to `configuration.yaml` without restarting your Home Assistant instance." + }, + "no_update": { + "title": "No update needed", + "description": "There is nothing to update." + } + } + } +} diff --git a/config/custom_components/pyscript/translations/de.json b/config/custom_components/pyscript/translations/de.json new file mode 100644 index 0000000..c11e276 --- /dev/null +++ b/config/custom_components/pyscript/translations/de.json @@ -0,0 +1,38 @@ +{ + "config": { + "step": { + "user": { + "title": "pyscript", + "description": "Wenn Sie einen Eintrag angelegt haben, können Sie sich die [Doku (Englisch)](https://hacs-pyscript.readthedocs.io/en/latest/) ansehen, um zu lernen wie Sie Scripts und Funktionen erstellen können.", + "data": { + "allow_all_imports": "Alle Importe erlauben?", + "hass_is_global": "Home Assistant als globale Variable verwenden?" + } + } + }, + "abort": { + "already_configured": "Bereits konfiguriert.", + "single_instance_allowed": "Bereits konfiguriert. Es ist nur eine Konfiguration gleichzeitig möglich", + "updated_entry": "Der Eintrag wurde bereits erstellt, aber die Konfiguration wurde aktualisiert." + } + }, + "options": { + "step": { + "init": { + "title": "Pyscript configuration aktualisieren", + "data": { + "allow_all_imports": "Alle Importe erlauben??", + "hass_is_global": "Home Assistant als globale Variable verwenden?" + } + }, + "no_ui_configuration_allowed": { + "title": "Die Konfiguartion der graphischen Nutzeroberfläche ist deaktiviert", + "description": "Der Eintrag wurde über die Datei `configuration.yaml` erstellt. Alle Konfigurationsparameter müssen desshalb dort eingestellt werden. Der [`pyscript.reload`](developer-tools/service) Service übernimmt alle Änderungen aus `configuration.yaml`, ohne dass Home Assistant neu gestartet werden muss." + }, + "no_update": { + "title": "Keine Aktualisierung notwendig", + "description": "Es gibt nichts zu aktualisieren." + } + } + } +} diff --git a/config/custom_components/pyscript/translations/en.json b/config/custom_components/pyscript/translations/en.json new file mode 100644 index 0000000..96a6966 --- /dev/null +++ b/config/custom_components/pyscript/translations/en.json @@ -0,0 +1,38 @@ +{ + "config": { + "step": { + "user": { + "title": "pyscript", + "description": "Once you have created an entry, refer to the [docs](https://hacs-pyscript.readthedocs.io/en/latest/) to learn how to create scripts and functions.", + "data": { + "allow_all_imports": "Allow All Imports?", + "hass_is_global": "Access hass as a global variable?" + } + } + }, + "abort": { + "already_configured": "Already configured.", + "single_instance_allowed": "Already configured. Only a single configuration possible.", + "updated_entry": "This entry has already been setup but the configuration has been updated." + } + }, + "options": { + "step": { + "init": { + "title": "Update pyscript configuration", + "data": { + "allow_all_imports": "Allow All Imports?", + "hass_is_global": "Access hass as a global variable?" + } + }, + "no_ui_configuration_allowed": { + "title": "No UI configuration allowed", + "description": "This entry was created via `configuration.yaml`, so all configuration parameters must be updated there. The [`pyscript.reload`](developer-tools/service) service will allow you to apply the changes you make to `configuration.yaml` without restarting your Home Assistant instance." + }, + "no_update": { + "title": "No update needed", + "description": "There is nothing to update." + } + } + } +} diff --git a/config/custom_components/pyscript/translations/sk.json b/config/custom_components/pyscript/translations/sk.json new file mode 100644 index 0000000..3271c69 --- /dev/null +++ b/config/custom_components/pyscript/translations/sk.json @@ -0,0 +1,38 @@ +{ + "config": { + "step": { + "user": { + "title": "pyscript", + "description": "Akonáhle ste vytvorili položku, pozrite si [docs](https://hacs-pyscript.readthedocs.io/en/latest/) naučiť sa, ako vytvárať skripty a funkcie.", + "data": { + "allow_all_imports": "Povoliť všetky importy?", + "hass_is_global": "Prístup k globálnej premennej?" + } + } + }, + "abort": { + "already_configured": "Už konfigurované.", + "single_instance_allowed": "Už nakonfigurované. Iba jedna možná konfigurácia.", + "updated_entry": "Táto položka už bola nastavená, ale konfigurácia bola aktualizovaná." + } + }, + "options": { + "step": { + "init": { + "title": "Aktualizovať pyscript konfiguráciu", + "data": { + "allow_all_imports": "povoliť všetky importy?", + "hass_is_global": "Prístup k globálnej premennej?" + } + }, + "no_ui_configuration_allowed": { + "title": "Nie je povolená konfigurácia používateľského rozhrania", + "description": "Tento záznam bol vytvorený cez `configuration.yaml`, Takže všetky konfiguračné parametre sa musia aktualizovať. [`pyscript.reload`](developer-tools/service) Služba vám umožní uplatniť zmeny, ktoré vykonáte `configuration.yaml` bez reštartovania inštancie Home Assistant." + }, + "no_update": { + "title": "Nie je potrebná aktualizácia", + "description": "Nie je nič na aktualizáciu." + } + } + } +} diff --git a/config/custom_components/pyscript/trigger.py b/config/custom_components/pyscript/trigger.py new file mode 100644 index 0000000..875a7c7 --- /dev/null +++ b/config/custom_components/pyscript/trigger.py @@ -0,0 +1,1396 @@ +"""Implements all the trigger logic.""" + +import asyncio +import datetime as dt +import functools +import locale +import logging +import math +import re +import time + +from croniter import croniter + +from homeassistant.core import Context +from homeassistant.helpers import sun +from homeassistant.util import dt as dt_util + +from .const import LOGGER_PATH +from .eval import AstEval, EvalFunc, EvalFuncVar +from .event import Event +from .function import Function +from .mqtt import Mqtt +from .state import STATE_VIRTUAL_ATTRS, State +from .webhook import Webhook + +_LOGGER = logging.getLogger(LOGGER_PATH + ".trigger") + + +STATE_RE = re.compile(r"\w+\.\w+(\.((\w+)|\*))?$") + + +def dt_now(): + """Return current time.""" + return dt.datetime.now() + + +def parse_time_offset(offset_str): + """Parse a time offset.""" + match = re.split(r"([-+]?\s*\d*\.?\d+(?:[eE][-+]?\d+)?)\s*(\w*)", offset_str) + scale = 1 + value = 0 + if len(match) == 4: + value = float(match[1].replace(" ", "")) + if match[2] in {"m", "min", "mins", "minute", "minutes"}: + scale = 60 + elif match[2] in {"h", "hr", "hour", "hours"}: + scale = 60 * 60 + elif match[2] in {"d", "day", "days"}: + scale = 60 * 60 * 24 + elif match[2] in {"w", "week", "weeks"}: + scale = 60 * 60 * 24 * 7 + elif match[2] not in {"", "s", "sec", "second", "seconds"}: + _LOGGER.error("can't parse time offset %s", offset_str) + else: + _LOGGER.error("can't parse time offset %s", offset_str) + return value * scale + + +def ident_any_values_changed(func_args, ident): + """Check for any changes to state or attributes on ident vars.""" + var_name = func_args.get("var_name", None) + + if var_name is None: + return False + value = func_args["value"] + old_value = func_args["old_value"] + + for check_var in ident: + if check_var == var_name and old_value != value: + return True + + if check_var.startswith(f"{var_name}."): + var_pieces = check_var.split(".") + if len(var_pieces) == 3 and f"{var_pieces[0]}.{var_pieces[1]}" == var_name: + if var_pieces[2] == "*": + # catch all has been requested, check all attributes for change + all_attrs = set() + if value is not None: + all_attrs |= set(value.__dict__.keys()) + if old_value is not None: + all_attrs |= set(old_value.__dict__.keys()) + for attr in all_attrs - STATE_VIRTUAL_ATTRS: + if getattr(value, attr, None) != getattr(old_value, attr, None): + return True + elif getattr(value, var_pieces[2], None) != getattr(old_value, var_pieces[2], None): + return True + + return False + + +def ident_values_changed(func_args, ident): + """Check for changes to state or attributes on ident vars.""" + var_name = func_args.get("var_name", None) + + if var_name is None: + return False + value = func_args["value"] + old_value = func_args["old_value"] + + for check_var in ident: + var_pieces = check_var.split(".") + if len(var_pieces) < 2 or len(var_pieces) > 3: + continue + var_root = f"{var_pieces[0]}.{var_pieces[1]}" + if var_root == var_name and (len(var_pieces) == 2 or var_pieces[2] == "old"): + if value != old_value: + return True + elif len(var_pieces) == 3 and var_root == var_name: + if getattr(value, var_pieces[2], None) != getattr(old_value, var_pieces[2], None): + return True + + return False + + +class TrigTime: + """Class for trigger time functions.""" + + # + # Global hass instance + # + hass = None + + # + # Mappings of day of week name to number, using US convention of sunday is 0. + # Initialized based on locale at startup. + # + dow2int = {} + + def __init__(self): + """Warn on TrigTime instantiation.""" + _LOGGER.error("TrigTime class is not meant to be instantiated") + + @classmethod + def init(cls, hass): + """Initialize TrigTime.""" + cls.hass = hass + + def wait_until_factory(ast_ctx): + """Return wapper to call to astFunction with the ast context.""" + + async def wait_until_call(*arg, **kw): + return await cls.wait_until(ast_ctx, *arg, **kw) + + return wait_until_call + + def user_task_create_factory(ast_ctx): + """Return wapper to call to astFunction with the ast context.""" + + async def user_task_create(func, *args, **kwargs): + """Implement task.create().""" + + async def func_call(func, func_name, new_ast_ctx, *args, **kwargs): + """Call user function inside task.create().""" + ret = await new_ast_ctx.call_func(func, func_name, *args, **kwargs) + if new_ast_ctx.get_exception_obj(): + new_ast_ctx.get_logger().error(new_ast_ctx.get_exception_long()) + return ret + + try: + if isinstance(func, (EvalFunc, EvalFuncVar)): + func_name = func.get_name() + else: + func_name = func.__name__ + except Exception: + func_name = "" + + new_ast_ctx = AstEval( + f"{ast_ctx.get_global_ctx_name()}.{func_name}", ast_ctx.get_global_ctx() + ) + Function.install_ast_funcs(new_ast_ctx) + task = Function.create_task( + func_call(func, func_name, new_ast_ctx, *args, **kwargs), ast_ctx=new_ast_ctx + ) + Function.task_done_callback_ctx(task, new_ast_ctx) + return task + + return user_task_create + + ast_funcs = { + "task.wait_until": wait_until_factory, + "task.create": user_task_create_factory, + } + Function.register_ast(ast_funcs) + + async def user_task_add_done_callback(task, callback, *args, **kwargs): + """Implement task.add_done_callback().""" + ast_ctx = None + if type(callback) is EvalFuncVar: + ast_ctx = callback.get_ast_ctx() + Function.task_add_done_callback(task, ast_ctx, callback, *args, **kwargs) + + funcs = { + "task.add_done_callback": user_task_add_done_callback, + "task.executor": cls.user_task_executor, + } + Function.register(funcs) + + try: + for i in range(0, 7): + cls.dow2int[locale.nl_langinfo(getattr(locale, f"ABDAY_{i + 1}")).lower()] = i + cls.dow2int[locale.nl_langinfo(getattr(locale, f"DAY_{i + 1}")).lower()] = i + except AttributeError: + # Win10 Python doesn't have locale.nl_langinfo, so default to English days of week + dow = [ + "sunday", + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + ] + for idx, name in enumerate(dow): + cls.dow2int[name] = idx + cls.dow2int[name[0:3]] = idx + + @classmethod + async def wait_until( + cls, + ast_ctx, + state_trigger=None, + state_check_now=True, + time_trigger=None, + event_trigger=None, + mqtt_trigger=None, + webhook_trigger=None, + webhook_local_only=True, + webhook_methods=None, + timeout=None, + state_hold=None, + state_hold_false=None, + __test_handshake__=None, + ): + """Wait for zero or more triggers, until an optional timeout.""" + if ( + state_trigger is None + and time_trigger is None + and event_trigger is None + and mqtt_trigger is None + and webhook_trigger is None + ): + if timeout is not None: + await asyncio.sleep(timeout) + return {"trigger_type": "timeout"} + return {"trigger_type": "none"} + state_trig_ident = set() + state_trig_ident_any = set() + state_trig_eval = None + event_trig_expr = None + mqtt_trig_expr = None + webhook_trig_expr = None + exc = None + notify_q = asyncio.Queue(0) + + last_state_trig_time = None + state_trig_waiting = False + state_trig_notify_info = [None, None] + state_false_time = None + check_state_expr_on_start = state_check_now or state_hold_false is not None + + if state_trigger is not None: + state_trig = [] + if isinstance(state_trigger, str): + state_trigger = [state_trigger] + elif isinstance(state_trigger, set): + state_trigger = list(state_trigger) + # + # separate out the entries that are just state var names, which mean trigger + # on any change (no expr) + # + for trig in state_trigger: + if STATE_RE.match(trig): + state_trig_ident_any.add(trig) + else: + state_trig.append(trig) + + if len(state_trig) > 0: + if len(state_trig) == 1: + state_trig_expr = state_trig[0] + else: + state_trig_expr = f"any([{', '.join(state_trig)}])" + state_trig_eval = AstEval( + f"{ast_ctx.name} state_trigger", + ast_ctx.get_global_ctx(), + logger_name=ast_ctx.get_logger_name(), + ) + Function.install_ast_funcs(state_trig_eval) + state_trig_eval.parse(state_trig_expr, mode="eval") + state_trig_ident = await state_trig_eval.get_names() + exc = state_trig_eval.get_exception_obj() + if exc is not None: + raise exc + + state_trig_ident.update(state_trig_ident_any) + if check_state_expr_on_start and state_trig_eval: + # + # check straight away to see if the condition is met + # + new_vars = State.notify_var_get(state_trig_ident, {}) + state_trig_ok = await state_trig_eval.eval(new_vars) + exc = state_trig_eval.get_exception_obj() + if exc is not None: + raise exc + if state_hold_false is not None and not state_check_now: + # + # if state_trig_ok we wait until it is false; + # otherwise we consider now to be the start of the false hold time + # + state_false_time = None if state_trig_ok else time.monotonic() + elif state_hold is not None and state_trig_ok: + state_trig_waiting = True + state_trig_notify_info = [None, {"trigger_type": "state"}] + last_state_trig_time = time.monotonic() + _LOGGER.debug( + "trigger %s wait_until: state trigger immediately true; now waiting for state_hold of %g seconds", + ast_ctx.name, + state_hold, + ) + elif state_trig_ok: + return {"trigger_type": "state"} + + _LOGGER.debug( + "trigger %s wait_until: watching vars %s", + ast_ctx.name, + state_trig_ident, + ) + if len(state_trig_ident) > 0: + await State.notify_add(state_trig_ident, notify_q) + if event_trigger is not None: + if isinstance(event_trigger, str): + event_trigger = [event_trigger] + if len(event_trigger) > 1: + event_trig_expr = AstEval( + f"{ast_ctx.name} event_trigger", + ast_ctx.get_global_ctx(), + logger_name=ast_ctx.get_logger_name(), + ) + Function.install_ast_funcs(event_trig_expr) + event_trig_expr.parse(event_trigger[1], mode="eval") + exc = event_trig_expr.get_exception_obj() + if exc is not None: + if len(state_trig_ident) > 0: + State.notify_del(state_trig_ident, notify_q) + raise exc + Event.notify_add(event_trigger[0], notify_q) + if mqtt_trigger is not None: + if isinstance(mqtt_trigger, str): + mqtt_trigger = [mqtt_trigger] + if len(mqtt_trigger) > 1: + mqtt_trig_expr = AstEval( + f"{ast_ctx.name} mqtt_trigger", + ast_ctx.get_global_ctx(), + logger_name=ast_ctx.get_logger_name(), + ) + Function.install_ast_funcs(mqtt_trig_expr) + mqtt_trig_expr.parse(mqtt_trigger[1], mode="eval") + exc = mqtt_trig_expr.get_exception_obj() + if exc is not None: + if len(state_trig_ident) > 0: + State.notify_del(state_trig_ident, notify_q) + raise exc + await Mqtt.notify_add(mqtt_trigger[0], notify_q) + if webhook_trigger is not None: + if isinstance(webhook_trigger, str): + webhook_trigger = [webhook_trigger] + if len(webhook_trigger) > 1: + webhook_trig_expr = AstEval( + f"{ast_ctx.name} webhook_trigger", + ast_ctx.get_global_ctx(), + logger_name=ast_ctx.get_logger_name(), + ) + Function.install_ast_funcs(webhook_trig_expr) + webhook_trig_expr.parse(webhook_trigger[1], mode="eval") + exc = webhook_trig_expr.get_exception_obj() + if exc is not None: + if len(state_trig_ident) > 0: + State.notify_del(state_trig_ident, notify_q) + raise exc + if webhook_methods is None: + webhook_methods = {"POST", "PUT"} + Webhook.notify_add(webhook_trigger[0], webhook_local_only, webhook_methods, notify_q) + + time0 = time.monotonic() + + if __test_handshake__: + # + # used for testing to avoid race conditions + # we use this as a handshake that we are about to + # listen to the queue + # + State.set(__test_handshake__[0], __test_handshake__[1]) + + while True: + ret = None + this_timeout = None + state_trig_timeout = False + time_next = None + startup_time = None + now = dt_now() + if startup_time is None: + startup_time = now + if time_trigger is not None: + time_next, time_next_adj = await cls.timer_trigger_next(time_trigger, now, startup_time) + _LOGGER.debug( + "trigger %s wait_until time_next = %s, now = %s", + ast_ctx.name, + time_next, + now, + ) + if time_next is not None: + this_timeout = (time_next_adj - now).total_seconds() + if timeout is not None: + time_left = time0 + timeout - time.monotonic() + if time_left <= 0: + ret = {"trigger_type": "timeout"} + break + if this_timeout is None or this_timeout > time_left: + ret = {"trigger_type": "timeout"} + this_timeout = time_left + time_next = now + dt.timedelta(seconds=this_timeout) + if state_trig_waiting: + time_left = last_state_trig_time + state_hold - time.monotonic() + if this_timeout is None or time_left < this_timeout: + this_timeout = time_left + state_trig_timeout = True + time_next = now + dt.timedelta(seconds=this_timeout) + if this_timeout is None: + if ( + state_trigger is None + and event_trigger is None + and mqtt_trigger is None + and webhook_trigger is None + ): + _LOGGER.debug( + "trigger %s wait_until no next time - returning with none", + ast_ctx.name, + ) + ret = {"trigger_type": "none"} + break + _LOGGER.debug("trigger %s wait_until no timeout", ast_ctx.name) + notify_type, notify_info = await notify_q.get() + else: + timeout_occured = False + while True: + try: + this_timeout = max(0, this_timeout) + _LOGGER.debug("trigger %s wait_until %.6g secs", ast_ctx.name, this_timeout) + notify_type, notify_info = await asyncio.wait_for( + notify_q.get(), timeout=this_timeout + ) + state_trig_timeout = False + except asyncio.TimeoutError: + actual_now = dt_now() + if actual_now < time_next: + this_timeout = (time_next - actual_now).total_seconds() + # tests/tests_function's simple now() requires us to ignore + # timeouts that are up to 1us too early; otherwise wait for + # longer until we are sure we are at or past time_next + if this_timeout > 1e-6: + continue + if not state_trig_timeout: + if not ret: + ret = {"trigger_type": "time"} + if time_next is not None: + ret["trigger_time"] = time_next + timeout_occured = True + break + if timeout_occured: + break + if state_trig_timeout: + ret = state_trig_notify_info[1] + state_trig_waiting = False + break + if notify_type == "state": + if notify_info: + new_vars, func_args = notify_info + else: + new_vars, func_args = None, {} + + state_trig_ok = True + + if not ident_any_values_changed(func_args, state_trig_ident_any): + # if var_name not in func_args we are state_check_now + if "var_name" in func_args and not ident_values_changed(func_args, state_trig_ident): + continue + + if state_trig_eval: + state_trig_ok = await state_trig_eval.eval(new_vars) + exc = state_trig_eval.get_exception_obj() + if exc is not None: + break + + if state_hold_false is not None: + if state_false_time is None: + if state_trig_ok: + # + # wasn't False, so ignore + # + continue + # + # first False, so remember when it is + # + state_false_time = time.monotonic() + elif state_trig_ok: + too_soon = time.monotonic() - state_false_time < state_hold_false + state_false_time = None + if too_soon: + # + # was False but not for long enough, so start over + # + continue + + if state_hold is not None: + if state_trig_ok: + if not state_trig_waiting: + state_trig_waiting = True + state_trig_notify_info = notify_info + last_state_trig_time = time.monotonic() + _LOGGER.debug( + "trigger %s wait_until: got %s trigger; now waiting for state_hold of %g seconds", + notify_type, + ast_ctx.name, + state_hold, + ) + else: + _LOGGER.debug( + "trigger %s wait_until: got %s trigger; still waiting for state_hold of %g seconds", + notify_type, + ast_ctx.name, + state_hold, + ) + continue + if state_trig_waiting: + state_trig_waiting = False + _LOGGER.debug( + "trigger %s wait_until: %s trigger now false during state_hold; waiting for new trigger", + notify_type, + ast_ctx.name, + ) + continue + if state_trig_ok: + ret = notify_info[1] if notify_info else None + break + elif notify_type == "event": + if event_trig_expr is None: + ret = notify_info + break + event_trig_ok = await event_trig_expr.eval(notify_info) + exc = event_trig_expr.get_exception_obj() + if exc is not None: + break + if event_trig_ok: + ret = notify_info + break + elif notify_type == "mqtt": + if mqtt_trig_expr is None: + ret = notify_info + break + mqtt_trig_ok = await mqtt_trig_expr.eval(notify_info) + exc = mqtt_trig_expr.get_exception_obj() + if exc is not None: + break + if mqtt_trig_ok: + ret = notify_info + break + elif notify_type == "webhook": + if webhook_trig_expr is None: + ret = notify_info + break + webhook_trig_ok = await webhook_trig_expr.eval(notify_info) + exc = webhook_trig_expr.get_exception_obj() + if exc is not None: + break + if webhook_trig_ok: + ret = notify_info + break + else: + _LOGGER.error( + "trigger %s wait_until got unexpected queue message %s", + ast_ctx.name, + notify_type, + ) + + if len(state_trig_ident) > 0: + State.notify_del(state_trig_ident, notify_q) + if event_trigger is not None: + Event.notify_del(event_trigger[0], notify_q) + if mqtt_trigger is not None: + Mqtt.notify_del(mqtt_trigger[0], notify_q) + if webhook_trigger is not None: + Webhook.notify_del(webhook_trigger[0], notify_q) + if exc: + raise exc + return ret + + @classmethod + async def user_task_executor(cls, func, *args, **kwargs): + """Implement task.executor().""" + if asyncio.iscoroutinefunction(func) or not callable(func): + raise TypeError(f"function {func} is not callable by task.executor") + if isinstance(func, EvalFuncVar): + raise TypeError( + "pyscript functions can't be called from task.executor - must be a regular python function" + ) + return await cls.hass.async_add_executor_job(functools.partial(func, **kwargs), *args) + + @classmethod + async def parse_date_time(cls, date_time_str, day_offset, now, startup_time): + """Parse a date time string, returning datetime.""" + year = now.year + month = now.month + day = now.day + + dt_str_orig = dt_str = date_time_str.strip().lower() + # + # parse the date + # + match0 = re.match(r"0*(\d+)[-/]0*(\d+)(?:[-/]0*(\d+))?", dt_str) + match1 = re.match(r"(\w+)", dt_str) + if match0: + if match0[3]: + year, month, day = int(match0[1]), int(match0[2]), int(match0[3]) + else: + month, day = int(match0[1]), int(match0[2]) + day_offset = 0 # explicit date means no offset + dt_str = dt_str[len(match0.group(0)) :] + elif match1: + skip = True + if match1[1] in cls.dow2int: + dow = cls.dow2int[match1[1]] + if dow >= (now.isoweekday() % 7): + day_offset = dow - (now.isoweekday() % 7) + else: + day_offset = 7 + dow - (now.isoweekday() % 7) + elif match1[1] == "today": + day_offset = 0 + elif match1[1] == "tomorrow": + day_offset = 1 + else: + skip = False + if skip: + dt_str = dt_str[len(match1.group(0)) :] + if day_offset != 0: + now = dt.datetime(year, month, day) + dt.timedelta(days=day_offset) + year = now.year + month = now.month + day = now.day + else: + now = dt.datetime(year, month, day) + dt_str = dt_str.strip() + if len(dt_str) == 0: + return now + + # + # parse the time + # + match0 = re.match(r"0*(\d+):0*(\d+)(?::0*(\d*\.?\d+(?:[eE][-+]?\d+)?))?", dt_str) + if match0: + if match0[3]: + hour, mins, sec = int(match0[1]), int(match0[2]), float(match0[3]) + else: + hour, mins, sec = int(match0[1]), int(match0[2]), 0 + dt_str = dt_str[len(match0.group(0)) :] + elif dt_str.startswith("sunrise") or dt_str.startswith("sunset"): + location = sun.get_astral_location(cls.hass) + if isinstance(location, tuple): + # HA core-2021.5.0 included this breaking change: https://github.com/home-assistant/core/pull/48573. + # As part of the upgrade to astral 2.2, sun.get_astral_location() now returns a tuple including the + # elevation. We just want the astral.location.Location object. + location = location[0] + try: + if dt_str.startswith("sunrise"): + time_sun = await cls.hass.async_add_executor_job( + location.sunrise, dt.date(year, month, day) + ) + dt_str = dt_str[7:] + else: + time_sun = await cls.hass.async_add_executor_job( + location.sunset, dt.date(year, month, day) + ) + dt_str = dt_str[6:] + except Exception: + _LOGGER.warning("'%s' not defined at this latitude", dt_str) + # return something in the past so it is ignored + return now - dt.timedelta(days=100) + now += time_sun.date() - now.date() + hour, mins, sec = time_sun.hour, time_sun.minute, time_sun.second + elif dt_str.startswith("noon"): + hour, mins, sec = 12, 0, 0 + dt_str = dt_str[4:] + elif dt_str.startswith("midnight"): + hour, mins, sec = 0, 0, 0 + dt_str = dt_str[8:] + elif dt_str.startswith("now") and dt_str_orig == dt_str: + # + # "now" means the first time, and only matches if there was no date specification + # + hour, mins, sec = 0, 0, 0 + now = startup_time + dt_str = dt_str[3:] + else: + hour, mins, sec = 0, 0, 0 + now += dt.timedelta(seconds=sec + 60 * (mins + 60 * hour)) + # + # parse the offset + # + dt_str = dt_str.strip() + if len(dt_str) > 0: + now = now + dt.timedelta(seconds=parse_time_offset(dt_str)) + return now + + @classmethod + async def timer_active_check(cls, time_spec, now, startup_time): + """Check if the given time matches the time specification.""" + results = {"+": [], "-": []} + for entry in time_spec if isinstance(time_spec, list) else [time_spec]: + this_match = False + negate = False + active_str = entry.strip() + if active_str.startswith("not"): + negate = True + active_str = active_str.replace("not ", "") + + cron_match = re.match(r"cron\((?P.*)\)", active_str) + range_expr = re.match(r"range\(([^,]+),\s?([^,]+)\)", active_str) + if cron_match: + if not croniter.is_valid(cron_match.group("cron_expr")): + _LOGGER.error("Invalid cron expression: %s", cron_match) + return False + + this_match = croniter.match(cron_match.group("cron_expr"), now) + + elif range_expr: + try: + dt_start, dt_end = range_expr.groups() + except ValueError as exc: + _LOGGER.error("Invalid range expression: %s", exc) + return False + + start = await cls.parse_date_time(dt_start.strip(), 0, now, startup_time) + end = await cls.parse_date_time(dt_end.strip(), 0, start, startup_time) + + if start <= end: + this_match = start <= now <= end + else: # Over midnight + this_match = now >= start or now <= end + else: + _LOGGER.error("Invalid time_active expression: %s", active_str) + return False + + if negate: + results["-"].append(not this_match) + else: + results["+"].append(this_match) + + # An empty spec, or only neg specs, is True + result = (any(results["+"]) if results["+"] else True) and all(results["-"]) + + return result + + @classmethod + async def timer_trigger_next(cls, time_spec, now, startup_time): + """Return the next trigger time based on the given time and time specification.""" + next_time = None + next_time_adj = None + if not isinstance(time_spec, list): + time_spec = [time_spec] + for spec in time_spec: + cron_match = re.search(r"cron\((?P.*)\)", spec) + match1 = re.split(r"once\((.*)\)", spec) + match2 = re.split(r"period\(([^,]*),([^,]*)(?:,([^,]*))?\)", spec) + if cron_match: + if not croniter.is_valid(cron_match.group("cron_expr")): + _LOGGER.error("Invalid cron expression: %s", cron_match) + continue + + # + # Handling DST changes is tricky; all times in pyscript are naive (no timezone). This is the + # one part of the code where we do check timezones, in case now and next_time bracket a DST + # change. We return next_time as the local time of the next trigger according to the cron + # spec, and next_time_adj is potentially adjusted so that (next_time_adj - now) is the correct + # timedelta to wait (eg: if cron is a daily trigger at 6am, next_time will always be 6am + # tomorrow, and next_time_adj will also by 6am, except on the day of a DST change, when it + # will be 5am or 7am, such that (next_time_adj - now) is 23 hours or 25 hours. + # + # We might have to fetch multiple croniter times, in case (next_time_adj - now) is non-positive + # after a DST change. + # + # Also, datetime doesn't correctly subtract datetimes in different timezones, so we need to compute + # the different in UTC. See https://blog.ganssle.io/articles/2018/02/aware-datetime-arithmetic.html. + # + cron_iter = croniter(cron_match.group("cron_expr"), now, dt.datetime) + delta = None + while delta is None or delta.total_seconds() <= 0: + val = cron_iter.get_next() + delta = dt_util.as_local(val).astimezone(dt_util.UTC) - dt_util.as_local(now).astimezone( + dt_util.UTC + ) + + if next_time is None or val < next_time: + next_time = val + next_time_adj = now + delta + + elif len(match1) == 3: + this_t = await cls.parse_date_time(match1[1].strip(), 0, now, startup_time) + day_offset = (now - this_t).days + 1 + if day_offset != 0 and this_t != startup_time: + # + # Try a day offset (won't make a difference if spec has full date) + # + this_t = await cls.parse_date_time(match1[1].strip(), day_offset, now, startup_time) + startup = now == this_t and now == startup_time + if (now < this_t or startup) and (next_time is None or this_t < next_time): + next_time_adj = next_time = this_t + + elif len(match2) == 5: + start_str, period_str = match2[1].strip(), match2[2].strip() + start = await cls.parse_date_time(start_str, 0, now, startup_time) + period = parse_time_offset(period_str) + if period <= 0: + _LOGGER.error("Invalid non-positive period %s in period(): %s", period, time_spec) + continue + + if match2[3] is None: + startup = now == start and now == startup_time + if (now < start or startup) and (next_time is None or start < next_time): + next_time_adj = next_time = start + if now >= start and not startup: + secs = period * (1.0 + math.floor((now - start).total_seconds() / period)) + this_t = start + dt.timedelta(seconds=secs) + if now < this_t and (next_time is None or this_t < next_time): + next_time_adj = next_time = this_t + continue + end_str = match2[3].strip() + end = await cls.parse_date_time(end_str, 0, now, startup_time) + end_offset = 1 if end < start else 0 + for day in [-1, 0, 1]: + start = await cls.parse_date_time(start_str, day, now, startup_time) + end = await cls.parse_date_time(end_str, day + end_offset, now, startup_time) + if now < start or (now == start and now == startup_time): + if next_time is None or start < next_time: + next_time_adj = next_time = start + break + secs = period * (1.0 + math.floor((now - start).total_seconds() / period)) + this_t = start + dt.timedelta(seconds=secs) + if start <= this_t <= end: + if next_time is None or this_t < next_time: + next_time_adj = next_time = this_t + break + + else: + _LOGGER.warning("Can't parse %s in time_trigger check", spec) + return next_time, next_time_adj + + +class TrigInfo: + """Class for all trigger-decorated functions.""" + + def __init__( + self, + name, + trig_cfg, + global_ctx=None, + ): + """Create a new TrigInfo.""" + self.name = name + self.task = None + self.global_ctx = global_ctx + self.trig_cfg = trig_cfg + self.state_trigger = trig_cfg.get("state_trigger", {}).get("args", None) + self.state_trigger_kwargs = trig_cfg.get("state_trigger", {}).get("kwargs", {}) + self.state_hold = self.state_trigger_kwargs.get("state_hold", None) + self.state_hold_false = self.state_trigger_kwargs.get("state_hold_false", None) + self.state_check_now = self.state_trigger_kwargs.get("state_check_now", False) + self.state_user_watch = self.state_trigger_kwargs.get("watch", None) + self.time_trigger = trig_cfg.get("time_trigger", {}).get("args", None) + self.time_trigger_kwargs = trig_cfg.get("time_trigger", {}).get("kwargs", {}) + self.event_trigger = trig_cfg.get("event_trigger", {}).get("args", None) + self.event_trigger_kwargs = trig_cfg.get("event_trigger", {}).get("kwargs", {}) + self.mqtt_trigger = trig_cfg.get("mqtt_trigger", {}).get("args", None) + self.mqtt_trigger_kwargs = trig_cfg.get("mqtt_trigger", {}).get("kwargs", {}) + self.webhook_trigger = trig_cfg.get("webhook_trigger", {}).get("args", None) + self.webhook_trigger_kwargs = trig_cfg.get("webhook_trigger", {}).get("kwargs", {}) + self.webhook_local_only = self.webhook_trigger_kwargs.get("local_only", True) + self.webhook_methods = self.webhook_trigger_kwargs.get("methods", {"POST", "PUT"}) + self.state_active = trig_cfg.get("state_active", {}).get("args", None) + self.time_active = trig_cfg.get("time_active", {}).get("args", None) + self.time_active_hold_off = trig_cfg.get("time_active", {}).get("kwargs", {}).get("hold_off", None) + self.task_unique = trig_cfg.get("task_unique", {}).get("args", None) + self.task_unique_kwargs = trig_cfg.get("task_unique", {}).get("kwargs", None) + self.action = trig_cfg.get("action") + self.global_sym_table = trig_cfg.get("global_sym_table", {}) + self.notify_q = asyncio.Queue(0) + self.active_expr = None + self.state_active_ident = None + self.state_trig_expr = None + self.state_trig_eval = None + self.state_trig_ident = None + self.state_trig_ident_any = set() + self.event_trig_expr = None + self.mqtt_trig_expr = None + self.webhook_trig_expr = None + self.have_trigger = False + self.setup_ok = False + self.run_on_startup = False + self.run_on_shutdown = False + + if self.state_active is not None: + self.active_expr = AstEval( + f"{self.name} @state_active()", self.global_ctx, logger_name=self.name + ) + Function.install_ast_funcs(self.active_expr) + self.active_expr.parse(self.state_active, mode="eval") + exc = self.active_expr.get_exception_long() + if exc is not None: + self.active_expr.get_logger().error(exc) + return + + if "time_trigger" in trig_cfg and self.time_trigger is None: + self.run_on_startup = True + if self.time_trigger is not None: + while "startup" in self.time_trigger: + self.run_on_startup = True + self.time_trigger.remove("startup") + while "shutdown" in self.time_trigger: + self.run_on_shutdown = True + self.time_trigger.remove("shutdown") + if len(self.time_trigger) == 0: + self.time_trigger = None + + if self.state_trigger is not None: + state_trig = [] + for triggers in self.state_trigger: + if isinstance(triggers, str): + triggers = [triggers] + elif isinstance(triggers, set): + triggers = list(triggers) + # + # separate out the entries that are just state var names, which mean trigger + # on any change (no expr) + # + for trig in triggers: + if STATE_RE.match(trig): + self.state_trig_ident_any.add(trig) + else: + state_trig.append(trig) + + if len(state_trig) > 0: + if len(state_trig) == 1: + self.state_trig_expr = state_trig[0] + else: + self.state_trig_expr = f"any([{', '.join(state_trig)}])" + self.state_trig_eval = AstEval( + f"{self.name} @state_trigger()", self.global_ctx, logger_name=self.name + ) + Function.install_ast_funcs(self.state_trig_eval) + self.state_trig_eval.parse(self.state_trig_expr, mode="eval") + exc = self.state_trig_eval.get_exception_long() + if exc is not None: + self.state_trig_eval.get_logger().error(exc) + return + self.have_trigger = True + + if self.event_trigger is not None: + if len(self.event_trigger) == 2: + self.event_trig_expr = AstEval( + f"{self.name} @event_trigger()", + self.global_ctx, + logger_name=self.name, + ) + Function.install_ast_funcs(self.event_trig_expr) + self.event_trig_expr.parse(self.event_trigger[1], mode="eval") + exc = self.event_trig_expr.get_exception_long() + if exc is not None: + self.event_trig_expr.get_logger().error(exc) + return + self.have_trigger = True + + if self.mqtt_trigger is not None: + if len(self.mqtt_trigger) == 2: + self.mqtt_trig_expr = AstEval( + f"{self.name} @mqtt_trigger()", + self.global_ctx, + logger_name=self.name, + ) + Function.install_ast_funcs(self.mqtt_trig_expr) + self.mqtt_trig_expr.parse(self.mqtt_trigger[1], mode="eval") + exc = self.mqtt_trig_expr.get_exception_long() + if exc is not None: + self.mqtt_trig_expr.get_logger().error(exc) + return + self.have_trigger = True + + if self.webhook_trigger is not None: + if len(self.webhook_trigger) == 2: + self.webhook_trig_expr = AstEval( + f"{self.name} @webhook_trigger()", + self.global_ctx, + logger_name=self.name, + ) + Function.install_ast_funcs(self.webhook_trig_expr) + self.webhook_trig_expr.parse(self.webhook_trigger[1], mode="eval") + exc = self.webhook_trig_expr.get_exception_long() + if exc is not None: + self.webhook_trig_expr.get_logger().error(exc) + return + self.have_trigger = True + + self.setup_ok = True + + def stop(self): + """Stop this trigger task.""" + + if self.task: + if self.state_trig_ident: + State.notify_del(self.state_trig_ident, self.notify_q) + if self.event_trigger is not None: + Event.notify_del(self.event_trigger[0], self.notify_q) + if self.mqtt_trigger is not None: + Mqtt.notify_del(self.mqtt_trigger[0], self.notify_q) + if self.webhook_trigger is not None: + Webhook.notify_del(self.webhook_trigger[0], self.notify_q) + if self.task: + Function.reaper_cancel(self.task) + self.task = None + if self.run_on_shutdown: + notify_type = "shutdown" + notify_info = {"trigger_type": "time", "trigger_time": "shutdown"} + notify_info.update(self.time_trigger_kwargs.get("kwargs", {})) + action_future = self.call_action(notify_type, notify_info, run_task=False) + Function.waiter_await(action_future) + + def start(self): + """Start this trigger task.""" + if not self.task and self.setup_ok: + self.task = Function.create_task(self.trigger_watch()) + _LOGGER.debug("trigger %s is active", self.name) + + async def trigger_watch(self): + """Task that runs for each trigger, waiting for the next trigger and calling the function.""" + + try: + + if self.state_trigger is not None: + self.state_trig_ident = set() + if self.state_user_watch: + if isinstance(self.state_user_watch, list): + self.state_trig_ident = set(self.state_user_watch) + else: + self.state_trig_ident = self.state_user_watch + else: + if self.state_trig_eval: + self.state_trig_ident = await self.state_trig_eval.get_names() + self.state_trig_ident.update(self.state_trig_ident_any) + _LOGGER.debug("trigger %s: watching vars %s", self.name, self.state_trig_ident) + if len(self.state_trig_ident) == 0 or not await State.notify_add( + self.state_trig_ident, self.notify_q + ): + _LOGGER.error( + "trigger %s: @state_trigger is not watching any variables; will never trigger", + self.name, + ) + + if self.active_expr: + self.state_active_ident = await self.active_expr.get_names() + + if self.event_trigger is not None: + _LOGGER.debug("trigger %s adding event_trigger %s", self.name, self.event_trigger[0]) + Event.notify_add(self.event_trigger[0], self.notify_q) + if self.mqtt_trigger is not None: + _LOGGER.debug("trigger %s adding mqtt_trigger %s", self.name, self.mqtt_trigger[0]) + await Mqtt.notify_add(self.mqtt_trigger[0], self.notify_q) + if self.webhook_trigger is not None: + _LOGGER.debug("trigger %s adding webhook_trigger %s", self.name, self.webhook_trigger[0]) + Webhook.notify_add( + self.webhook_trigger[0], self.webhook_local_only, self.webhook_methods, self.notify_q + ) + + last_trig_time = None + last_state_trig_time = None + state_trig_waiting = False + state_trig_notify_info = [None, None] + state_false_time = None + now = startup_time = None + check_state_expr_on_start = self.state_check_now or self.state_hold_false is not None + + while True: + timeout = None + state_trig_timeout = False + notify_info = None + notify_type = None + now = dt_now() + if startup_time is None: + startup_time = now + if self.run_on_startup: + # + # first time only - skip waiting for other triggers + # + notify_type = "startup" + notify_info = {"trigger_type": "time", "trigger_time": "startup"} + self.run_on_startup = False + elif check_state_expr_on_start: + # + # first time only - skip wait and check state trigger + # + notify_type = "state" + if self.state_trig_ident: + notify_vars = State.notify_var_get(self.state_trig_ident, {}) + else: + notify_vars = {} + notify_info = [notify_vars, {"trigger_type": notify_type}] + check_state_expr_on_start = False + else: + if self.time_trigger: + time_next, time_next_adj = await TrigTime.timer_trigger_next( + self.time_trigger, now, startup_time + ) + _LOGGER.debug( + "trigger %s time_next = %s, now = %s", + self.name, + time_next, + now, + ) + if time_next is not None: + timeout = (time_next_adj - now).total_seconds() + if state_trig_waiting: + time_left = last_state_trig_time + self.state_hold - time.monotonic() + if timeout is None or time_left < timeout: + timeout = time_left + time_next = now + dt.timedelta(seconds=timeout) + state_trig_timeout = True + if timeout is not None: + while True: + try: + timeout = max(0, timeout) + _LOGGER.debug("trigger %s waiting for %.6g secs", self.name, timeout) + notify_type, notify_info = await asyncio.wait_for( + self.notify_q.get(), timeout=timeout + ) + state_trig_timeout = False + now = dt_now() + except asyncio.TimeoutError: + actual_now = dt_now() + if actual_now < time_next: + timeout = (time_next - actual_now).total_seconds() + continue + now = time_next + if not state_trig_timeout: + notify_type = "time" + notify_info = { + "trigger_type": "time", + "trigger_time": time_next, + } + break + elif self.have_trigger: + _LOGGER.debug("trigger %s waiting for state change or event", self.name) + notify_type, notify_info = await self.notify_q.get() + now = dt_now() + else: + _LOGGER.debug("trigger %s finished", self.name) + return + + # + # check the trigger-specific expressions + # + trig_ok = True + new_vars = {} + user_kwargs = {} + if state_trig_timeout: + new_vars, func_args = state_trig_notify_info + state_trig_waiting = False + elif notify_type == "state": + new_vars, func_args = notify_info + user_kwargs = self.state_trigger_kwargs.get("kwargs", {}) + + if not ident_any_values_changed(func_args, self.state_trig_ident_any): + # + # if var_name not in func_args we are check_state_expr_on_start + # + if "var_name" in func_args and not ident_values_changed( + func_args, self.state_trig_ident + ): + continue + + if self.state_trig_eval: + trig_ok = await self.state_trig_eval.eval(new_vars) + exc = self.state_trig_eval.get_exception_long() + if exc is not None: + self.state_trig_eval.get_logger().error(exc) + trig_ok = False + + if self.state_hold_false is not None: + if "var_name" not in func_args: + # + # this is check_state_expr_on_start check + # if immediately true, force wait until False + # otherwise start False wait now + # + state_false_time = None if trig_ok else time.monotonic() + if not self.state_check_now: + continue + if state_false_time is None: + if trig_ok: + # + # wasn't False, so ignore after initial check + # + if "var_name" in func_args: + continue + else: + # + # first False, so remember when it is + # + state_false_time = time.monotonic() + elif trig_ok and "var_name" in func_args: + too_soon = time.monotonic() - state_false_time < self.state_hold_false + state_false_time = None + if too_soon: + # + # was False but not for long enough, so start over + # + continue + else: + trig_ok = False + + if self.state_hold is not None: + if trig_ok: + if not state_trig_waiting: + state_trig_waiting = True + state_trig_notify_info = notify_info + last_state_trig_time = time.monotonic() + _LOGGER.debug( + "trigger %s got %s trigger; now waiting for state_hold of %g seconds", + notify_type, + self.name, + self.state_hold, + ) + else: + _LOGGER.debug( + "trigger %s got %s trigger; still waiting for state_hold of %g seconds", + notify_type, + self.name, + self.state_hold, + ) + func_args.update(user_kwargs) + continue + if state_trig_waiting: + state_trig_waiting = False + _LOGGER.debug( + "trigger %s %s trigger now false during state_hold; waiting for new trigger", + notify_type, + self.name, + ) + continue + + elif notify_type == "event": + func_args = notify_info + user_kwargs = self.event_trigger_kwargs.get("kwargs", {}) + if self.event_trig_expr: + trig_ok = await self.event_trig_expr.eval(notify_info) + elif notify_type == "mqtt": + func_args = notify_info + user_kwargs = self.mqtt_trigger_kwargs.get("kwargs", {}) + if self.mqtt_trig_expr: + trig_ok = await self.mqtt_trig_expr.eval(notify_info) + elif notify_type == "webhook": + func_args = notify_info + user_kwargs = self.webhook_trigger_kwargs.get("kwargs", {}) + if self.webhook_trig_expr: + trig_ok = await self.webhook_trig_expr.eval(notify_info) + + else: + user_kwargs = self.time_trigger_kwargs.get("kwargs", {}) + func_args = notify_info + + # + # now check the state and time active expressions + # + if trig_ok and self.active_expr: + active_vars = State.notify_var_get(self.state_active_ident, new_vars) + trig_ok = await self.active_expr.eval(active_vars) + exc = self.active_expr.get_exception_long() + if exc is not None: + self.active_expr.get_logger().error(exc) + trig_ok = False + if trig_ok and self.time_active: + trig_ok = await TrigTime.timer_active_check(self.time_active, now, startup_time) + + if not trig_ok: + _LOGGER.debug( + "trigger %s got %s trigger, but not active", + self.name, + notify_type, + ) + continue + + if ( + self.time_active_hold_off is not None + and last_trig_time is not None + and time.monotonic() < last_trig_time + self.time_active_hold_off + ): + _LOGGER.debug( + "trigger %s got %s trigger, but less than %s seconds since last trigger, so skipping", + notify_type, + self.name, + self.time_active_hold_off, + ) + continue + + func_args.update(user_kwargs) + if self.call_action(notify_type, func_args): + last_trig_time = time.monotonic() + + except asyncio.CancelledError: + raise + + except Exception as exc: + # _LOGGER.error(f"{self.name}: " + traceback.format_exc(-1)) + _LOGGER.error("%s: %s", self.name, exc) + if self.state_trig_ident: + State.notify_del(self.state_trig_ident, self.notify_q) + if self.event_trigger is not None: + Event.notify_del(self.event_trigger[0], self.notify_q) + if self.mqtt_trigger is not None: + Mqtt.notify_del(self.mqtt_trigger[0], self.notify_q) + if self.webhook_trigger is not None: + Webhook.notify_del(self.webhook_trigger[0], self.notify_q) + return + + def call_action(self, notify_type, func_args, run_task=True): + """Call the trigger action function.""" + action_ast_ctx = AstEval(f"{self.action.global_ctx_name}.{self.action.name}", self.action.global_ctx) + Function.install_ast_funcs(action_ast_ctx) + task_unique_func = None + if self.task_unique is not None: + task_unique_func = Function.task_unique_factory(action_ast_ctx) + + # + # check for @task_unique with kill_me=True + # + if ( + self.task_unique is not None + and self.task_unique_kwargs + and self.task_unique_kwargs["kill_me"] + and Function.unique_name_used(action_ast_ctx, self.task_unique) + ): + _LOGGER.debug( + "trigger %s got %s trigger, @task_unique kill_me=True prevented new action", + notify_type, + self.name, + ) + return False + + # Create new HASS Context with incoming as parent + if "context" in func_args and isinstance(func_args["context"], Context): + hass_context = Context(parent_id=func_args["context"].id) + else: + hass_context = Context() + + # Fire an event indicating that pyscript is running + # Note: the event must have an entity_id for logbook to work correctly. + ev_name = self.name.replace(".", "_") + ev_entity_id = f"pyscript.{ev_name}" + + event_data = {"name": ev_name, "entity_id": ev_entity_id, "func_args": func_args} + Function.hass.bus.async_fire("pyscript_running", event_data, context=hass_context) + + _LOGGER.debug( + "trigger %s got %s trigger, running action (kwargs = %s)", + self.name, + notify_type, + func_args, + ) + + async def do_func_call(func, ast_ctx, task_unique, task_unique_func, hass_context, **kwargs): + # Store HASS Context for this Task + Function.store_hass_context(hass_context) + + if task_unique and task_unique_func: + await task_unique_func(task_unique) + await ast_ctx.call_func(func, None, **kwargs) + if ast_ctx.get_exception_obj(): + ast_ctx.get_logger().error(ast_ctx.get_exception_long()) + + func = do_func_call( + self.action, + action_ast_ctx, + self.task_unique, + task_unique_func, + hass_context, + **func_args, + ) + if run_task: + task = Function.create_task(func, ast_ctx=action_ast_ctx) + Function.task_done_callback_ctx(task, action_ast_ctx) + return True + return func diff --git a/config/custom_components/pyscript/webhook.py b/config/custom_components/pyscript/webhook.py new file mode 100644 index 0000000..3c9b06a --- /dev/null +++ b/config/custom_components/pyscript/webhook.py @@ -0,0 +1,95 @@ +"""Handles webhooks and notification.""" + +import logging + +from aiohttp import hdrs + +from homeassistant.components import webhook + +from .const import LOGGER_PATH + +_LOGGER = logging.getLogger(LOGGER_PATH + ".webhook") + + +class Webhook: + """Define webhook functions.""" + + # + # Global hass instance + # + hass = None + + # + # notify message queues by webhook type + # + notify = {} + notify_remove = {} + + def __init__(self): + """Warn on Webhook instantiation.""" + _LOGGER.error("Webhook class is not meant to be instantiated") + + @classmethod + def init(cls, hass): + """Initialize Webhook.""" + + cls.hass = hass + + @classmethod + async def webhook_handler(cls, hass, webhook_id, request): + """Listen callback for given webhook which updates any notifications.""" + + func_args = { + "trigger_type": "webhook", + "webhook_id": webhook_id, + } + + if "json" in request.headers.get(hdrs.CONTENT_TYPE, ""): + func_args["payload"] = await request.json() + else: + # Could potentially return multiples of a key - only take the first + payload_multidict = await request.post() + func_args["payload"] = {k: payload_multidict.getone(k) for k in payload_multidict.keys()} + + await cls.update(webhook_id, func_args) + + @classmethod + def notify_add(cls, webhook_id, local_only, methods, queue): + """Register to notify for webhooks of given type to be sent to queue.""" + if webhook_id not in cls.notify: + cls.notify[webhook_id] = set() + _LOGGER.debug("webhook.notify_add(%s) -> adding webhook listener", webhook_id) + webhook.async_register( + cls.hass, + "pyscript", # DOMAIN + "pyscript", # NAME + webhook_id, + cls.webhook_handler, + local_only=local_only, + allowed_methods=methods, + ) + cls.notify_remove[webhook_id] = lambda: webhook.async_unregister(cls.hass, webhook_id) + + cls.notify[webhook_id].add(queue) + + @classmethod + def notify_del(cls, webhook_id, queue): + """Unregister to notify for webhooks of given type for given queue.""" + + if webhook_id not in cls.notify or queue not in cls.notify[webhook_id]: + return + cls.notify[webhook_id].discard(queue) + if len(cls.notify[webhook_id]) == 0: + cls.notify_remove[webhook_id]() + _LOGGER.debug("webhook.notify_del(%s) -> removing webhook listener", webhook_id) + del cls.notify[webhook_id] + del cls.notify_remove[webhook_id] + + @classmethod + async def update(cls, webhook_id, func_args): + """Deliver all notifications for an webhook of the given type.""" + + _LOGGER.debug("webhook.update(%s, %s)", webhook_id, func_args) + if webhook_id in cls.notify: + for queue in cls.notify[webhook_id]: + await queue.put(["webhook", func_args.copy()]) diff --git a/config/custom_components/solar_optimizer/__init__.py b/config/custom_components/solar_optimizer/__init__.py new file mode 100644 index 0000000..f3ffb46 --- /dev/null +++ b/config/custom_components/solar_optimizer/__init__.py @@ -0,0 +1,143 @@ +"""Initialisation du package de l'intégration HACS Tuto""" +import logging +import voluptuous as vol + +from homeassistant.const import EVENT_HOMEASSISTANT_START +from homeassistant.core import HomeAssistant +from homeassistant.config_entries import ConfigEntry +from homeassistant.helpers.typing import ConfigType +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers import selector +from homeassistant.components.input_boolean import DOMAIN as INPUT_BOOLEAN_DOMAIN +from homeassistant.components.input_number import DOMAIN as INPUT_NUMBER_DOMAIN +from homeassistant.components.switch import DOMAIN as SWITCH_DOMAIN +from homeassistant.components.humidifier import DOMAIN as HUMIDIFIER_DOMAIN +from homeassistant.components.climate import DOMAIN as CLIMATE_DOMAIN +from homeassistant.components.number import DOMAIN as NUMBER_DOMAIN +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN + +# from homeassistant.helpers.entity_component import EntityComponent + + +from .const import DOMAIN, PLATFORMS +from .coordinator import SolarOptimizerCoordinator + +# from .input_boolean import async_setup_entry as async_setup_entry_input_boolean + +_LOGGER = logging.getLogger(__name__) + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + "algorithm": vol.Schema( + { + vol.Required("initial_temp", default=1000): vol.Coerce(float), + vol.Required("min_temp", default=0.1): vol.Coerce(float), + vol.Required("cooling_factor", default=0.95): vol.Coerce(float), + vol.Required( + "max_iteration_number", default=1000 + ): cv.positive_int, + } + ), + "devices": vol.All( + [ + { + vol.Required("name"): str, + vol.Required("entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig( + domain=[INPUT_BOOLEAN_DOMAIN, SWITCH_DOMAIN, HUMIDIFIER_DOMAIN, CLIMATE_DOMAIN, BUTTON_DOMAIN] + ) + ), + vol.Optional("power_entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig( + domain=[INPUT_NUMBER_DOMAIN, NUMBER_DOMAIN] + ) + ), + vol.Required("power_max"): vol.Coerce(float), + vol.Optional("power_min"): vol.Coerce(float), + vol.Optional("power_step"): vol.Coerce(float), + vol.Optional("check_usable_template"): str, + vol.Optional("check_active_template"): str, + vol.Optional("duration_min"): vol.Coerce(float), + vol.Optional("duration_stop_min"): vol.Coerce(float), + vol.Optional("duration_power_min"): vol.Coerce(float), + vol.Optional("action_mode"): str, + vol.Required("activation_service"): str, + vol.Required("deactivation_service"): str, + vol.Optional("change_power_service"): str, + vol.Optional("convert_power_divide_factor"): vol.Coerce( + float + ), + vol.Optional("battery_soc_threshold", default=0): vol.Coerce(float), + } + ] + ), + } + ), + }, + extra=vol.ALLOW_EXTRA, +) + + +async def async_setup( + hass: HomeAssistant, config: ConfigType +): # pylint: disable=unused-argument + """Initialisation de l'intégration""" + _LOGGER.info( + "Initializing %s integration with plaforms: %s with config: %s", + DOMAIN, + PLATFORMS, + config.get(DOMAIN), + ) + + hass.data.setdefault(DOMAIN, {}) + + # L'argument config contient votre fichier configuration.yaml + solar_optimizer_config = config.get(DOMAIN) + + hass.data[DOMAIN]["coordinator"] = coordinator = SolarOptimizerCoordinator( + hass, solar_optimizer_config + ) + + hass.bus.async_listen_once("homeassistant_started", coordinator.on_ha_started) + + return True + + +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Creation des entités à partir d'une configEntry""" + + _LOGGER.debug( + "Appel de async_setup_entry entry: entry_id='%s', data='%s'", + entry.entry_id, + entry.data, + ) + + hass.data.setdefault(DOMAIN, {}) + + # Enregistrement de l'écouteur de changement 'update_listener' + entry.async_on_unload(entry.add_update_listener(update_listener)) + + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) + + return True + + +async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Fonction qui force le rechargement des entités associées à une configEntry""" + await hass.config_entries.async_reload(entry.entry_id) + + +async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: + """Handle removal of an entry.""" + if unloaded := await hass.config_entries.async_unload_platforms(entry, PLATFORMS): + pass + # hass.data[DOMAIN].pop(entry.entry_id) + return unloaded + + +async def async_reload_entry(hass: HomeAssistant, entry: ConfigEntry) -> None: + """Reload config entry.""" + await async_unload_entry(hass, entry) + # await async_setup_entry(hass, entry) diff --git a/config/custom_components/solar_optimizer/config_flow.py b/config/custom_components/solar_optimizer/config_flow.py new file mode 100644 index 0000000..845cb87 --- /dev/null +++ b/config/custom_components/solar_optimizer/config_flow.py @@ -0,0 +1,145 @@ +""" Le Config Flow """ + +import logging +from typing import Any +import copy +from collections.abc import Mapping +import voluptuous as vol + + +from homeassistant.core import callback +from homeassistant.config_entries import ( + ConfigFlow, + FlowResult, + OptionsFlow, + ConfigEntry, +) +from homeassistant.components.sensor import DOMAIN as SENSOR_DOMAIN +from homeassistant.components.input_number import DOMAIN as INPUT_NUMBER_DOMAIN +from homeassistant.helpers import selector +import homeassistant.helpers.config_validation as cv + +from .const import DOMAIN + +_LOGGER = logging.getLogger(__name__) + +solar_optimizer_schema = { + vol.Required("refresh_period_sec", default=300): int, + vol.Required("power_consumption_entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]) + ), + vol.Required("power_production_entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]) + ), + vol.Required("sell_cost_entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]) + ), + vol.Required("buy_cost_entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]) + ), + vol.Required("sell_tax_percent_entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig(domain=[INPUT_NUMBER_DOMAIN]) + ), + vol.Optional("smooth_production", default=True): cv.boolean, + vol.Optional("battery_soc_entity_id"): selector.EntitySelector( + selector.EntitySelectorConfig(domain=[SENSOR_DOMAIN, INPUT_NUMBER_DOMAIN]) + ), +} + + +class SolarOptimizerConfigFlow(ConfigFlow, domain=DOMAIN): + """La classe qui implémente le config flow pour notre DOMAIN. + Elle doit dériver de FlowHandler""" + + # La version de notre configFlow. Va permettre de migrer les entités + # vers une version plus récente en cas de changement + VERSION = 1 + _user_inputs: dict = {} + + async def async_step_user(self, user_input: dict | None = None) -> FlowResult: + """Gestion de l'étape 'user'. Point d'entrée de notre + configFlow. Cette méthode est appelée 2 fois : + 1. une première fois sans user_input -> on affiche le formulaire de configuration + 2. une deuxième fois avec les données saisies par l'utilisateur dans user_input -> on sauvegarde les données saisies + """ + user_form = vol.Schema(solar_optimizer_schema) + + if user_input is None: + _LOGGER.debug( + "config_flow step user (1). 1er appel : pas de user_input -> on affiche le form user_form" + ) + return self.async_show_form(step_id="user", data_schema=user_form) + + # 2ème appel : il y a des user_input -> on stocke le résultat + self._user_inputs.update(user_input) + _LOGGER.debug( + "config_flow step2 (2). L'ensemble de la configuration est: %s", + self._user_inputs, + ) + + return self.async_create_entry(title="SolarOptimizer", data=self._user_inputs) + + @staticmethod + @callback + def async_get_options_flow(config_entry: ConfigEntry): + """Get options flow for this handler""" + return SolarOptimizerOptionsFlow(config_entry) + + +class SolarOptimizerOptionsFlow(OptionsFlow): + """The class which enable to modified the configuration""" + + _user_inputs: dict = {} + config_entry: ConfigEntry = None + + def __init__(self, config_entry: ConfigEntry) -> None: + """Initialisation de l'option flow. On a le ConfigEntry existant en entrée""" + self.config_entry = config_entry + # On initialise les user_inputs avec les données du configEntry + self._user_inputs = config_entry.data.copy() + + async def async_step_init(self, user_input: dict | None = None) -> FlowResult: + """Gestion de l'étape 'user'. Point d'entrée de notre + configFlow. Cette méthode est appelée 2 fois : + 1. une première fois sans user_input -> on affiche le formulaire de configuration + 2. une deuxième fois avec les données saisies par l'utilisateur dans user_input -> on sauvegarde les données saisies + """ + user_form = vol.Schema(solar_optimizer_schema) + + if user_input is None: + _LOGGER.debug( + "config_flow step user (1). 1er appel : pas de user_input -> on affiche le form user_form" + ) + return self.async_show_form( + step_id="init", + data_schema=self.add_suggested_values_to_schema( + data_schema=user_form, + suggested_values=self._user_inputs, + ), + ) + + # 2ème appel : il y a des user_input -> on stocke le résultat + self._user_inputs.update(user_input) + _LOGGER.debug( + "config_flow step_user (2). L'ensemble de la configuration est: %s", + self._user_inputs, + ) + + # On appelle le step de fin pour enregistrer les modifications + return await self.async_end() + + async def async_end(self): + """Finalization of the ConfigEntry creation""" + _LOGGER.info( + "Recreation de l'entry %s. La nouvelle config est maintenant : %s", + self.config_entry.entry_id, + self._user_inputs, + ) + + # Modification des data de la configEntry + # (et non pas ajout d'un objet options dans la configEntry) + self.hass.config_entries.async_update_entry( + self.config_entry, data=self._user_inputs + ) + # Suppression de l'objet options dans la configEntry + return self.async_create_entry(title=None, data=None) diff --git a/config/custom_components/solar_optimizer/const.py b/config/custom_components/solar_optimizer/const.py new file mode 100644 index 0000000..f89b2e2 --- /dev/null +++ b/config/custom_components/solar_optimizer/const.py @@ -0,0 +1,41 @@ +""" Les constantes pour l'intégration Solar Optimizer """ +from slugify import slugify + +from homeassistant.const import Platform +from homeassistant.core import HomeAssistant +from homeassistant.util import dt as dt_util + +DOMAIN = "solar_optimizer" +PLATFORMS: list[Platform] = [Platform.SENSOR, Platform.SWITCH] + +DEFAULT_REFRESH_PERIOD_SEC = 300 + +CONF_ACTION_MODE_SERVICE = "service_call" +CONF_ACTION_MODE_EVENT = "event" + +CONF_ACTION_MODES = [CONF_ACTION_MODE_SERVICE, CONF_ACTION_MODE_EVENT] + +EVENT_TYPE_SOLAR_OPTIMIZER_CHANGE_POWER = "solar_optimizer_change_power_event" +EVENT_TYPE_SOLAR_OPTIMIZER_STATE_CHANGE = "solar_optimizer_state_change_event" + +EVENT_TYPE_SOLAR_OPTIMIZER_ENABLE_STATE_CHANGE = ( + "solar_optimizer_enable_state_change_event" +) + + +def get_tz(hass: HomeAssistant): + """Get the current timezone""" + + return dt_util.get_time_zone(hass.config.time_zone) + + +def name_to_unique_id(name: str) -> str: + """Convert a name to a unique id. Replace ' ' by _""" + return slugify(name).replace("-", "_") + + +class ConfigurationError(Exception): + """An error in configuration""" + + def __init__(self, message): + super().__init__(message) diff --git a/config/custom_components/solar_optimizer/coordinator.py b/config/custom_components/solar_optimizer/coordinator.py new file mode 100644 index 0000000..1707295 --- /dev/null +++ b/config/custom_components/solar_optimizer/coordinator.py @@ -0,0 +1,228 @@ +""" The data coordinator class """ +import logging +import math +from datetime import timedelta + + +from homeassistant.core import HomeAssistant # callback + +from homeassistant.helpers.update_coordinator import ( + DataUpdateCoordinator, +) + +from homeassistant.config_entries import ConfigEntry + +from .const import DEFAULT_REFRESH_PERIOD_SEC, name_to_unique_id +from .managed_device import ManagedDevice +from .simulated_annealing_algo import SimulatedAnnealingAlgorithm + +_LOGGER = logging.getLogger(__name__) + + +def get_safe_float(hass, entity_id: str): + """Get a safe float state value for an entity. + Return None if entity is not available""" + if entity_id is None or not (state := hass.states.get(entity_id)) or state.state == "unknown" or state.state == "unavailable": + return None + float_val = float(state.state) + return None if math.isinf(float_val) or not math.isfinite(float_val) else float_val + + +class SolarOptimizerCoordinator(DataUpdateCoordinator): + """The coordinator class which is used to coordinate all update""" + + _devices: list[ManagedDevice] + _power_consumption_entity_id: str + _power_production_entity_id: str + _sell_cost_entity_id: str + _buy_cost_entity_id: str + _sell_tax_percent_entity_id: str + _battery_soc_entity_id: str + _smooth_production: bool + _last_production: float + + _algo: SimulatedAnnealingAlgorithm + + def __init__(self, hass: HomeAssistant, config): + """Initialize the coordinator""" + super().__init__( + hass, + _LOGGER, + name="Solar Optimizer", + # update_interval=timedelta(seconds=refresh_period_sec), + ) # pylint : disable=line-too-long + self._devices = [] + try: + for _, device in enumerate(config.get("devices")): + _LOGGER.debug("Configuration of manageable device: %s", device) + self._devices.append(ManagedDevice(hass, device)) + except Exception as err: + _LOGGER.error(err) + _LOGGER.error( + "Your 'devices' configuration is wrong. SolarOptimizer will not be operational until you fix it" + ) + raise err + + algo_config = config.get("algorithm") + self._algo = SimulatedAnnealingAlgorithm( + float(algo_config.get("initial_temp")), + float(algo_config.get("min_temp")), + float(algo_config.get("cooling_factor")), + int(algo_config.get("max_iteration_number")), + ) + self.config = config + + async def configure(self, config: ConfigEntry) -> None: + """Configure the coordinator from configEntry of the integration""" + refresh_period_sec = ( + config.data.get("refresh_period_sec") or DEFAULT_REFRESH_PERIOD_SEC + ) + self.update_interval = timedelta(seconds=refresh_period_sec) + self._schedule_refresh() + + self._power_consumption_entity_id = config.data.get( + "power_consumption_entity_id" + ) + self._power_production_entity_id = config.data.get("power_production_entity_id") + self._sell_cost_entity_id = config.data.get("sell_cost_entity_id") + self._buy_cost_entity_id = config.data.get("buy_cost_entity_id") + self._sell_tax_percent_entity_id = config.data.get("sell_tax_percent_entity_id") + self._battery_soc_entity_id = config.data.get("battery_soc_entity_id") + self._smooth_production = config.data.get("smooth_production") is True + self._last_production = 0.0 + + # Do not calculate immediatly because switch state are not restored yet. Wait for homeassistant_started event + # which is captured in onHAStarted method + # await self.async_config_entry_first_refresh() + + async def on_ha_started(self, _) -> None: + """Listen the homeassistant_started event to initialize the first calculation""" + _LOGGER.info("First initialization of Solar Optimizer") + await self.async_config_entry_first_refresh() + + async def _async_update_data(self): + _LOGGER.info("Refreshing Solar Optimizer calculation") + + calculated_data = {} + + # Add a device state attributes + for _, device in enumerate(self._devices): + # Initialize current power depending or reality + device.set_current_power_with_device_state() + + # Add a power_consumption and power_production + power_production = get_safe_float(self.hass, self._power_production_entity_id) + if not power_production: + _LOGGER.warning( + "Power production is not valued. Solar Optimizer will be disabled" + ) + return None + + if not self._smooth_production: + calculated_data["power_production"] = power_production + else: + self._last_production = round( + 0.5 * self._last_production + 0.5 * power_production + ) + calculated_data["power_production"] = self._last_production + + calculated_data["power_production_brut"] = power_production + + calculated_data["power_consumption"] = get_safe_float( + self.hass, self._power_consumption_entity_id + ) + + calculated_data["sell_cost"] = get_safe_float( + self.hass, self._sell_cost_entity_id + ) + + calculated_data["buy_cost"] = get_safe_float( + self.hass, self._buy_cost_entity_id + ) + + calculated_data["sell_tax_percent"] = get_safe_float( + self.hass, self._sell_tax_percent_entity_id + ) + + soc = get_safe_float(self.hass, self._battery_soc_entity_id) + calculated_data["battery_soc"] = soc if soc is not None else 0 + + # + # Call Algorithm Recuit simulé + # + best_solution, best_objective, total_power = self._algo.recuit_simule( + self._devices, + calculated_data["power_consumption"], + calculated_data["power_production"], + calculated_data["sell_cost"], + calculated_data["buy_cost"], + calculated_data["sell_tax_percent"], + calculated_data["battery_soc"] + ) + + calculated_data["best_solution"] = best_solution + calculated_data["best_objective"] = best_objective + calculated_data["total_power"] = total_power + + # Uses the result to turn on or off or change power + should_log = False + for _, equipement in enumerate(best_solution): + _LOGGER.debug("Dealing with best_solution for %s", equipement) + name = equipement["name"] + requested_power = equipement.get("requested_power") + state = equipement["state"] + device = self.get_device_by_name(name) + if not device: + continue + is_active = device.is_active + if is_active and not state: + _LOGGER.debug("Extinction de %s", name) + should_log = True + await device.deactivate() + elif not is_active and state: + _LOGGER.debug("Allumage de %s", name) + should_log = True + await device.activate(requested_power) + + # Send change power if state is now on and change power is accepted and (power have change or eqt is just activated) + if ( + state + and device.can_change_power + and (device.current_power != requested_power or not is_active) + ): + _LOGGER.debug( + "Change power of %s to %s", + equipement["name"], + requested_power, + ) + should_log = True + await device.change_requested_power(requested_power) + + # Add updated data to the result + calculated_data[name_to_unique_id(name)] = device + + if should_log: + _LOGGER.info("Calculated data are: %s", calculated_data) + else: + _LOGGER.debug("Calculated data are: %s", calculated_data) + + return calculated_data + + @property + def devices(self) -> list[ManagedDevice]: + """Get all the managed device""" + return self._devices + + def get_device_by_name(self, name: str) -> ManagedDevice | None: + """Returns the device which name is given in argument""" + for _, device in enumerate(self._devices): + if device.name == name: + return device + return None + + def get_device_by_unique_id(self, uid: str) -> ManagedDevice | None: + """Returns the device which name is given in argument""" + for _, device in enumerate(self._devices): + if device.unique_id == uid: + return device + return None diff --git a/config/custom_components/solar_optimizer/hacs.json b/config/custom_components/solar_optimizer/hacs.json new file mode 100644 index 0000000..d894bce --- /dev/null +++ b/config/custom_components/solar_optimizer/hacs.json @@ -0,0 +1,7 @@ +{ + "name": "Solar Optimizer", + "content_in_root": false, + "render_readme": true, + "hide_default_branch": false, + "homeassistant": "2023.6.1" +} \ No newline at end of file diff --git a/config/custom_components/solar_optimizer/managed_device.py b/config/custom_components/solar_optimizer/managed_device.py new file mode 100644 index 0000000..0d0fb69 --- /dev/null +++ b/config/custom_components/solar_optimizer/managed_device.py @@ -0,0 +1,479 @@ +""" A ManagedDevice represent a device than can be managed by the optimisatiion algorithm""" +import logging +from datetime import datetime, timedelta + +from homeassistant.core import HomeAssistant +from homeassistant.helpers.template import Template +from homeassistant.const import STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN + +from .const import ( + get_tz, + name_to_unique_id, + CONF_ACTION_MODE_SERVICE, + CONF_ACTION_MODE_EVENT, + CONF_ACTION_MODES, + ConfigurationError, + EVENT_TYPE_SOLAR_OPTIMIZER_CHANGE_POWER, + EVENT_TYPE_SOLAR_OPTIMIZER_STATE_CHANGE, + EVENT_TYPE_SOLAR_OPTIMIZER_ENABLE_STATE_CHANGE, +) + +ACTION_ACTIVATE = "Activate" +ACTION_DEACTIVATE = "Deactivate" +ACTION_CHANGE_POWER = "ChangePower" + +_LOGGER = logging.getLogger(__name__) + + +async def do_service_action( + hass: HomeAssistant, + entity_id, + action_type, + service_name, + current_power, + requested_power, + convert_power_divide_factor, +): + """Activate an entity via a service call""" + _LOGGER.info("Calling service %s for entity %s", service_name, entity_id) + + parties = service_name.split("/") + if len(parties) != 2: + raise ConfigurationError( + f"Incorrect service declaration for entity {entity_id}. Service {service_name} should be formatted with: 'domain/service'" + ) + + if action_type == ACTION_CHANGE_POWER: + value = round(requested_power / convert_power_divide_factor) + service_data = {"value": value} + else: + service_data = {} + + target = { + "entity_id": entity_id, + } + + await hass.services.async_call( + parties[0], parties[1], service_data=service_data, target=target + ) + + # Also send an event to inform + do_event_action( + hass, + entity_id, + action_type, + current_power, + requested_power, + EVENT_TYPE_SOLAR_OPTIMIZER_STATE_CHANGE, + ) + + +def do_event_action( + hass: HomeAssistant, + entity_id, + action_type, + current_power, + requested_power, + event_type: str, +): + """Activate an entity via an event""" + _LOGGER.info( + "Sending event %s with action %s for entity %s with requested_power %s and current_power %s", + event_type, + action_type, + entity_id, + requested_power, + current_power, + ) + + hass.bus.fire( + event_type=event_type, + event_data={ + "action_type": action_type, + "requested_power": requested_power, + "current_power": current_power, + "entity_id": entity_id, + }, + ) + + +class ManagedDevice: + """A Managed device representation""" + + _name: str + _unique_id: str + _entity_id: str + _power_entity_id: str + _power_max: int + _power_min: int + _power_step: int + _can_change_power: bool + _current_power: int + _requested_power: int + _duration_sec: int + _duration_stop_sec: int + _duration_power_sec: int + _check_usable_template: Template + _check_active_template: Template + _next_date_available: datetime + _next_date_available_power: datetime + _action_mode: str + _activation_service: str + _deactivation_service: str + _change_power_service: str + _convert_power_divide_factor: int + _battery_soc: float + _battery_soc_threshold: float + + def __init__(self, hass: HomeAssistant, device_config): + """Initialize a manageable device""" + self._hass = hass + self._name = device_config.get("name") + self._unique_id = name_to_unique_id(self._name) + self._entity_id = device_config.get("entity_id") + self._power_entity_id = device_config.get("power_entity_id") + self._power_max = int(device_config.get("power_max")) + self._power_min = int(device_config.get("power_min") or -1) + self._power_step = int(device_config.get("power_step") or 0) + self._can_change_power = self._power_min >= 0 + self._convert_power_divide_factor = int( + device_config.get("convert_power_divide_factor") or 1 + ) + + self._current_power = self._requested_power = 0 + duration_min = float(device_config.get("duration_min")) + self._duration_sec = round(duration_min * 60) + self._duration_power_sec = round( + float(device_config.get("duration_power_min") or duration_min) * 60 + ) + + self._duration_stop_sec = round( + float(device_config.get("duration_stop_min") or duration_min) * 60 + ) + + if device_config.get("check_usable_template"): + self._check_usable_template = Template( + device_config.get("check_usable_template"), hass + ) + else: + # If no template for usability, the device is supposed to be always usable + self._check_usable_template = Template("{{ True }}", hass) + if device_config.get("check_active_template"): + self._check_active_template = Template( + device_config.get("check_active_template"), hass + ) + else: + template_string = ( + "{{ is_state('" + self._entity_id + "', '" + STATE_ON + "') }}" + ) + self._check_active_template = Template(template_string, hass) + self._next_date_available_power = self._next_date_available = datetime.now( + get_tz(hass) + ) + self._action_mode = device_config.get("action_mode") + self._activation_service = device_config.get("activation_service") + self._deactivation_service = device_config.get("deactivation_service") + self._change_power_service = device_config.get("change_power_service") + + self._battery_soc = None + self._battery_soc_threshold = float(device_config.get("battery_soc_threshold") or 0) + + if self.is_active: + self._requested_power = self._current_power = ( + self._power_max if self._can_change_power else self._power_min + ) + + self._enable = True + + async def _apply_action(self, action_type: str, requested_power=None): + """Apply an action to a managed device. + This method is a generical method for activate, deactivate, change_requested_power + """ + _LOGGER.debug( + "Applying action %s for entity %s. requested_power=%s", + action_type, + self._entity_id, + requested_power, + ) + if requested_power is not None: + self._requested_power = requested_power + + if self._action_mode == CONF_ACTION_MODE_SERVICE: + method = None + entity_id = self._entity_id + if action_type == ACTION_ACTIVATE: + method = self._activation_service + self.reset_next_date_available(action_type) + if self._can_change_power: + self.reset_next_date_available_power() + elif action_type == ACTION_DEACTIVATE: + method = self._deactivation_service + self.reset_next_date_available(action_type) + elif action_type == ACTION_CHANGE_POWER: + assert ( + self._can_change_power + ), f"Equipment {self._name} cannot change its power. We should not be there." + method = self._change_power_service + entity_id = self._power_entity_id + self.reset_next_date_available_power() + + await do_service_action( + self._hass, + entity_id, + action_type, + method, + self._current_power, + self._requested_power, + self._convert_power_divide_factor, + ) + elif self._action_mode == CONF_ACTION_MODE_EVENT: + do_event_action( + self._hass, + self._entity_id, + action_type, + self._current_power, + self._requested_power, + EVENT_TYPE_SOLAR_OPTIMIZER_CHANGE_POWER, + ) + else: + raise ConfigurationError( + f"Incorrect action_mode declaration for entity '{self._entity_id}'. Action_mode '{self._action_mode}' is not supported. Use one of {CONF_ACTION_MODES}" + ) + + self._current_power = self._requested_power + + async def activate(self, requested_power=None): + """Use this method to activate this ManagedDevice""" + return await self._apply_action(ACTION_ACTIVATE, requested_power) + + async def deactivate(self): + """Use this method to deactivate this ManagedDevice""" + return await self._apply_action(ACTION_DEACTIVATE, 0) + + async def change_requested_power(self, requested_power): + """Use this method to change the requested power of this ManagedDevice""" + return await self._apply_action(ACTION_CHANGE_POWER, requested_power) + + def reset_next_date_available(self, action_type): + """Incremente the next availability date to now + _duration_sec""" + if action_type == ACTION_ACTIVATE: + self._next_date_available = datetime.now(get_tz(self._hass)) + timedelta( + seconds=self._duration_sec + ) + else: + self._next_date_available = datetime.now(get_tz(self._hass)) + timedelta( + seconds=self._duration_stop_sec + ) + + _LOGGER.debug( + "Next availability date for %s is %s", self._name, self._next_date_available + ) + + def reset_next_date_available_power(self): + """Incremente the next availability date for power change to now + _duration_power_sec""" + self._next_date_available_power = datetime.now(get_tz(self._hass)) + timedelta( + seconds=self._duration_power_sec + ) + _LOGGER.debug( + "Next availability date for power change for %s is %s", + self._name, + self._next_date_available_power, + ) + + # def init_power(self, power: int): + # """Initialise current_power and requested_power to the given value""" + # _LOGGER.debug( + # "Initializing power for entity '%s' with %s value", self._name, power + # ) + # self._requested_power = self._current_power = power + + def set_current_power_with_device_state(self): + """Set the current power according to the real device state""" + if not self.is_active: + self._current_power = 0 + _LOGGER.debug( + "Set current_power to 0 for device %s cause not active", self._name + ) + return + + if not self._can_change_power: + self._current_power = self._power_max + _LOGGER.debug( + "Set current_power to %s for device %s cause active and not can_change_power", + self._current_power, + self._name, + ) + return + + amps = self._hass.states.get(self._power_entity_id) + if not amps or amps.state in [None, STATE_UNKNOWN, STATE_UNAVAILABLE]: + self._current_power = self._power_min + _LOGGER.debug( + "Set current_power to %s for device %s cause can_change_power but amps is %s", + self._current_power, + self._name, + amps, + ) + return + + self._current_power = round( + float(amps.state) * self._convert_power_divide_factor + ) + _LOGGER.debug( + "Set current_power to %s for device %s cause can_change_power and amps is %s", + self._current_power, + self._name, + amps.state, + ) + + def set_enable(self, enable: bool): + """Enable or disable the ManagedDevice for Solar Optimizer""" + _LOGGER.info("%s - Set enable=%s", self.name, enable) + self._enable = enable + self.publish_enable_state_change() + + @property + def is_enabled(self) -> bool: + """return true if the managed device is enabled for solar optimisation""" + return self._enable + + @property + def is_active(self) -> bool: + """Check if device is active by getting the underlying state of the device""" + result = self._check_active_template.async_render(context={}) + if result: + _LOGGER.debug("%s is active", self._name) + + return result + + @property + def is_usable(self) -> bool: + """A device is usable for optimisation if the check_usable_template returns true and + if the device is not waiting for the end of its cycle and if the battery_soc_threshold is >= battery_soc""" + + context = {} + now = datetime.now(get_tz(self._hass)) + result = self._check_usable_template.async_render(context) and ( + now >= self._next_date_available + or (self._can_change_power and now >= self._next_date_available_power) + ) + if not result: + _LOGGER.debug("%s is not usable", self._name) + + if result and self._battery_soc is not None and self._battery_soc_threshold is not None: + if self._battery_soc < self._battery_soc_threshold: + result = False + _LOGGER.debug("%s is not usable due to battery soc threshold (%s < %s)", self._name, self._battery_soc, self._battery_soc_threshold) + + return result + + @property + def is_waiting(self): + """A device is waiting if the device is waiting for the end of its cycle""" + now = datetime.now(get_tz(self._hass)) + result = now < self._next_date_available + + if result: + _LOGGER.debug("%s is waiting", self._name) + + return result + + @property + def name(self): + """The name of the ManagedDevice""" + return self._name + + @property + def unique_id(self): + """The id of the ManagedDevice""" + return self._unique_id + + @property + def power_max(self): + """The power max of the managed device""" + return self._power_max + + @property + def power_min(self): + """The power min of the managed device""" + return self._power_min + + @property + def power_step(self): + """The power step of the managed device""" + return self._power_step + + @property + def duration_sec(self) -> int: + """The duration a device is not available after a change of the managed device""" + return self._duration_sec + + @property + def duration_stop_sec(self) -> int: + """The duration a device is not available after a change of the managed device to stop""" + return self._duration_stop_sec + + @property + def duration_power_sec(self) -> int: + """The duration a device is not available after a change of the managed device for power change""" + return self._duration_power_sec + + @property + def entity_id(self) -> str: + """The entity_id of the device""" + return self._entity_id + + @property + def power_entity_id(self) -> str: + """The entity_id of the device which gives the current power""" + return self._power_entity_id + + @property + def current_power(self) -> int: + """The current_power of the device""" + return self._current_power + + @property + def requested_power(self) -> int: + """The requested_power of the device""" + return self._requested_power + + @property + def can_change_power(self) -> bool: + """true is the device can change its power""" + return self._can_change_power + + @property + def next_date_available(self) -> datetime: + """returns the next available date for state change""" + return self._next_date_available + + @property + def next_date_available_power(self) -> datetime: + """return the next available date for power change""" + return self._next_date_available_power + + @property + def convert_power_divide_factor(self) -> int: + """return""" + return self._convert_power_divide_factor + + def set_battery_soc(self, battery_soc): + """Define the battery soc. This is used with is_usable + to determine if the device is usable""" + self._battery_soc = battery_soc + + + def publish_enable_state_change(self) -> None: + """Publish an event when the state is changed""" + + self._hass.bus.fire( + event_type=EVENT_TYPE_SOLAR_OPTIMIZER_ENABLE_STATE_CHANGE, + event_data={ + "device_unique_id": self._unique_id, + "is_enabled": self.is_enabled, + "is_active": self.is_active, + "is_usable": self.is_usable, + "is_waiting": self.is_waiting, + }, + ) diff --git a/config/custom_components/solar_optimizer/manifest.json b/config/custom_components/solar_optimizer/manifest.json new file mode 100644 index 0000000..f97ae47 --- /dev/null +++ b/config/custom_components/solar_optimizer/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "solar_optimizer", + "name": "Solar Optimizer", + "codeowners": [ + "@jmcollin78" + ], + "config_flow": true, + "documentation": "https://github.com/jmcollin78/solar_optimizer", + "integration_type": "device", + "iot_class": "local_polling", + "issue_tracker": "https://github.com/jmcollin78/solar_optimizer/issues", + "quality_scale": "silver", + "version": "1.7.0" +} \ No newline at end of file diff --git a/config/custom_components/solar_optimizer/sensor.py b/config/custom_components/solar_optimizer/sensor.py new file mode 100644 index 0000000..6206436 --- /dev/null +++ b/config/custom_components/solar_optimizer/sensor.py @@ -0,0 +1,112 @@ +""" A sensor entity that holds the result of the recuit simule algorithm """ +import logging +from homeassistant.const import UnitOfPower +from homeassistant.core import callback, HomeAssistant +from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.components.sensor import ( + SensorEntity, + SensorDeviceClass, + SensorStateClass, +) +from homeassistant.config_entries import ConfigEntry + +from homeassistant.helpers.entity_platform import ( + AddEntitiesCallback, +) + + +from .const import DOMAIN +from .coordinator import SolarOptimizerCoordinator + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback +) -> None: + """Setup the entries of type Sensor""" + + # Sets the config entries values to SolarOptimizer coordinator + coordinator: SolarOptimizerCoordinator = hass.data[DOMAIN]["coordinator"] + + entity1 = SolarOptimizerSensorEntity(coordinator, hass, "best_objective") + entity2 = SolarOptimizerSensorEntity(coordinator, hass, "total_power") + entity3 = SolarOptimizerSensorEntity(coordinator, hass, "power_production") + entity4 = SolarOptimizerSensorEntity(coordinator, hass, "power_production_brut") + entity5 = SolarOptimizerSensorEntity(coordinator, hass, "battery_soc") + + async_add_entities([entity1, entity2, entity3, entity4, entity5], False) + + await coordinator.configure(entry) + + +class SolarOptimizerSensorEntity(CoordinatorEntity, SensorEntity): + """The entity holding the algorithm calculation""" + + def __init__(self, coordinator, hass, idx): + super().__init__(coordinator, context=idx) + self._hass = hass + self.idx = idx + self._attr_name = idx + self._attr_unique_id = "solar_optimizer_" + idx + + self._attr_native_value = None + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + if ( + not self.coordinator + or not self.coordinator.data + or (value := self.coordinator.data.get(self.idx)) == None + ): + _LOGGER.debug("No coordinator found or no data...") + return + + self._attr_native_value = value + self.async_write_ha_state() + + @property + def device_info(self): + # Retournez des informations sur le périphérique associé à votre entité + return { + "identifiers": {(DOMAIN, "solar_optimizer_device")}, + "name": "Solar Optimizer", + # Autres attributs du périphérique ici + } + + @property + def icon(self) -> str | None: + if self.idx == "best_objective": + return "mdi:bullseye-arrow" + elif self.idx == "total_power": + return "mdi:flash" + elif self.idx == "battery_soc": + return "mdi:battery" + else: + return "mdi:solar-power-variant" + + @property + def device_class(self) -> SensorDeviceClass | None: + if self.idx == "best_objective": + return SensorDeviceClass.MONETARY + elif self.idx == "battery_soc": + return SensorDeviceClass.BATTERY + else: + return SensorDeviceClass.POWER + + @property + def state_class(self) -> SensorStateClass | None: + if self.idx == "best_objective": + return SensorStateClass.TOTAL + else: + return SensorStateClass.MEASUREMENT + + @property + def native_unit_of_measurement(self) -> str | None: + if self.idx == "best_objective": + return "€" + elif self.idx == "battery_soc": + return "%" + else: + return UnitOfPower.WATT diff --git a/config/custom_components/solar_optimizer/services.yaml b/config/custom_components/solar_optimizer/services.yaml new file mode 100644 index 0000000..b782340 --- /dev/null +++ b/config/custom_components/solar_optimizer/services.yaml @@ -0,0 +1,3 @@ +reload: + name: Reload + description: Reload Solar Optimizer configuration \ No newline at end of file diff --git a/config/custom_components/solar_optimizer/simulated_annealing_algo.py b/config/custom_components/solar_optimizer/simulated_annealing_algo.py new file mode 100644 index 0000000..0c4fd24 --- /dev/null +++ b/config/custom_components/solar_optimizer/simulated_annealing_algo.py @@ -0,0 +1,384 @@ +""" The Simulated Annealing (recuit simulé) algorithm""" +import logging +import random +import math +import copy + +from .managed_device import ManagedDevice + +_LOGGER = logging.getLogger(__name__) + +DEBUG = False + + +class SimulatedAnnealingAlgorithm: + """The class which implemenets the Simulated Annealing algorithm""" + + # Paramètres de l'algorithme de recuit simulé + _temperature_initiale: float = 1000 + _temperature_minimale: float = 0.1 + _facteur_refroidissement: float = 0.95 + _nombre_iterations: float = 1000 + _equipements: list[ManagedDevice] + _puissance_totale_eqt_initiale: float + _cout_achat: float = 15 # centimes + _cout_revente: float = 10 # centimes + _taxe_revente: float = 0.13 # pourcentage + _consommation_net: float + _production_solaire: float + + def __init__( + self, + initial_temp: float, + min_temp: float, + cooling_factor: float, + max_iteration_number: int, + ): + """Initialize the algorithm with values""" + self._temperature_initiale = initial_temp + self._temperature_minimale = min_temp + self._facteur_refroidissement = cooling_factor + self._nombre_iterations = max_iteration_number + _LOGGER.info( + "Initializing the SimulatedAnnealingAlgorithm with initial_temp=%.2f min_temp=%.2f cooling_factor=%.2f max_iterations_number=%d", + self._temperature_initiale, + self._temperature_minimale, + self._facteur_refroidissement, + self._nombre_iterations, + ) + + def recuit_simule( + self, + devices: list[ManagedDevice], + power_consumption: float, + solar_power_production: float, + sell_cost: float, + buy_cost: float, + sell_tax_percent: float, + battery_soc: float + ): + """The entrypoint of the algorithm: + You should give: + - devices: a list of ManagedDevices. devices that are is_usable false are not taken into account + - power_consumption: the current power consumption. Can be negeative if power is given back to grid + - solar_power_production: the solar production power + - sell_cost: the sell cost of energy + - buy_cost: the buy cost of energy + - sell_tax_percent: a sell taxe applied to sell energy (a percentage) + + In return you will have: + - best_solution: a list of object in whitch name, power_max and state are set, + - best_objectif: the measure of the objective for that solution, + - total_power_consumption: the total of power consumption for all equipments which should be activated (state=True) + """ + if ( + len(devices) <= 0 # pylint: disable=too-many-boolean-expressions + or power_consumption is None + or solar_power_production is None + or sell_cost is None + or buy_cost is None + or sell_tax_percent is None + ): + _LOGGER.info( + "Not all informations are available for Simulated Annealign algorithm to work. Calculation is abandoned" + ) + return [], -1, -1 + + _LOGGER.debug( + "Calling recuit_simule with power_consumption=%.2f, solar_power_production=%.2f sell_cost=%.2f, buy_cost=%.2f, tax=%.2f%% devices=%s", + power_consumption, + solar_power_production, + sell_cost, + buy_cost, + sell_tax_percent, + devices, + ) + self._cout_achat = buy_cost + self._cout_revente = sell_cost + self._taxe_revente = sell_tax_percent + self._consommation_net = power_consumption + self._production_solaire = solar_power_production + + self._equipements = [] + for _, device in enumerate(devices): + if not device.is_enabled: + _LOGGER.debug("%s is disabled. Forget it", device.name) + continue + + device.set_battery_soc(battery_soc) + usable = device.is_usable + waiting = device.is_waiting + # Force deactivation if active, not usable and not waiting + force_state = ( + False + if device.is_active and not usable and not waiting + else device.is_active + ) + self._equipements.append( + { + "power_max": device.power_max, + "power_min": device.power_min, + "power_step": device.power_step, + "current_power": device.current_power, # if force_state else 0, + # Initial Requested power is the current power if usable + "requested_power": device.current_power, # if force_state else 0, + "name": device.name, + "state": force_state, + "is_usable": device.is_usable, + "is_waiting": waiting, + "can_change_power": device.can_change_power, + } + ) + if DEBUG: + _LOGGER.debug("enabled _equipements are: %s", self._equipements) + + # Générer une solution initiale + solution_actuelle = self.generer_solution_initiale(self._equipements) + meilleure_solution = solution_actuelle + meilleure_objectif = self.calculer_objectif(solution_actuelle) + temperature = self._temperature_initiale + + for _ in range(self._nombre_iterations): + # Générer un voisin + objectif_actuel = self.calculer_objectif(solution_actuelle) + if DEBUG: + _LOGGER.debug("Objectif actuel : %.2f", objectif_actuel) + + voisin = self.permuter_equipement(solution_actuelle) + + # Calculer les objectifs pour la solution actuelle et le voisin + objectif_voisin = self.calculer_objectif(voisin) + if DEBUG: + _LOGGER.debug("Objectif voisin : %2.f", objectif_voisin) + + # Accepter le voisin si son objectif est meilleur ou si la consommation totale n'excède pas la production solaire + if objectif_voisin < objectif_actuel: + _LOGGER.debug("---> On garde l'objectif voisin") + solution_actuelle = voisin + if objectif_voisin < self.calculer_objectif(meilleure_solution): + _LOGGER.debug("---> C'est la meilleure jusque là") + meilleure_solution = voisin + meilleure_objectif = objectif_voisin + else: + # Accepter le voisin avec une certaine probabilité + probabilite = math.exp( + (objectif_actuel - objectif_voisin) / temperature + ) + if (seuil := random.random()) < probabilite: + solution_actuelle = voisin + if DEBUG: + _LOGGER.debug( + "---> On garde l'objectif voisin car seuil (%.2f) inférieur à proba (%.2f)", + seuil, + probabilite, + ) + else: + if DEBUG: + _LOGGER.debug("--> On ne prend pas") + + # Réduire la température + temperature *= self._facteur_refroidissement + if DEBUG: + _LOGGER.debug(" !! Temperature %.2f", temperature) + if temperature < self._temperature_minimale: + break + + return ( + meilleure_solution, + meilleure_objectif, + self.consommation_equipements(meilleure_solution), + ) + + def calculer_objectif(self, solution) -> float: + """Calcul de l'objectif : minimiser le surplus de production solaire + rejets = 0 if consommation_net >=0 else -consommation_net + consommation_solaire = min(production_solaire, production_solaire - rejets) + consommation_totale = consommation_net + consommation_solaire + """ + + puissance_totale_eqt = self.consommation_equipements(solution) + diff_puissance_totale_eqt = ( + puissance_totale_eqt - self._puissance_totale_eqt_initiale + ) + + new_consommation_net = self._consommation_net + diff_puissance_totale_eqt + new_rejets = 0 if new_consommation_net >= 0 else -new_consommation_net + new_import = 0 if new_consommation_net < 0 else new_consommation_net + new_consommation_solaire = min( + self._production_solaire, self._production_solaire - new_rejets + ) + new_consommation_totale = ( + new_consommation_net + new_rejets + ) + new_consommation_solaire + if DEBUG: + _LOGGER.debug( + "Objectif : cette solution ajoute %.3fW a la consommation initial. Nouvelle consommation nette=%.3fW. Nouveaux rejets=%.3fW. Nouvelle conso totale=%.3fW", + diff_puissance_totale_eqt, + new_consommation_net, + new_rejets, + new_consommation_totale, + ) + + cout_revente_impose = self._cout_revente * (1.0 - self._taxe_revente / 100.0) + coef_import = (self._cout_achat) / (self._cout_achat + cout_revente_impose) + coef_rejets = (cout_revente_impose) / (self._cout_achat + cout_revente_impose) + + return coef_import * new_import + coef_rejets * new_rejets + + def generer_solution_initiale(self, solution): + """Generate the initial solution (which is the solution given in argument) and calculate the total initial power""" + self._puissance_totale_eqt_initiale = self.consommation_equipements(solution) + return copy.deepcopy(solution) + + def consommation_equipements(self, solution): + """The total power consumption for all active equipement""" + return sum( + equipement["requested_power"] + for _, equipement in enumerate(solution) + if equipement["state"] + ) + + def calculer_new_power( + self, current_power, power_step, power_min, power_max, can_switch_off + ): + """Calcul une nouvelle puissance""" + choices = [] + if current_power > power_min or can_switch_off: + choices.append(-1) + if current_power < power_max: + choices.append(1) + + if len(choices) <= 0: + # No changes + return current_power + + power_add = random.choice(choices) * power_step + _LOGGER.debug("Adding %d power to current_power (%d)", power_add, current_power) + requested_power = current_power + power_add + _LOGGER.debug("New requested_power is %s", requested_power) + return requested_power + # if requested_power < power_min: + # deactivate the equipment + # requested_power = 0 + # elif requested_power > power_max: + # Do nothing + # requested_power = current_power + + def permuter_equipement(self, solution): + """Permuter le state d'un equipement eau hasard""" + voisin = copy.deepcopy(solution) + + usable = [eqt for eqt in voisin if eqt["is_usable"]] + + if len(usable) <= 0: + return voisin + + eqt = random.choice(usable) + + # name = eqt["name"] + state = eqt["state"] + can_change_power = eqt["can_change_power"] + is_waiting = eqt["is_waiting"] + + # Current power is the last requested_power + current_power = eqt.get("requested_power") + power_max = eqt.get("power_max") + power_step = eqt.get("power_step") + if can_change_power: + power_min = eqt.get("power_min") + else: + # If power is not manageable, min = max + power_min = power_max + + # On veut gérer le is_waiting qui interdit d'allumer ou éteindre un eqt usable. + # On veut pouvoir changer la puissance si l'eqt est déjà allumé malgré qu'il soit waiting. + # Usable veut dire qu'on peut l'allumer/éteindre OU qu'on peut changer la puissance + + # if not can_change_power and is_waiting: + # -> on ne fait rien (mais ne devrait pas arriver car il ne serait pas usable dans ce cas) + # + # if state and can_change_power and is_waiting: + # -> change power mais sans l'éteindre (requested_power >= power_min) + # + # if state and can_change_power and not is_waiting: + # -> change power avec extinction possible + # + # if not state and not is_waiting + # -> allumage + # + # if state and not is_waiting + # -> extinction + # + if (not can_change_power and is_waiting) or ( + not state and can_change_power and is_waiting + ): + _LOGGER.debug("not can_change_power and is_waiting -> do nothing") + return voisin + + if state and can_change_power and is_waiting: + # calculated a new power but do not switch off (because waiting) + requested_power = self.calculer_new_power( + current_power, power_step, power_min, power_max, False + ) + assert ( + requested_power > 0 + ), "Requested_power should be > 0 because is_waiting is True" + + elif state and can_change_power and not is_waiting: + # change power and accept switching off + requested_power = self.calculer_new_power( + current_power, power_step, power_min, power_max, True + ) + if requested_power < power_min: + # deactivate the equipment + eqt["state"] = False + requested_power = 0 + + elif not state and not is_waiting: + # Allumage + eqt["state"] = not state + requested_power = power_min + + elif state and not is_waiting: + # Extinction + eqt["state"] = not state + requested_power = 0 + + elif "requested_power" not in locals(): + _LOGGER.error("We should not be there. eqt=%s", eqt) + assert False, "Requested power n'a pas été calculé. Ce n'est pas normal" + + eqt["requested_power"] = requested_power + + # old code that was working + # if not state or not can_change_power: + # eqt["state"] = not state + # # We always start at the min power + # eqt["requested_power"] = power_min + # else: + # _LOGGER.debug("Managing a can_change_power eqt which is already Activated") + # # Deactivate eqt or change power + # power_add = random.choice([-1, 1]) * power_step + # _LOGGER.debug( + # "Adding %d power to current_power (%d)", power_add, current_power + # ) + # requested_power = current_power + power_add + # if requested_power < power_min: + # # deactivate the equipment + # eqt["state"] = False + # requested_power = 0 + # elif requested_power > power_max: + # # Do nothing + # requested_power = current_power + # _LOGGER.debug("New requested_power is %s for eqt %s", requested_power, name) + # # Update the solution with current_power and + # eqt["requested_power"] = requested_power + + if DEBUG: + _LOGGER.debug( + " -- On permute %s puissance max de %.2f. Il passe à %s", + eqt["name"], + eqt["requested_power"], + eqt["state"], + ) + return voisin diff --git a/config/custom_components/solar_optimizer/strings.json b/config/custom_components/solar_optimizer/strings.json new file mode 100644 index 0000000..563c8fa --- /dev/null +++ b/config/custom_components/solar_optimizer/strings.json @@ -0,0 +1,61 @@ +{ + "title": "solar_optimizer", + "config": { + "flow_title": "Solar Optimizer configuration", + "step": { + "user": { + "title": "General parameters", + "description": "Give the general parameters", + "data": { + "refresh_period_sec": "Refresh period", + "power_consumption_entity_id": "Net power consumption", + "power_production_entity_id": "Solar power production", + "sell_cost_entity_id": "Energy sell price", + "buy_cost_entity_id": "Energy buy price", + "sell_tax_percent_entity_id": "Sell taxe percent", + "smooth_production": "Smooth the solar production", + "battery_soc_entity_id": "Battery soc" + }, + "data_description": { + "refresh_period_sec": "Refresh period in seconds. Warning heavy calculs are done at each period. Don't refresh to often", + "power_consumption_entity_id": "the entity_id of the net power consumption sensor. Net power should be negative if power is exported to grid.", + "power_production_entity_id": "the entity_id of the solar power production sensor.", + "sell_cost_entity_id": "The entity_id which holds the current energy sell price.", + "buy_cost_entity_id": "The entity_id which holds the current energy buy price.", + "sell_tax_percent_entity_id": "The energy resell tax percent (0 to 100)", + "smooth_production": "If checked, the solar production will be smoothed to avoid hard variation", + "battery_soc_entity_id": "Battery state of charge in %. If you don't have battery, keep it empty" + } + } + } + }, + "options": { + "flow_title": "Solar Optimizer options configuration", + "step": { + "init": { + "title": "General parameters", + "description": "Give the general parameters", + "data": { + "refresh_period_sec": "Refresh period", + "power_consumption_entity_id": "Net power consumption", + "power_production_entity_id": "Solar power production", + "sell_cost_entity_id": "Energy sell price", + "buy_cost_entity_id": "Energy buy price", + "sell_tax_percent_entity_id": "Sell taxe percent", + "smooth_production": "Smooth the solar production", + "battery_soc_entity_id": "Battery soc" + }, + "data_description": { + "refresh_period_sec": "Refresh period in seconds. Warning heavy calculs are done at each period. Don't refresh to often", + "power_consumption_entity_id": "the entity_id of the net power consumption sensor. Net power should be negative if power is exported to grid.", + "power_production_entity_id": "the entity_id of the solar power production sensor.", + "sell_cost_entity_id": "The entity_id which holds the current energy sell price.", + "buy_cost_entity_id": "The entity_id which holds the current energy buy price.", + "sell_tax_percent_entity_id": "The energy resell tax percent (0 to 100)", + "smooth_production": "If checked, the solar production will be smoothed to avoid hard variation", + "battery_soc_entity_id": "Battery state of charge in %. If you don't have battery, keep it empty" + } + } + } + } +} \ No newline at end of file diff --git a/config/custom_components/solar_optimizer/switch.py b/config/custom_components/solar_optimizer/switch.py new file mode 100644 index 0000000..50eff94 --- /dev/null +++ b/config/custom_components/solar_optimizer/switch.py @@ -0,0 +1,337 @@ +""" A bonary sensor entity that holds the state of each managed_device """ +import logging +from datetime import datetime +from typing import Any + +from homeassistant.const import STATE_UNAVAILABLE, STATE_UNKNOWN, STATE_ON +from homeassistant.core import callback, HomeAssistant, State, Event +from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.components.switch import ( + SwitchEntity, +) + +from homeassistant.helpers.entity_platform import ( + AddEntitiesCallback, +) + +from homeassistant.helpers.event import ( + async_track_state_change_event, +) + +from .const import ( + DOMAIN, + name_to_unique_id, + get_tz, + EVENT_TYPE_SOLAR_OPTIMIZER_ENABLE_STATE_CHANGE, +) +from .coordinator import SolarOptimizerCoordinator +from .managed_device import ManagedDevice + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry( + hass: HomeAssistant, _, async_add_entities: AddEntitiesCallback +) -> None: + """Setup the entries of type Binary sensor, one for each ManagedDevice""" + _LOGGER.debug("Calling switch.async_setup_entry") + + coordinator: SolarOptimizerCoordinator = hass.data[DOMAIN]["coordinator"] + + entities = [] + for _, device in enumerate(coordinator.devices): + entity = ManagedDeviceSwitch( + coordinator, + hass, + device.name, + name_to_unique_id(device.name), + device.entity_id, + ) + if entity is not None: + entities.append(entity) + + entity = ManagedDeviceEnable(hass, device) + if entity is not None: + entities.append(entity) + + async_add_entities(entities) + + +class ManagedDeviceSwitch(CoordinatorEntity, SwitchEntity): + """The entity holding the algorithm calculation""" + + _entity_component_unrecorded_attributes = ( + SwitchEntity._entity_component_unrecorded_attributes.union( + frozenset( + { + "is_enabled", + "is_active", + "is_waiting", + "is_usable", + "can_change_power", + "duration_sec", + "duration_power_sec", + "power_min", + "power_max", + "next_date_available", + "next_date_available_power", + "battery_soc_threshold", + "battery_soc", + } + ) + ) + ) + + def __init__(self, coordinator, hass, name, idx, entity_id): + _LOGGER.debug("Adding ManagedDeviceSwitch for %s", name) + super().__init__(coordinator, context=idx) + self._hass: HomeAssistant = hass + self.idx = idx + self._attr_name = "Solar Optimizer " + name + self._attr_unique_id = "solar_optimizer_" + idx + self._entity_id = entity_id + + # Try to get the state if it exists + device: ManagedDevice = None + if (device := coordinator.get_device_by_unique_id(self.idx)) is not None: + self._attr_is_on = device.is_active + else: + self._attr_is_on = None + + async def async_added_to_hass(self) -> None: + """The entity have been added to hass, listen to state change of the underlying entity""" + await super().async_added_to_hass() + + # Arme l'écoute de la première entité + listener_cancel = async_track_state_change_event( + self.hass, + [self._entity_id], + self._on_state_change, + ) + # desarme le timer lors de la destruction de l'entité + self.async_on_remove(listener_cancel) + + # desarme le timer lors de la destruction de l'entité + self.async_on_remove( + self._hass.bus.async_listen( + event_type=EVENT_TYPE_SOLAR_OPTIMIZER_ENABLE_STATE_CHANGE, + listener=self._on_enable_state_change, + ) + ) + + @callback + async def _on_enable_state_change(self, event: Event) -> None: + """Triggered when the ManagedDevice enable state have change""" + + # is it for me ? + if ( + not event.data + or (device_id := event.data.get("device_unique_id")) != self.idx + ): + return + + # search for coordinator and device + if not self.coordinator or not ( + device := self.coordinator.get_device_by_unique_id(device_id) + ): + return + + _LOGGER.info( + "Changing enabled state for %s to %s", device_id, device.is_enabled + ) + + self.update_custom_attributes(device) + self.async_write_ha_state() + + @callback + async def _on_state_change(self, event: Event) -> None: + """The entity have change its state""" + _LOGGER.info( + "Appel de on_state_change à %s avec l'event %s", datetime.now(), event + ) + + if not event.data: + return + + # search for coordinator and device + if not self.coordinator or not ( + device := self.coordinator.get_device_by_unique_id(self.idx) + ): + return + + new_state: State = event.data.get("new_state") + # old_state: State = event.data.get("old_state") + + if new_state is None or new_state.state in (STATE_UNAVAILABLE, STATE_UNKNOWN): + _LOGGER.debug("Pas d'état disponible. Evenement ignoré") + return + + # On recherche la date de l'event pour la stocker dans notre état + new_state = new_state.state == STATE_ON + if new_state == self._attr_is_on: + return + + self._attr_is_on = new_state + # On sauvegarde le nouvel état + self.update_custom_attributes(device) + self.async_write_ha_state() + + def update_custom_attributes(self, device): + """Add some custom attributes to the entity""" + current_tz = get_tz(self._hass) + self._attr_extra_state_attributes: dict(str, str) = { + "is_enabled": device.is_enabled, + "is_active": device.is_active, + "is_waiting": device.is_waiting, + "is_usable": device.is_usable, + "can_change_power": device.can_change_power, + "current_power": device.current_power, + "requested_power": device.requested_power, + "duration_sec": device.duration_sec, + "duration_power_sec": device.duration_power_sec, + "power_min": device.power_min, + "power_max": device.power_max, + "next_date_available": device.next_date_available.astimezone( + current_tz + ).isoformat(), + "next_date_available_power": device.next_date_available_power.astimezone( + current_tz + ).isoformat(), + "battery_soc_threshold": device._battery_soc_threshold, + "battery_soc": device._battery_soc, + } + + @callback + def _handle_coordinator_update(self) -> None: + """Handle updated data from the coordinator.""" + _LOGGER.debug("Calling _handle_coordinator_update for %s", self._attr_name) + + if not self.coordinator or not self.coordinator.data: + _LOGGER.debug("No coordinator found or no data...") + return + + device: ManagedDevice = self.coordinator.data.get(self.idx) + if not device: + # it is possible to not have device in coordinator update (if device is not enabled) + _LOGGER.debug("No device %s found ...", self.idx) + return + + self._attr_is_on = device.is_active + self.update_custom_attributes(device) + self.async_write_ha_state() + + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the entity on.""" + if not self.coordinator or not self.coordinator.data: + return + + _LOGGER.info("Turn_on Solar Optimizer switch %s", self._attr_name) + # search for coordinator and device + if not self.coordinator or not ( + device := self.coordinator.get_device_by_unique_id(self.idx) + ): + return + + if not self._attr_is_on: + await device.activate() + self._attr_is_on = True + self.update_custom_attributes(device) + self.async_write_ha_state() + + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the entity on.""" + if not self.coordinator or not self.coordinator.data: + return + + _LOGGER.info("Turn_on Solar Optimizer switch %s", self._attr_name) + # search for coordinator and device + if not self.coordinator or not ( + device := self.coordinator.get_device_by_unique_id(self.idx) + ): + return + + if self._attr_is_on: + await device.deactivate() + self._attr_is_on = False + self.update_custom_attributes(device) + self.async_write_ha_state() + + @property + def device_info(self): + # Retournez des informations sur le périphérique associé à votre entité + return { + "identifiers": {(DOMAIN, "solar_optimizer_device")}, + "name": "Solar Optimizer", + # Autres attributs du périphérique ici + } + + @property + def get_attr_extra_state_attributes(self): + """Get the extra state attributes for the entity""" + return self._attr_extra_state_attributes + + +class ManagedDeviceEnable(SwitchEntity, RestoreEntity): + """The that enables the ManagedDevice optimisation with""" + + _device: ManagedDevice + + def __init__(self, hass: HomeAssistant, device: ManagedDevice): + self._hass: HomeAssistant = hass + self._device = device + self._attr_name = "Enable Solar Optimizer " + device.name + self._attr_unique_id = "solar_optimizer_enable_" + name_to_unique_id( + device.name + ) + self._attr_is_on = True + + @property + def device_info(self): + # Retournez des informations sur le périphérique associé à votre entité + return { + "identifiers": {(DOMAIN, "solar_optimizer_device")}, + "name": "Solar Optimizer", + # Autres attributs du périphérique ici + } + + @property + def icon(self) -> str | None: + return "mdi:check" + + async def async_added_to_hass(self): + await super().async_added_to_hass() + + # Récupérer le dernier état sauvegardé de l'entité + last_state = await self.async_get_last_state() + + # Si l'état précédent existe, vous pouvez l'utiliser + if last_state is not None: + self._attr_is_on = last_state.state == "on" + else: + # Si l'état précédent n'existe pas, initialisez l'état comme vous le souhaitez + self._attr_is_on = True + + # this breaks the start of integration + self.update_device_enabled() + + @callback + async def async_turn_on(self, **kwargs: Any) -> None: + """Turn the entity on.""" + self._attr_is_on = True + self.async_write_ha_state() + self.update_device_enabled() + + @callback + async def async_turn_off(self, **kwargs: Any) -> None: + """Turn the entity off.""" + self._attr_is_on = False + self.async_write_ha_state() + self.update_device_enabled() + + def update_device_enabled(self) -> None: + """Update the device is enabled flag""" + if not self._device: + return + + self._device.set_enable(self._attr_is_on) diff --git a/config/custom_components/solar_optimizer/translations/en.json b/config/custom_components/solar_optimizer/translations/en.json new file mode 100644 index 0000000..563c8fa --- /dev/null +++ b/config/custom_components/solar_optimizer/translations/en.json @@ -0,0 +1,61 @@ +{ + "title": "solar_optimizer", + "config": { + "flow_title": "Solar Optimizer configuration", + "step": { + "user": { + "title": "General parameters", + "description": "Give the general parameters", + "data": { + "refresh_period_sec": "Refresh period", + "power_consumption_entity_id": "Net power consumption", + "power_production_entity_id": "Solar power production", + "sell_cost_entity_id": "Energy sell price", + "buy_cost_entity_id": "Energy buy price", + "sell_tax_percent_entity_id": "Sell taxe percent", + "smooth_production": "Smooth the solar production", + "battery_soc_entity_id": "Battery soc" + }, + "data_description": { + "refresh_period_sec": "Refresh period in seconds. Warning heavy calculs are done at each period. Don't refresh to often", + "power_consumption_entity_id": "the entity_id of the net power consumption sensor. Net power should be negative if power is exported to grid.", + "power_production_entity_id": "the entity_id of the solar power production sensor.", + "sell_cost_entity_id": "The entity_id which holds the current energy sell price.", + "buy_cost_entity_id": "The entity_id which holds the current energy buy price.", + "sell_tax_percent_entity_id": "The energy resell tax percent (0 to 100)", + "smooth_production": "If checked, the solar production will be smoothed to avoid hard variation", + "battery_soc_entity_id": "Battery state of charge in %. If you don't have battery, keep it empty" + } + } + } + }, + "options": { + "flow_title": "Solar Optimizer options configuration", + "step": { + "init": { + "title": "General parameters", + "description": "Give the general parameters", + "data": { + "refresh_period_sec": "Refresh period", + "power_consumption_entity_id": "Net power consumption", + "power_production_entity_id": "Solar power production", + "sell_cost_entity_id": "Energy sell price", + "buy_cost_entity_id": "Energy buy price", + "sell_tax_percent_entity_id": "Sell taxe percent", + "smooth_production": "Smooth the solar production", + "battery_soc_entity_id": "Battery soc" + }, + "data_description": { + "refresh_period_sec": "Refresh period in seconds. Warning heavy calculs are done at each period. Don't refresh to often", + "power_consumption_entity_id": "the entity_id of the net power consumption sensor. Net power should be negative if power is exported to grid.", + "power_production_entity_id": "the entity_id of the solar power production sensor.", + "sell_cost_entity_id": "The entity_id which holds the current energy sell price.", + "buy_cost_entity_id": "The entity_id which holds the current energy buy price.", + "sell_tax_percent_entity_id": "The energy resell tax percent (0 to 100)", + "smooth_production": "If checked, the solar production will be smoothed to avoid hard variation", + "battery_soc_entity_id": "Battery state of charge in %. If you don't have battery, keep it empty" + } + } + } + } +} \ No newline at end of file diff --git a/config/custom_components/solar_optimizer/translations/fr.json b/config/custom_components/solar_optimizer/translations/fr.json new file mode 100644 index 0000000..563c8fa --- /dev/null +++ b/config/custom_components/solar_optimizer/translations/fr.json @@ -0,0 +1,61 @@ +{ + "title": "solar_optimizer", + "config": { + "flow_title": "Solar Optimizer configuration", + "step": { + "user": { + "title": "General parameters", + "description": "Give the general parameters", + "data": { + "refresh_period_sec": "Refresh period", + "power_consumption_entity_id": "Net power consumption", + "power_production_entity_id": "Solar power production", + "sell_cost_entity_id": "Energy sell price", + "buy_cost_entity_id": "Energy buy price", + "sell_tax_percent_entity_id": "Sell taxe percent", + "smooth_production": "Smooth the solar production", + "battery_soc_entity_id": "Battery soc" + }, + "data_description": { + "refresh_period_sec": "Refresh period in seconds. Warning heavy calculs are done at each period. Don't refresh to often", + "power_consumption_entity_id": "the entity_id of the net power consumption sensor. Net power should be negative if power is exported to grid.", + "power_production_entity_id": "the entity_id of the solar power production sensor.", + "sell_cost_entity_id": "The entity_id which holds the current energy sell price.", + "buy_cost_entity_id": "The entity_id which holds the current energy buy price.", + "sell_tax_percent_entity_id": "The energy resell tax percent (0 to 100)", + "smooth_production": "If checked, the solar production will be smoothed to avoid hard variation", + "battery_soc_entity_id": "Battery state of charge in %. If you don't have battery, keep it empty" + } + } + } + }, + "options": { + "flow_title": "Solar Optimizer options configuration", + "step": { + "init": { + "title": "General parameters", + "description": "Give the general parameters", + "data": { + "refresh_period_sec": "Refresh period", + "power_consumption_entity_id": "Net power consumption", + "power_production_entity_id": "Solar power production", + "sell_cost_entity_id": "Energy sell price", + "buy_cost_entity_id": "Energy buy price", + "sell_tax_percent_entity_id": "Sell taxe percent", + "smooth_production": "Smooth the solar production", + "battery_soc_entity_id": "Battery soc" + }, + "data_description": { + "refresh_period_sec": "Refresh period in seconds. Warning heavy calculs are done at each period. Don't refresh to often", + "power_consumption_entity_id": "the entity_id of the net power consumption sensor. Net power should be negative if power is exported to grid.", + "power_production_entity_id": "the entity_id of the solar power production sensor.", + "sell_cost_entity_id": "The entity_id which holds the current energy sell price.", + "buy_cost_entity_id": "The entity_id which holds the current energy buy price.", + "sell_tax_percent_entity_id": "The energy resell tax percent (0 to 100)", + "smooth_production": "If checked, the solar production will be smoothed to avoid hard variation", + "battery_soc_entity_id": "Battery state of charge in %. If you don't have battery, keep it empty" + } + } + } + } +} \ No newline at end of file diff --git a/config/custom_components/versatile_thermostat/__init__.py b/config/custom_components/versatile_thermostat/__init__.py index e132735..f4a6970 100644 --- a/config/custom_components/versatile_thermostat/__init__.py +++ b/config/custom_components/versatile_thermostat/__init__.py @@ -13,6 +13,7 @@ from homeassistant.const import SERVICE_RELOAD, EVENT_HOMEASSISTANT_STARTED from homeassistant.config_entries import ConfigEntry, ConfigType from homeassistant.core import HomeAssistant, CoreState, callback +from homeassistant.helpers.service import async_register_admin_service from .base_thermostat import BaseThermostat @@ -115,7 +116,8 @@ async def async_setup( else: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, _async_startup_internal) - hass.helpers.service.async_register_admin_service( + async_register_admin_service( + hass, DOMAIN, SERVICE_RELOAD, _handle_reload, diff --git a/config/custom_components/versatile_thermostat/base_thermostat.py b/config/custom_components/versatile_thermostat/base_thermostat.py index 5ce03e2..fddf8f0 100644 --- a/config/custom_components/versatile_thermostat/base_thermostat.py +++ b/config/custom_components/versatile_thermostat/base_thermostat.py @@ -22,7 +22,6 @@ from homeassistant.components.climate import ClimateEntity from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.config_entries import ConfigEntry from homeassistant.helpers.device_registry import DeviceInfo, DeviceEntryType -from homeassistant.helpers.typing import EventType as HASSEventType from homeassistant.helpers.event import ( async_track_state_change_event, @@ -737,37 +736,37 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ) need_write_state = True - # try to acquire window entity state - if self._window_sensor_entity_id: - window_state = self.hass.states.get(self._window_sensor_entity_id) - if window_state and window_state.state not in ( - STATE_UNAVAILABLE, - STATE_UNKNOWN, - ): - self._window_state = window_state.state == STATE_ON - _LOGGER.debug( - "%s - Window state have been retrieved: %s", - self, - self._window_state, - ) - need_write_state = True + # try to acquire window entity state + if self._window_sensor_entity_id: + window_state = self.hass.states.get(self._window_sensor_entity_id) + if window_state and window_state.state not in ( + STATE_UNAVAILABLE, + STATE_UNKNOWN, + ): + self._window_state = window_state.state == STATE_ON + _LOGGER.debug( + "%s - Window state have been retrieved: %s", + self, + self._window_state, + ) + need_write_state = True - # try to acquire motion entity state - if self._motion_sensor_entity_id: - motion_state = self.hass.states.get(self._motion_sensor_entity_id) - if motion_state and motion_state.state not in ( - STATE_UNAVAILABLE, - STATE_UNKNOWN, - ): - self._motion_state = motion_state.state - _LOGGER.debug( - "%s - Motion state have been retrieved: %s", - self, - self._motion_state, - ) - # recalculate the right target_temp in activity mode - await self._async_update_motion_temp() - need_write_state = True + # try to acquire motion entity state + if self._motion_sensor_entity_id: + motion_state = self.hass.states.get(self._motion_sensor_entity_id) + if motion_state and motion_state.state not in ( + STATE_UNAVAILABLE, + STATE_UNKNOWN, + ): + self._motion_state = motion_state.state + _LOGGER.debug( + "%s - Motion state have been retrieved: %s", + self, + self._motion_state, + ) + # recalculate the right target_temp in activity mode + await self._async_update_motion_temp() + need_write_state = True if self._presence_on: # try to acquire presence entity state @@ -1377,11 +1376,19 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): if preset_mode == PRESET_POWER: return self._power_temp if preset_mode == PRESET_ACTIVITY: - motion_preset = ( - self._motion_preset - if self._motion_state == STATE_ON - else self._no_motion_preset - ) + if self._ac_mode and self._hvac_mode == HVACMode.COOL: + motion_preset = ( + self._motion_preset + PRESET_AC_SUFFIX + if self._motion_state == STATE_ON + else self._no_motion_preset + PRESET_AC_SUFFIX + ) + else: + motion_preset = ( + self._motion_preset + if self._motion_state == STATE_ON + else self._no_motion_preset + ) + if motion_preset in self._presets: return self._presets[motion_preset] else: @@ -1646,6 +1653,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): _LOGGER.debug("%s - Motion delay condition is satisfied", self) self._motion_state = new_state.state if self._attr_preset_mode == PRESET_ACTIVITY: + new_preset = ( self._motion_preset if self._motion_state == STATE_ON @@ -1658,6 +1666,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ) # We do not change the preset which is kept to ACTIVITY but only the target_temperature # We take the presence into account + await self._async_internal_set_temperature( self.find_preset_temp(new_preset) ) @@ -1780,7 +1789,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): _LOGGER.error("Unable to update external temperature from sensor: %s", ex) @callback - async def _async_power_changed(self, event: HASSEventType[EventStateChangedData]): + async def _async_power_changed(self, event: Event[EventStateChangedData]): """Handle power changes.""" _LOGGER.debug("Thermostat %s - Receive new Power event", self.name) _LOGGER.debug(event) @@ -1806,9 +1815,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): _LOGGER.error("Unable to update current_power from sensor: %s", ex) @callback - async def _async_max_power_changed( - self, event: HASSEventType[EventStateChangedData] - ): + async def _async_max_power_changed(self, event: Event[EventStateChangedData]): """Handle power max changes.""" _LOGGER.debug("Thermostat %s - Receive new Power Max event", self.name) _LOGGER.debug(event) @@ -1833,9 +1840,7 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): _LOGGER.error("Unable to update current_power from sensor: %s", ex) @callback - async def _async_presence_changed( - self, event: HASSEventType[EventStateChangedData] - ): + async def _async_presence_changed(self, event: Event[EventStateChangedData]): """Handle presence changes.""" new_state = event.data.get("new_state") _LOGGER.info( @@ -1896,16 +1901,23 @@ class BaseThermostat(ClimateEntity, RestoreEntity, Generic[T]): ): return + new_preset = ( + self._motion_preset + if self._motion_state == STATE_ON + else self._no_motion_preset + ) + _LOGGER.info( + "%s - Motion condition have changes. New preset temp will be %s", + self, + new_preset, + ) + # We do not change the preset which is kept to ACTIVITY but only the target_temperature + # We take the presence into account + await self._async_internal_set_temperature( - self._presets.get( - ( - self._motion_preset - if self._motion_state == STATE_ON - else self._no_motion_preset - ), - None, - ) + self.find_preset_temp(new_preset) ) + _LOGGER.debug( "%s - regarding motion, target_temp have been set to %.2f", self, diff --git a/config/custom_components/versatile_thermostat/keep_alive.py b/config/custom_components/versatile_thermostat/keep_alive.py index 1457cdf..e5fdc68 100644 --- a/config/custom_components/versatile_thermostat/keep_alive.py +++ b/config/custom_components/versatile_thermostat/keep_alive.py @@ -10,6 +10,7 @@ the keep_alive setting of Home Assistant's Generic Thermostat integration: import logging from collections.abc import Awaitable, Callable from datetime import timedelta, datetime +from time import monotonic from homeassistant.core import HomeAssistant, CALLBACK_TYPE from homeassistant.helpers.event import async_track_time_interval @@ -18,6 +19,79 @@ from homeassistant.helpers.event import async_track_time_interval _LOGGER = logging.getLogger(__name__) +class BackoffTimer: + """Exponential backoff timer with a non-blocking polling-style implementation. + + Usage example: + timer = BackoffTimer(multiplier=1.5, upper_limit_sec=600) + while some_condition: + if timer.is_ready(): + do_something() + """ + + def __init__( + self, + *, + multiplier=2.0, + lower_limit_sec=30, + upper_limit_sec=86400, + initially_ready=True, + ): + """Initialize a BackoffTimer instance. + + Args: + multiplier (int, optional): Period multiplier applied when is_ready() is True. + lower_limit_sec (int, optional): Initial backoff period in seconds. + upper_limit_sec (int, optional): Maximum backoff period in seconds. + initially_ready (bool, optional): Whether is_ready() should return True the + first time it is called, or after a call to reset(). + """ + self._multiplier = multiplier + self._lower_limit_sec = lower_limit_sec + self._upper_limit_sec = upper_limit_sec + self._initially_ready = initially_ready + + self._timestamp = 0 + self._period_sec = self._lower_limit_sec + + @property + def in_progress(self) -> bool: + """Whether the backoff timer is in progress (True after a call to is_ready()).""" + return bool(self._timestamp) + + def reset(self): + """Reset a BackoffTimer instance.""" + self._timestamp = 0 + self._period_sec = self._lower_limit_sec + + def is_ready(self) -> bool: + """Check whether an exponentially increasing period of time has passed. + + Whenever is_ready() returns True, the timer period is multiplied so that + it takes longer until is_ready() returns True again. + Returns: + bool: True if enough time has passed since one of the following events, + in relation to an instance of this class: + - The last time when this method returned True, if it ever did. + - Or else, when this method was first called after a call to reset(). + - Or else, when this method was first called. + False otherwise. + """ + now = monotonic() + if self._timestamp == 0: + self._timestamp = now + return self._initially_ready + elif now - self._timestamp >= self._period_sec: + self._timestamp = now + self._period_sec = max( + self._lower_limit_sec, + min(self._upper_limit_sec, self._period_sec * self._multiplier), + ) + return True + + return False + + class IntervalCaller: """Repeatedly call a given async action function at a given regular interval. @@ -28,6 +102,7 @@ class IntervalCaller: self._hass = hass self._interval_sec = interval_sec self._remove_handle: CALLBACK_TYPE | None = None + self.backoff_timer = BackoffTimer() @property def interval_sec(self) -> float: diff --git a/config/custom_components/versatile_thermostat/manifest.json b/config/custom_components/versatile_thermostat/manifest.json index d4c7b57..976fb18 100644 --- a/config/custom_components/versatile_thermostat/manifest.json +++ b/config/custom_components/versatile_thermostat/manifest.json @@ -14,6 +14,6 @@ "quality_scale": "silver", "requirements": [], "ssdp": [], - "version": "6.2.3", + "version": "6.2.9", "zeroconf": [] -} \ No newline at end of file +} diff --git a/config/custom_components/versatile_thermostat/number.py b/config/custom_components/versatile_thermostat/number.py index 117a607..b6236a3 100644 --- a/config/custom_components/versatile_thermostat/number.py +++ b/config/custom_components/versatile_thermostat/number.py @@ -283,7 +283,7 @@ class CentralConfigTemperatureNumber( self.entity_id = f"{NUMBER_DOMAIN}.{slugify(name)}_preset_{preset_name}" self._attr_unique_id = f"central_configuration_preset_{preset_name}" self._attr_device_class = NumberDeviceClass.TEMPERATURE - self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + self._attr_native_unit_of_measurement = hass.config.units.temperature_unit self._attr_native_step = entry_infos.get(CONF_STEP_TEMPERATURE, 0.5) self._attr_native_min_value = entry_infos.get(CONF_TEMP_MIN) @@ -371,7 +371,7 @@ class CentralConfigTemperatureNumber( # TODO Kelvin ? It seems not because all internal values are stored in # ° Celsius but only the render in front can be in °K depending on the # user configuration. - return UnitOfTemperature.CELSIUS + return self.hass.config.units.temperature_unit class TemperatureNumber( # pylint: disable=abstract-method @@ -400,7 +400,7 @@ class TemperatureNumber( # pylint: disable=abstract-method self._attr_unique_id = f"{self._device_name}_preset_{preset_name}" self._attr_device_class = NumberDeviceClass.TEMPERATURE - self._attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + self._attr_native_unit_of_measurement = hass.config.units.temperature_unit self._has_central_main_attributes = entry_infos.get( CONF_USE_MAIN_CENTRAL_CONFIG, False @@ -498,7 +498,7 @@ class TemperatureNumber( # pylint: disable=abstract-method def native_unit_of_measurement(self) -> str | None: """The unit of measurement""" if not self.my_climate: - return UnitOfTemperature.CELSIUS + return self.hass.config.units.temperature_unit return self.my_climate.temperature_unit def init_min_max_step(self, entry_infos=None): diff --git a/config/custom_components/versatile_thermostat/prop_algorithm.py b/config/custom_components/versatile_thermostat/prop_algorithm.py index 3cd4aee..c129262 100644 --- a/config/custom_components/versatile_thermostat/prop_algorithm.py +++ b/config/custom_components/versatile_thermostat/prop_algorithm.py @@ -70,9 +70,9 @@ class PropAlgorithm: if hvac_mode == HVACMode.COOL: delta_temp = current_temp - target_temp delta_ext_temp = ( - ext_current_temp + ext_current_temp - target_temp if ext_current_temp is not None - else 0 - target_temp + else 0 ) else: delta_temp = target_temp - current_temp diff --git a/config/custom_components/versatile_thermostat/sensor.py b/config/custom_components/versatile_thermostat/sensor.py index e917b9f..f3018fc 100644 --- a/config/custom_components/versatile_thermostat/sensor.py +++ b/config/custom_components/versatile_thermostat/sensor.py @@ -570,7 +570,7 @@ class RegulatedTemperatureSensor(VersatileThermostatBaseEntity, SensorEntity): @property def native_unit_of_measurement(self) -> str | None: if not self.my_climate: - return UnitOfTemperature.CELSIUS + return self.hass.config.units.temperature_unit return self.my_climate.temperature_unit @property @@ -621,7 +621,7 @@ class EMATemperatureSensor(VersatileThermostatBaseEntity, SensorEntity): @property def native_unit_of_measurement(self) -> str | None: if not self.my_climate: - return UnitOfTemperature.CELSIUS + return self.hass.config.units.temperature_unit return self.my_climate.temperature_unit @property diff --git a/config/custom_components/versatile_thermostat/thermostat_climate.py b/config/custom_components/versatile_thermostat/thermostat_climate.py index de2042f..6675ad7 100644 --- a/config/custom_components/versatile_thermostat/thermostat_climate.py +++ b/config/custom_components/versatile_thermostat/thermostat_climate.py @@ -3,13 +3,12 @@ import logging from datetime import timedelta, datetime -from homeassistant.core import HomeAssistant, State, callback +from homeassistant.core import Event, HomeAssistant, State, callback from homeassistant.helpers.event import ( async_track_state_change_event, async_track_time_interval, EventStateChangedData, ) -from homeassistant.helpers.typing import EventType as HASSEventType from homeassistant.components.climate import ( HVACAction, HVACMode, @@ -168,11 +167,17 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): _LOGGER.info("%s - regulation calculation will be done", self) + # use _attr_target_temperature_step to round value if _auto_regulation_dtemp is equal to 0 + regulation_step = self._auto_regulation_dtemp if self._auto_regulation_dtemp else self._attr_target_temperature_step + _LOGGER.debug("%s - usage of regulation_step: %.2f ", + self, + regulation_step) + new_regulated_temp = round_to_nearest( self._regulation_algo.calculate_regulated_temperature( self.current_temperature, self._cur_ext_temp ), - self._auto_regulation_dtemp, + regulation_step, ) dtemp = new_regulated_temp - self._regulated_target_temp @@ -216,7 +221,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): ): offset_temp = device_temp - self.current_temperature - target_temp = round_to_nearest(self.regulated_target_temp + offset_temp, self._auto_regulation_dtemp) + target_temp = round_to_nearest(self.regulated_target_temp + offset_temp, regulation_step) _LOGGER.debug( "%s - The device offset temp for regulation is %.2f - internal temp is %.2f. New target is %.2f", @@ -594,7 +599,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): ) @callback - async def _async_climate_changed(self, event: HASSEventType[EventStateChangedData]): + async def _async_climate_changed(self, event: Event[EventStateChangedData]): """Handle unerdlying climate state changes. This method takes the underlying values and update the VTherm with them. To avoid loops (issues #121 #101 #95 #99), we discard the event if it is received @@ -894,10 +899,7 @@ class ThermostatOverClimate(BaseThermostat[UnderlyingClimate]): @property def temperature_unit(self) -> str: """Return the unit of measurement.""" - if self.underlying_entity(0): - return self.underlying_entity(0).temperature_unit - - return self._unit + return self.hass.config.units.temperature_unit @property def supported_features(self): diff --git a/config/custom_components/versatile_thermostat/thermostat_switch.py b/config/custom_components/versatile_thermostat/thermostat_switch.py index cdfd8b9..4f760bc 100644 --- a/config/custom_components/versatile_thermostat/thermostat_switch.py +++ b/config/custom_components/versatile_thermostat/thermostat_switch.py @@ -2,12 +2,11 @@ """ A climate over switch classe """ import logging -from homeassistant.core import callback +from homeassistant.core import Event, callback from homeassistant.helpers.event import ( async_track_state_change_event, EventStateChangedData, ) -from homeassistant.helpers.typing import EventType as HASSEventType from homeassistant.components.climate import HVACMode from .const import ( @@ -212,7 +211,7 @@ class ThermostatOverSwitch(BaseThermostat[UnderlyingSwitch]): ) @callback - def _async_switch_changed(self, event: HASSEventType[EventStateChangedData]): + def _async_switch_changed(self, event: Event[EventStateChangedData]): """Handle heater switch state changes.""" new_state = event.data.get("new_state") old_state = event.data.get("old_state") diff --git a/config/custom_components/versatile_thermostat/thermostat_valve.py b/config/custom_components/versatile_thermostat/thermostat_valve.py index f560937..1af53db 100644 --- a/config/custom_components/versatile_thermostat/thermostat_valve.py +++ b/config/custom_components/versatile_thermostat/thermostat_valve.py @@ -8,8 +8,7 @@ from homeassistant.helpers.event import ( async_track_time_interval, EventStateChangedData, ) -from homeassistant.helpers.typing import EventType as HASSEventType -from homeassistant.core import HomeAssistant, callback +from homeassistant.core import Event, HomeAssistant, callback from homeassistant.components.climate import HVACMode from .base_thermostat import BaseThermostat, ConfigData @@ -149,7 +148,7 @@ class ThermostatOverValve(BaseThermostat[UnderlyingValve]): # pylint: disable=a ) @callback - async def _async_valve_changed(self, event: HASSEventType[EventStateChangedData]): + async def _async_valve_changed(self, event: Event[EventStateChangedData]): """Handle unerdlying valve state changes. This method just log the change. It changes nothing to avoid loops. """ diff --git a/config/custom_components/versatile_thermostat/translations/sk.json b/config/custom_components/versatile_thermostat/translations/sk.json index 622d850..6aa50b0 100644 --- a/config/custom_components/versatile_thermostat/translations/sk.json +++ b/config/custom_components/versatile_thermostat/translations/sk.json @@ -12,6 +12,25 @@ "thermostat_type": "Len jeden centrálny typ konfigurácie je možný" } }, + "menu": { + "title": "Menu", + "description": "Nakonfigurujte si termostat. Po zadaní všetkých požadovaných parametrov budete môcť dokončiť konfiguráciu.", + "menu_options": { + "main": "Hlavné atribúty", + "central_boiler": "Centrálny kotol", + "type": "Podklady", + "tpi": "TPI parametre", + "features": "Vlastnosti", + "presets": "Predvoľby", + "window": "Detekcia okien", + "motion": "Detekcia pohybu", + "power": "Správa napájania", + "presence": "Detekcia prítomnosti", + "advanced": "Pokročilé parametre", + "finalize": "Všetko hotové", + "configuration_not_complete": "Konfigurácia nie je dokončená" + } + }, "main": { "title": "Pridajte nový všestranný termostat", "description": "Hlavné povinné atribúty", @@ -19,22 +38,32 @@ "name": "Názov", "thermostat_type": "Termostat typ", "temperature_sensor_entity_id": "ID entity snímača teploty", + "last_seen_temperature_sensor_entity_id": "Dátum posledného zobrazenia izbovej teploty", "external_temperature_sensor_entity_id": "ID entity externého snímača teploty", "cycle_min": "Trvanie cyklu (minúty)", "temp_min": "Minimálna povolená teplota", "temp_max": "Maximálna povolená teplota", + "step_temperature": "Krok teploty", "device_power": "Napájanie zariadenia", "use_central_mode": "Povoliť ovládanie centrálnou entitou (potrebná centrálna konfigurácia)", + "use_main_central_config": "Použite dodatočnú centrálnu hlavnú konfiguráciu. Začiarknite, ak chcete použiť centrálnu hlavnú konfiguráciu (vonkajšia teplota, min, max, krok, ...).", + "used_by_controls_central_boiler": "Používa sa centrálnym kotlom. Skontrolujte, či má mať tento VTherm ovládanie na centrálnom kotli" + }, + "data_description": { + "temperature_sensor_entity_id": "ID entity snímača izbovej teploty", + "last_seen_temperature_sensor_entity_id": "Naposledy videný snímač izbovej teploty ID entity. Mal by to byť snímač dátumu a času", + "external_temperature_sensor_entity_id": "ID entity snímača vonkajšej teploty. Nepoužíva sa, ak je zvolená centrálna konfigurácia" + } + }, + "features": { + "title": "Vlastnosti", + "description": "Vlastnosti termostatu", + "data": { "use_window_feature": "Použite detekciu okien", "use_motion_feature": "Použite detekciu pohybu", "use_power_feature": "Použite správu napájania", "use_presence_feature": "Použite detekciu prítomnosti", - "use_main_central_config": "Použite centrálnu hlavnú konfiguráciu" - }, - "data_description": { - "use_central_mode": "Zaškrtnutím povolíte ovládanie VTherm pomocou vybraných entít central_mode", - "use_main_central_config": "Začiarknite, ak chcete použiť centrálnu hlavnú konfiguráciu. Zrušte začiarknutie, ak chcete použiť špecifickú hlavnú konfiguráciu pre tento VTherm", - "external_temperature_sensor_entity_id": "ID entity snímača vonkajšej teploty. Nepoužíva sa, ak je zvolená centrálna konfigurácia" + "use_central_boiler_feature": "Použite centrálny kotol. Začiarknutím tohto políčka pridáte ovládanie do centrálneho kotla. Po zaškrtnutí tohto políčka budete musieť nakonfigurovať VTherm, ktorý bude mať ovládanie centrálneho kotla, aby sa prejavilo. Ak jeden VTherm vyžaduje ohrev, kotol sa zapne. Ak žiadny VTherm nevyžaduje ohrev, kotol sa vypne. Príkazy na zapnutie/vypnutie centrálneho kotla sú uvedené na príslušnej konfiguračnej stránke" } }, "type": { @@ -45,6 +74,7 @@ "heater_entity2_id": "2. spínač ohrievača", "heater_entity3_id": "3. spínač ohrievača", "heater_entity4_id": "4. spínač ohrievača", + "heater_keep_alive": "Prepnite interval udržiavania v sekundách", "proportional_function": "Algoritmus", "climate_entity_id": "1. základná klíma", "climate_entity2_id": "2. základná klíma", @@ -58,6 +88,7 @@ "auto_regulation_mode": "Samoregulácia", "auto_regulation_dtemp": "Regulačný prah", "auto_regulation_periode_min": "Regulačné minimálne obdobie", + "auto_regulation_use_device_temp": "Použite vnútornú teplotu podkladu", "inverse_switch_command": "Inverzný prepínací príkaz", "auto_fan_mode": "Režim automatického ventilátora" }, @@ -66,6 +97,7 @@ "heater_entity2_id": "Voliteľné ID entity 2. ohrievača. Ak sa nepoužíva, nechajte prázdne", "heater_entity3_id": "Voliteľné ID entity 3. ohrievača. Ak sa nepoužíva, nechajte prázdne", "heater_entity4_id": "Voliteľné ID entity 4. ohrievača. Ak sa nepoužíva, nechajte prázdne", + "heater_keep_alive": "Voliteľný interval obnovy stavu spínača ohrievača. Ak to nie je potrebné, nechajte prázdne.", "proportional_function": "Algoritmus, ktorý sa má použiť (TPI je zatiaľ jediný)", "climate_entity_id": "ID základnej klimatickej entity", "climate_entity2_id": "2. základné identifikačné číslo klimatickej entity", @@ -79,6 +111,7 @@ "auto_regulation_mode": "Automatické nastavenie cieľovej teploty", "auto_regulation_dtemp": "Hranica v °, pod ktorou sa zmena teploty neodošle", "auto_regulation_periode_min": "Trvanie v minútach medzi dvoma aktualizáciami predpisov", + "auto_regulation_use_device_temp": "Na urýchlenie samoregulácie použite prípadný vnútorný snímač teploty podkladu", "inverse_switch_command": "V prípade spínača s pilotným vodičom a diódou možno budete musieť príkaz invertovať", "auto_fan_mode": "Automaticky aktivujte ventilátor, keď je potrebné veľké vykurovanie/chladenie" } @@ -101,24 +134,7 @@ "title": "Predvoľby", "description": "Pre každú predvoľbu zadajte cieľovú teplotu (0, ak chcete predvoľbu ignorovať)", "data": { - "eco_temp": "Teplota v predvoľbe Eco", - "comfort_temp": "Prednastavená teplota v komfortnom režime", - "boost_temp": "Teplota v prednastavení Boost", - "frost_temp": "Teplota v prednastavení Frost protection", - "eco_ac_temp": "Teplota v režime Eco prednastavená pre režim AC", - "comfort_ac_temp": "Teplota v režime Comfort je prednastavená pre režim AC", - "boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC", "use_presets_central_config": "Použite konfiguráciu centrálnych predvolieb" - }, - "data_description": { - "eco_temp": "Teplota v predvoľbe Eco", - "comfort_temp": "Prednastavená teplota v komfortnom režime", - "boost_temp": "Teplota v prednastavení Boost", - "frost_temp": "Teplota v prednastavenej ochrane proti mrazu", - "eco_ac_temp": "Teplota v režime Eco prednastavená pre režim AC", - "comfort_ac_temp": "Teplota v režime Comfort je prednastavená pre režim AC", - "boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC", - "use_presets_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnych predvolieb. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu predvolieb pre tento VTherm" } }, "window": { @@ -130,7 +146,8 @@ "window_auto_open_threshold": "Prah poklesu teploty pre automatickú detekciu otvoreného okna (v °/hodina)", "window_auto_close_threshold": "Prahová hodnota zvýšenia teploty pre koniec automatickej detekcie (v °/hodina)", "window_auto_max_duration": "Maximálne trvanie automatickej detekcie otvoreného okna (v min)", - "use_window_central_config": "Použite centrálnu konfiguráciu okna" + "use_window_central_config": "Použite centrálnu konfiguráciu okna", + "window_action": "Akcia" }, "data_description": { "window_sensor_entity_id": "Nechajte prázdne, ak nemáte použiť žiadny okenný senzor", @@ -138,7 +155,8 @@ "window_auto_open_threshold": "Odporúčaná hodnota: medzi 3 a 10. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne", "window_auto_close_threshold": "Odporúčaná hodnota: 0. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne", "window_auto_max_duration": "Odporúčaná hodnota: 60 (jedna hodina). Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne", - "use_window_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho okna. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu okna pre tento VTherm" + "use_window_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho okna. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu okna pre tento VTherm", + "window_action": "Akcia, ktorá sa má vykonať, ak sa okno zistí ako otvorené" } }, "motion": { @@ -181,26 +199,11 @@ "title": "Riadenie prítomnosti", "description": "Atribúty správy prítomnosti.\nPoskytuje senzor prítomnosti vášho domova (pravda, ak je niekto prítomný).\nPotom zadajte buď predvoľbu, ktorá sa má použiť, keď je senzor prítomnosti nepravdivý, alebo posun teploty, ktorý sa má použiť.\nAk je zadaná predvoľba, posun sa nepoužije.\nAk sa nepoužije, ponechajte zodpovedajúce entity_id prázdne.", "data": { - "presence_sensor_entity_id": "ID entity senzora prítomnosti", - "eco_away_temp": "Teplota v prednastavenej Eco, keď nie je žiadna prítomnosť", - "comfort_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný", - "boost_away_temp": "Prednastavená teplota v režime Boost, keď nie je prítomný", - "frost_away_temp": "Prednastavená teplota v režime Frost protection, keď nie je prítomný", - "eco_ac_away_temp": "Teplota v prednastavenej Eco, keď nie je prítomná v režime AC", - "comfort_ac_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný v režime AC", - "boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC", - "use_presence_central_config": "Použite centrálnu konfiguráciu prítomnosti" + "presence_sensor_entity_id": "Senzora prítomnosti", + "use_presence_central_config": "Použite konfiguráciu centrálnej prítomnosti teploty. Ak chcete použiť špecifické teplotné entity, zrušte výber" }, "data_description": { - "presence_sensor_entity_id": "ID entity senzora prítomnosti", - "eco_away_temp": "Teplota v prednastavenej Eco, keď nie je žiadna prítomnosť", - "comfort_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný", - "boost_away_temp": "Prednastavená teplota v režime Boost, keď nie je prítomný", - "frost_away_temp": "Teplota v Prednastavená ochrana pred mrazom, keď nie je prítomný", - "eco_ac_away_temp": "Teplota v prednastavenej Eco, keď nie je prítomná v režime AC", - "comfort_ac_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný v režime AC", - "boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC", - "use_presence_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnej prítomnosti. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu prítomnosti pre tento VTherm" + "presence_sensor_entity_id": "ID entity senzora prítomnosti" } }, "advanced": { @@ -244,6 +247,25 @@ "thermostat_type": "Je možný len jeden centrálny typ konfigurácie" } }, + "menu": { + "title": "Menu", + "description": "Nakonfigurujte si termostat. Po zadaní všetkých požadovaných parametrov budete môcť dokončiť konfiguráciu.", + "menu_options": { + "main": "Hlavné atribúty", + "central_boiler": "Centrálny kotol", + "type": "Podklady", + "tpi": "TPI parametre", + "features": "Vlastnosti", + "presets": "Predvoľby", + "window": "Detekcia okien", + "motion": "Detekcia pohybu", + "power": "Správa napájania", + "presence": "Detekcia prítomnosti", + "advanced": "Pokročilé parametre", + "finalize": "Všetko hotové", + "configuration_not_complete": "Konfigurácia nie je dokončená" + } + }, "main": { "title": "Hlavný - {name}", "description": "Hlavné povinné atribúty", @@ -251,22 +273,32 @@ "name": "Názov", "thermostat_type": "Termostat typ", "temperature_sensor_entity_id": "ID entity snímača teploty", + "last_seen_temperature_sensor_entity_id": "Dátum posledného zobrazenia izbovej teploty", "external_temperature_sensor_entity_id": "ID entity externého snímača teploty", "cycle_min": "Trvanie cyklu (minúty)", "temp_min": "Minimálna povolená teplota", "temp_max": "Maximálna povolená teplota", + "step_temperature": "Krok teploty", "device_power": "Výkon zariadenia (kW)", - "use_central_mode": "Povoliť ovládanie centrálnou entitou (potrebná centrálna konfigurácia)", + "use_central_mode": "Povoliť ovládanie centrálnou entitou (vyžaduje centrálnu konfiguráciu). Zaškrtnutím povolíte ovládanie VTherm pomocou vybraných entít central_mode.", + "use_main_central_config": "Použite dodatočnú centrálnu hlavnú konfiguráciu. Začiarknite, ak chcete použiť centrálnu hlavnú konfiguráciu (vonkajšia teplota, min, max, krok, ...).", + "used_by_controls_central_boiler": "Používa sa centrálnym kotlom. Skontrolujte, či má mať tento VTherm ovládanie na centrálnom kotli" + }, + "data_description": { + "temperature_sensor_entity_id": "ID entity snímača izbovej teploty", + "last_seen_temperature_sensor_entity_id": "Naposledy videný snímač izbovej teploty ID entity. Mal by to byť snímač dátumu a času", + "external_temperature_sensor_entity_id": "ID entity snímača vonkajšej teploty. Nepoužíva sa, ak je zvolená centrálna konfigurácia" + } + }, + "features": { + "title": "Vlastnosti - {name}", + "description": "Vlastnosti termostatu", + "data": { "use_window_feature": "Použite detekciu okien", "use_motion_feature": "Použite detekciu pohybu", "use_power_feature": "Použite správu napájania", "use_presence_feature": "Použite detekciu prítomnosti", - "use_main_central_config": "Použite centrálnu hlavnú konfiguráciu" - }, - "data_description": { - "use_central_mode": "Zaškrtnutím povolíte ovládanie VTherm pomocou vybraných entít central_mode", - "use_main_central_config": "Začiarknite, ak chcete použiť centrálnu hlavnú konfiguráciu. Ak chcete použiť špecifickú konfiguráciu pre tento VTherm, zrušte začiarknutie", - "external_temperature_sensor_entity_id": "ID entity snímača vonkajšej teploty. Nepoužíva sa, ak je zvolená centrálna konfigurácia" + "use_central_boiler_feature": "Použite centrálny kotol. Začiarknutím tohto políčka pridáte ovládanie do centrálneho kotla. Po zaškrtnutí tohto políčka budete musieť nakonfigurovať VTherm, ktorý bude mať ovládanie centrálneho kotla, aby sa prejavilo. Ak jeden VTherm vyžaduje ohrev, kotol sa zapne. Ak žiadny VTherm nevyžaduje ohrev, kotol sa vypne. Príkazy na zapnutie/vypnutie centrálneho kotla sú uvedené na príslušnej konfiguračnej stránke" } }, "type": { @@ -277,6 +309,7 @@ "heater_entity2_id": "2. spínač ohrievača", "heater_entity3_id": "3. spínač ohrievača", "heater_entity4_id": "4. spínač ohrievača", + "heater_keep_alive": "Prepnite interval udržiavania v sekundách", "proportional_function": "Algoritmus", "climate_entity_id": "Základná klíma", "climate_entity2_id": "2. základná klíma", @@ -290,6 +323,7 @@ "auto_regulation_mode": "Samoregulácia", "auto_regulation_dtemp": "Regulačný prah", "auto_regulation_periode_min": "Regulačné minimálne obdobie", + "auto_regulation_use_device_temp": "Použite vnútornú teplotu podkladu", "inverse_switch_command": "Inverzný prepínací príkaz", "auto_fan_mode": "Režim automatického ventilátora" }, @@ -298,6 +332,7 @@ "heater_entity2_id": "Voliteľné ID entity 2. ohrievača. Ak sa nepoužíva, nechajte prázdne", "heater_entity3_id": "Voliteľné ID entity 3. ohrievača. Ak sa nepoužíva, nechajte prázdne", "heater_entity4_id": "Voliteľné ID entity 4. ohrievača. Ak sa nepoužíva, nechajte prázdne", + "heater_keep_alive": "Voliteľný interval obnovy stavu spínača ohrievača. Ak to nie je potrebné, nechajte prázdne.", "proportional_function": "Algoritmus, ktorý sa má použiť (TPI je zatiaľ jediný)", "climate_entity_id": "ID základnej klimatickej entity", "climate_entity2_id": "2. základný identifikátor klimatickej entity", @@ -311,6 +346,7 @@ "auto_regulation_mode": "Automatické nastavenie cieľovej teploty", "auto_regulation_dtemp": "Hranica v °, pod ktorou sa zmena teploty neodošle", "auto_regulation_periode_min": "Trvanie v minútach medzi dvoma aktualizáciami predpisov", + "auto_regulation_use_device_temp": "Na urýchlenie samoregulácie použite prípadný vnútorný snímač teploty podkladu", "inverse_switch_command": "V prípade spínača s pilotným vodičom a diódou možno budete musieť príkaz invertovať", "auto_fan_mode": "Automaticky aktivujte ventilátor, keď je potrebné veľké vykurovanie/chladenie" } @@ -333,24 +369,7 @@ "title": "Predvoľby - {name}", "description": "Pre každú predvoľbu zadajte cieľovú teplotu (0, ak chcete predvoľbu ignorovať)", "data": { - "eco_temp": "Teplota v predvoľbe Eco", - "comfort_temp": "Prednastavená teplota v komfortnom režime", - "boost_temp": "Teplota v prednastavení Boost", - "frost_temp": "Teplota v prednastavení Frost protection", - "eco_ac_temp": "Teplota v režime Eco prednastavená pre režim AC", - "comfort_ac_temp": "Teplota v režime Comfort je prednastavená pre režim AC", - "boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC", "use_presets_central_config": "Použite konfiguráciu centrálnych predvolieb" - }, - "data_description": { - "eco_temp": "Teplota v predvoľbe Eco", - "comfort_temp": "Prednastavená teplota v komfortnom režime", - "boost_temp": "Teplota v prednastavení Boost", - "frost_temp": "Teplota v prednastavenej ochrane proti mrazu", - "eco_ac_temp": "Teplota v režime Eco prednastavená pre režim AC", - "comfort_ac_temp": "Teplota v režime Comfort je prednastavená pre režim AC", - "boost_ac_temp": "Prednastavená teplota v režime Boost pre režim AC", - "use_presets_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnych predvolieb. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu predvolieb pre tento VTherm" } }, "window": { @@ -362,7 +381,8 @@ "window_auto_open_threshold": "Prah poklesu teploty pre automatickú detekciu otvoreného okna (v °/hodina)", "window_auto_close_threshold": "Prahová hodnota zvýšenia teploty pre koniec automatickej detekcie (v °/hodina)", "window_auto_max_duration": "Maximálne trvanie automatickej detekcie otvoreného okna (v min)", - "use_window_central_config": "Použite centrálnu konfiguráciu okna" + "use_window_central_config": "Použite centrálnu konfiguráciu okna", + "window_action": "Akcia" }, "data_description": { "window_sensor_entity_id": "Nechajte prázdne, ak nemáte použiť žiadny okenný senzor", @@ -370,7 +390,8 @@ "window_auto_open_threshold": "Odporúčaná hodnota: medzi 3 a 10. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne", "window_auto_close_threshold": "Odporúčaná hodnota: 0. Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne", "window_auto_max_duration": "Odporúčaná hodnota: 60 (jedna hodina). Ak sa nepoužíva automatická detekcia otvoreného okna, nechajte prázdne", - "use_window_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho okna. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu okna pre tento VTherm" + "use_window_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálneho okna. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu okna pre tento VTherm", + "window_action": "Akcia, ktorá sa má vykonať, ak sa okno zistí ako otvorené" } }, "motion": { @@ -410,29 +431,14 @@ } }, "presence": { - "title": "Riadenie prítomnosti", - "description": "Atribúty správy prítomnosti.\nPoskytuje senzor prítomnosti vášho domova (pravda, ak je niekto prítomný).\nPotom zadajte buď predvoľbu, ktorá sa má použiť, keď je senzor prítomnosti nepravdivý, alebo posun teploty, ktorý sa má použiť.\nAk je zadaná predvoľba, posun sa nepoužije.\nAk sa nepoužije, ponechajte zodpovedajúce entity_id prázdne.", + "title": "Prítommnosť - {name}", + "description": "Atribúty riadenia prítomnosti.\nPoskytuje senzor prítomnosti vášho domova (pravda, je niekto prítomný) a poskytuje zodpovedajúce prednastavené nastavenie teploty.", "data": { - "presence_sensor_entity_id": "ID entity senzora prítomnosti (pravda je prítomná)", - "eco_away_temp": "Teplota v prednastavenej Eco, keď nie je žiadna prítomnosť", - "comfort_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný", - "boost_away_temp": "Prednastavená teplota v režime Boost, keď nie je prítomný", - "frost_away_temp": "Prednastavená teplota v režime Frost protection, keď nie je prítomný", - "eco_ac_away_temp": "Teplota v prednastavenej Eco, keď nie je prítomná v režime AC", - "comfort_ac_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný v režime AC", - "boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC", - "use_presence_central_config": "Použite centrálnu konfiguráciu prítomnosti" + "presence_sensor_entity_id": "Senzor prítomnosti", + "use_presence_central_config": "Použite konfiguráciu centrálnej prítomnosti teploty. Ak chcete použiť špecifické entity teploty, zrušte začiarknutie" }, "data_description": { - "presence_sensor_entity_id": "ID entity senzora prítomnosti", - "eco_away_temp": "Teplota v prednastavenej Eco, keď nie je žiadna prítomnosť", - "comfort_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný", - "boost_away_temp": "Prednastavená teplota v režime Boost, keď nie je prítomný", - "frost_away_temp": "Teplota v Prednastavená ochrana pred mrazom, keď nie je prítomný", - "eco_ac_away_temp": "Teplota v prednastavenej Eco, keď nie je prítomná v režime AC", - "comfort_ac_away_temp": "Teplota v režime Comfort je prednastavená, keď nie je prítomný v režime AC", - "boost_ac_away_temp": "Teplota v prednastavenom Boost, keď nie je prítomný v režime AC", - "use_presence_central_config": "Začiarknite, ak chcete použiť konfiguráciu centrálnej prítomnosti. Zrušte začiarknutie, ak chcete použiť špecifickú konfiguráciu prítomnosti pre tento VTherm" + "presence_sensor_entity_id": "ID entity senzora prítomnosti" } }, "advanced": { @@ -458,7 +464,8 @@ "unknown": "Neočakávaná chyba", "unknown_entity": "Neznáme ID entity", "window_open_detection_method": "Mala by sa použiť iba jedna metóda detekcie otvoreného okna. Použite senzor alebo automatickú detekciu cez teplotný prah, ale nie oboje", - "no_central_config": "Nemôžete zaškrtnúť „použiť centrálnu konfiguráciu“, pretože sa nenašla žiadna centrálna konfigurácia. Aby ste ho mohli používať, musíte si vytvoriť všestranný termostat typu „Central Configuration“." + "no_central_config": "Nemôžete zaškrtnúť „použiť centrálnu konfiguráciu“, pretože sa nenašla žiadna centrálna konfigurácia. Aby ste ho mohli používať, musíte si vytvoriť všestranný termostat typu „Central Configuration“.", + "service_configuration_format": "Formát konfigurácie služby je nesprávny" }, "abort": { "already_configured": "Zariadenie je už nakonfigurované" @@ -491,6 +498,22 @@ "auto_fan_high": "Vysoký", "auto_fan_turbo": "Turbo" } + }, + "window_action": { + "options": { + "window_turn_off": "Vypnúť", + "window_fan_only": "Len ventilátor", + "window_frost_temp": "Ochrana pred mrazom", + "window_eco_temp": "Eco" + } + }, + "presets": { + "options": { + "frost": "Ochrana proti mrazu", + "eco": "Eco", + "comfort": "Komfort", + "boost": "Boost" + } } }, "entity": { @@ -506,6 +529,53 @@ } } } + }, + "number": { + "frost_temp": { + "name": "Mráz" + }, + "eco_temp": { + "name": "Eco" + }, + "comfort_temp": { + "name": "Komfort" + }, + "boost_temp": { + "name": "Boost" + }, + "frost_ac_temp": { + "name": "Mráz ac" + }, + "eco_ac_temp": { + "name": "Eco ac" + }, + "comfort_ac_temp": { + "name": "Komfort ac" + }, + "boost_ac_temp": { + "name": "Boost ac" + }, + "frost_away_temp": { + "name": "Mráz mimo" + }, + "eco_away_temp": { + "name": "Eko mimo" + }, + "comfort_away_temp": { + "name": "Komfort mimo" + }, + "boost_away_temp": { + "name": "Boost mimo" + }, + "eco_ac_away_temp": { + "name": "Eco ac mimo" + }, + "comfort_ac_away_temp": { + "name": "Komfort ac mimo" + }, + "boost_ac_away_temp": { + "name": "Boost ac mimo" + } } } } diff --git a/config/custom_components/versatile_thermostat/underlyings.py b/config/custom_components/versatile_thermostat/underlyings.py index 2b3580c..f1ae0e4 100644 --- a/config/custom_components/versatile_thermostat/underlyings.py +++ b/config/custom_components/versatile_thermostat/underlyings.py @@ -5,7 +5,7 @@ import logging from typing import Any from enum import StrEnum -from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, UnitOfTemperature +from homeassistant.const import ATTR_ENTITY_ID, STATE_ON, STATE_UNAVAILABLE from homeassistant.core import State from homeassistant.exceptions import ServiceNotFound @@ -30,6 +30,7 @@ from homeassistant.components.number import SERVICE_SET_VALUE from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.event import async_call_later +from homeassistant.util.unit_conversion import TemperatureConverter from .const import UnknownEntity, overrides from .keep_alive import IntervalCaller @@ -252,7 +253,28 @@ class UnderlyingSwitch(UnderlyingEntity): async def _keep_alive_callback(self): """Keep alive: Turn on if already turned on, turn off if already turned off.""" - await (self.turn_on() if self.is_device_active else self.turn_off()) + timer = self._keep_alive.backoff_timer + state: State | None = self._hass.states.get(self._entity_id) + # Normal, expected state.state values are "on" and "off". An absent + # underlying MQTT switch was observed to produce either state == None + # or state.state == STATE_UNAVAILABLE ("unavailable"). + if state is None or state.state == STATE_UNAVAILABLE: + if timer.is_ready(): + _LOGGER.warning( + "Entity %s is not available (state: %s). Will keep trying " + "keep alive calls, but won't log this condition every time.", + self._entity_id, + state.state if state else "None", + ) + else: + if timer.in_progress: + timer.reset() + _LOGGER.warning( + "Entity %s has recovered (state: %s).", + self._entity_id, + state.state, + ) + await (self.turn_on() if self.is_device_active else self.turn_off()) # @overrides this breaks some unit tests TypeError: object MagicMock can't be used in 'await' expression async def turn_off(self): @@ -663,7 +685,7 @@ class UnderlyingClimate(UnderlyingEntity): def temperature_unit(self) -> str: """Get the temperature_unit""" if not self.is_initialized: - return UnitOfTemperature.CELSIUS + return self._hass.config.units.temperature_unit return self._underlying_climate.temperature_unit @property @@ -704,7 +726,7 @@ class UnderlyingClimate(UnderlyingEntity): if not hasattr(self._underlying_climate, "current_temperature"): return None - return self._underlying_climate.current_temperature + return self._hass.states.get(self._entity_id).attributes.get("current_temperature") def turn_aux_heat_on(self) -> None: """Turn auxiliary heater on.""" @@ -731,8 +753,12 @@ class UnderlyingClimate(UnderlyingEntity): self._underlying_climate.min_temp is not None and self._underlying_climate is not None ): - min_val = self._underlying_climate.min_temp - max_val = self._underlying_climate.max_temp + min_val = TemperatureConverter.convert( + self._underlying_climate.min_temp, self._underlying_climate.temperature_unit, self._hass.config.units.temperature_unit + ) + max_val = TemperatureConverter.convert( + self._underlying_climate.max_temp, self._underlying_climate.temperature_unit, self._hass.config.units.temperature_unit + ) new_value = max(min_val, min(value, max_val)) else: diff --git a/config/custom_components/vigieau/const.py b/config/custom_components/vigieau/const.py index 7a8238f..feebd8f 100644 --- a/config/custom_components/vigieau/const.py +++ b/config/custom_components/vigieau/const.py @@ -48,9 +48,10 @@ SENSOR_DEFINITIONS: tuple[VigieEauSensorEntityDescription, ...] = ( key="fountains", matchers=[ "alimentation des fontaines.+", - "douches .+ plages.+", + "douches .+ plage.+", "fontaines", "jeux d'eau", + ".*Alimentation de douches de plage.*", ], ), VigieEauSensorEntityDescription( @@ -93,7 +94,8 @@ SENSOR_DEFINITIONS: tuple[VigieEauSensorEntityDescription, ...] = ( "Arrosage, arbustes et arbres", "Arrosage des jardinières et suspensions", "Arrosage des espaces arborés", - "Arrosage des terrains de sport", + "Arrosage.+terrains de sport", + "Arrosage terrains sport.+", ], ), VigieEauSensorEntityDescription( @@ -157,6 +159,8 @@ SENSOR_DEFINITIONS: tuple[VigieEauSensorEntityDescription, ...] = ( "Remplissage des piscine privées", "Remplissage des piscines individuelles", "remise à niveau des piscines", + "Remplissage de piscines.+", + "Piscines ouvertes au public.*", ], ), VigieEauSensorEntityDescription( @@ -201,6 +205,10 @@ SENSOR_DEFINITIONS: tuple[VigieEauSensorEntityDescription, ...] = ( "Entretien de cours d'eau", "Travaux et rejets", "Travaux sur les systèmes d’assainissement occasionnant des rejets", + ".*installations hydrauliques.*", + ".*électricité d’origine hydraulique.*", + "production.+origine.+hydraulique.*", + "Installations de production d'électricité d'orignie hydraulique", ], ), VigieEauSensorEntityDescription( @@ -219,7 +227,10 @@ SENSOR_DEFINITIONS: tuple[VigieEauSensorEntityDescription, ...] = ( icon="mdi:golf", category="golfs", key="golfs", - matchers=["arrosage des golfs"], + matchers=[ + "arrosage des golfs", + "Arrosage des.+golfs", + ], ), VigieEauSensorEntityDescription( name="Prélèvement en canaux", @@ -243,6 +254,7 @@ SENSOR_DEFINITIONS: tuple[VigieEauSensorEntityDescription, ...] = ( "Nouvelles demandes de prélèvement d'eau et création de forages", "Création de prélèvements", "Prélèvement en cours d’eau", + "alimentation en eau potable des populations.+", ], ), VigieEauSensorEntityDescription( @@ -259,6 +271,8 @@ SENSOR_DEFINITIONS: tuple[VigieEauSensorEntityDescription, ...] = ( # ICPE means "Installation classée pour la protection de l'environment" "ICPE soumises à un APC relatif à la sécheresse", "Usages récréatifs collectifs à partir d’eau potable.+", + "Réalisation de seuils provisoires", + "Activités industrielles et commerciales", ], ), ) diff --git a/config/custom_components/vigieau/scripts/full_usage_list.json b/config/custom_components/vigieau/scripts/full_usage_list.json index 1b8bdd8..e3e93ed 100644 --- a/config/custom_components/vigieau/scripts/full_usage_list.json +++ b/config/custom_components/vigieau/scripts/full_usage_list.json @@ -9,25 +9,105 @@ "thematique": "Travaux et activités en cours d'eau" }, { - "usage": "Alimentation des fontaines publiques", + "usage": "Alimentation de douches de plage, jeux d’eau", + "thematique": "Prélever" + }, + { + "usage": "Alimentation des canaux et des rigoles", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Alimentation des fontaines d'ornement en circuit ouvert (publiques et privées) ", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Alimentation des fontaines publiques Et privées d'ornement en circuit ouvert", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Alimentation des fontaines publiques et privées d'ornement en circuit ouvert", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Alimentation des fontaines publiques et privées d'ornement en circuit ouvert", "thematique": "Alimenter des fontaines et autres usages de loisirs" }, { "usage": "Alimentation des fontaines publiques et privées d’ornement", "thematique": "Alimenter des fontaines et autres usages de loisirs" }, + { + "usage": "Alimentation des fontaines, pièces d'eau d'agrément et jeux d'eau récréatifs en circuit ouvert", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Alimentation en eau des fontaines publiques et privées d’ornement", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Alimentation fontaines publiques et privées d’ornement", + "thematique": "Prélever" + }, + { + "usage": "Alimentation gravitaire des ouvrages d’irrigation et des canaux d'agrément dans la Lozère", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Arrosage d'arbres et arbustes", + "thematique": "Arroser" + }, + { + "usage": "Arrosage de surfaces de circulation générant de la poussière (chantiers, motocross, athlétisme...)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des espaces arborés, pelouses, massifs fleuris et espaces verts publics et privés", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des espaces verts et pelouses", + "thematique": "Arroser" + }, { "usage": "Arrosage des golfs", "thematique": "Arroser" }, + { + "usage": "Arrosage des golfs (conformément à l’accord cadre golf et environnement 2019-2024)", + "thematique": "Irriguer" + }, + { + "usage": "Arrosage des golfs (conformément à l’accord cadre golf et environnement de 2019-2024)", + "thematique": "Arroser" + }, { "usage": "Arrosage des golfs(Conformément à l'accord cadre golf et environnement 2019-2024", "thematique": "Arroser" }, + { + "usage": "Arrosage des greens et départs de golfs", + "thematique": "Arroser" + }, { "usage": "Arrosage des jardins potagers", "thematique": "Arroser" }, + { + "usage": "Arrosage des jardins potagers (y compris serres non agricoles)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des jardins potagers (y compris serres non-agricoles)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des jardins potagers (y compris serres, non agricoles)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des jardins potagers (yc serres non-agricoles)", + "thematique": "Arroser" + }, { "usage": "Arrosage des jardins potagers collectifs", "thematique": "Arroser" @@ -36,6 +116,14 @@ "usage": "Arrosage des jardins potagers individuels", "thematique": "Arroser" }, + { + "usage": "Arrosage des jardins potagers y compris serres non agricoles", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des jardins potagers y/c serres non agricoles", + "thematique": "Arroser" + }, { "usage": "Arrosage des pelouses, massifs fleuris", "thematique": "Arroser" @@ -44,18 +132,174 @@ "usage": "Arrosage des pelouses, massifs fleuris et espaces verts (y compris rond-points, voies de tramway).", "thematique": "Arroser" }, + { + "usage": "Arrosage des pelouses, massifs fleuris, Jardins d’agrément, des espaces verts, golfs particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, arbres et arbustes", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, espaces verts", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, espaces verts, golfs particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, jardins d'agrément", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, jardins d'agrément, arrosage des espaces verts, golf particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, jardins d’agrément, espaces verts et golfs particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, jardins d’agrément, espaces verts, golfs particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris, jardins d’agrément, golfs particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des pelouses, massifs fleuris. ", + "thematique": "Irriguer" + }, + { + "usage": "Arrosage des plantations d'arbre de moins de 3 ans", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des plantations d’arbre de moins de 3 ans", + "thematique": "Arroser" + }, { "usage": "Arrosage des terrains de sport", "thematique": "Arroser" }, + { + "usage": "Arrosage des terrains de sport (dont aires et centres équestres, circuits motocross ou VTT)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport (dont aires/centres équestres, hippodromes, circuits VTT/motocross)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport (dont aires/centres équestres, hippodromes, circuits motocross/VTT)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport (voir ACI)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport (y compris aires d’évolutions équestres, centres équestres, hippodromes, circuits motocross, circuits vtt)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport (y compris aires et centres équestres, circuits VTT motocross)", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport (y compris aires équestres,circuits motocross, circuit vtt) ", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport dont aires et centres équestres, hippodromes, circuits motocross/VTT", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport y compris aires/centre équestres, hippodromes, circuits motocross/vtt", + "thematique": "Arroser" + }, + { + "usage": "Arrosage des terrains de sport, sols équestres et terrains de sports motorisés", + "thematique": "Arroser" + }, + { + "usage": "Arrosage massifs fleuris", + "thematique": "Arroser" + }, + { + "usage": "Arrosage pelouses, massifs fleuris, Jardins d’agrément, espaces verts, golfs particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage pelouses, massifs fleuris, jardins agrément, espaces verts, golfs particuliers", + "thematique": "Arroser" + }, + { + "usage": "Arrosage terrains de sport (+ centres équestres, hippodromes, circuits motocross/VTT", + "thematique": "Arroser" + }, + { + "usage": "Arrosage terrains sport yc aires d’évolutions et centres équestres, hippodromes, motocross, VTT", + "thematique": "Arroser" + }, + { + "usage": "Arrosage, arbustes et arbres", + "thematique": "Arroser" + }, + { + "usage": "Fonctionnement des douches de plages et tout autre dispositif analogue", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Fonctionnement des fontaines publiques et privées", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Gestion des ouvrages hydrauliques (hors plans d'eau et canaux)", + "thematique": "Ouvrages hydrauliques" + }, { "usage": "ICPE soumises à un APC relatif à la sécheresse", "thematique": "ICPE" }, + { + "usage": "Installations de production d'électricité d'origine hydraulique", + "thematique": "Installations de production d'électricité" + }, + { + "usage": "Installations de production d'électricité d'origine nucléaire, hydraulique, et thermique à flamme", + "thematique": "Installations de production d'électricité" + }, + { + "usage": "Installations de production d'électricité d'orignie hydraulique", + "thematique": "Installations de production d'électricité" + }, + { + "usage": "Installations de production d’électricité d’origine hydraulique", + "thematique": "Installations de production d'électricité" + }, { "usage": "Irrigation par aspersion des cultures", "thematique": "Irriguer" }, + { + "usage": "Jeux d'eau", + "thematique": "Alimenter des fontaines et autres usages de loisirs" + }, + { + "usage": "Lavage de tous les véhicules et engins terrestres/nautiques dans des installations professionnelles", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules automobiles et engins nautiques par des particuliers", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules chez des professionnels", + "thematique": "Nettoyer" + }, { "usage": "Lavage de véhicules chez les particuliers", "thematique": "Nettoyer" @@ -65,7 +309,27 @@ "thematique": "Nettoyer" }, { - "usage": "Lavage de véhicules et bateaux chez les particuliers", + "usage": "Lavage de véhicules et engins nautiques chez les particuliers", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules et engins nautiques chez les professionnels", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules et engins nautiques dans des installations de professionnels", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules et engins nautiques par des professionnels", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules et engins nautiques par les professionnels", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules et engins nautiques privés chez les particuliers", "thematique": "Nettoyer" }, { @@ -80,18 +344,74 @@ "usage": "Lavage de véhicules publics ou privés en stations de lavage professionnelles.", "thematique": "Nettoyer" }, + { + "usage": "Lavage de véhicules, bateaux et engins nautiques chez les particuliers", + "thematique": "Arroser" + }, + { + "usage": "Lavage de véhicules, bateaux et engins nautiques chez les particuliers", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage de véhicules, engins terrestres ou nautiques dans des installations professionnelles", + "thematique": "Nettoyer" + }, { "usage": "Lavage des bateaux", "thematique": "Nettoyer" }, + { + "usage": "Lavage des espaces publics (trottoirs, terrasses...)", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage des navires, bateaux et engins nautiques", + "thematique": "Nettoyer" + }, + { + "usage": "Lavage d’engins nautiques", + "thematique": "Nettoyer" + }, { "usage": "Lavage et entretien des embarcations (motorisées ou non) en aire de carénage.", "thematique": "Nettoyer" }, + { + "usage": "Manoeuvre de vannes d'installations hydrauliques", + "thematique": "Ouvrages hydrauliques" + }, + { + "usage": "Manoeuvres de vannes d'installations hydrauliques", + "thematique": "Ouvrages hydrauliques" + }, + { + "usage": "Manœuvres des vannes d'installations hydrauliques", + "thematique": "Ouvrages hydrauliques" + }, + { + "usage": "Manœuvres des vannes d’installations hydrauliques", + "thematique": "Ouvrages hydrauliques" + }, + { + "usage": "Navigation fluviale", + "thematique": "Travaux et activités en cours d'eau" + }, { "usage": "Navigation fluviale.", "thematique": "Travaux et activités en cours d'eau" }, + { + "usage": "Nettoyage / arrosage des sites de manifestations temporaires sportives et culturelles", + "thematique": "Arroser" + }, + { + "usage": "Nettoyage de la voirie, des trottoirs et autres surfaces imperméabilisées", + "thematique": "Nettoyer" + }, + { + "usage": "Nettoyage des façades et toitures", + "thematique": "Nettoyer" + }, { "usage": "Nettoyage des façades, terrasses et murs de clôture", "thematique": "Nettoyer" @@ -105,17 +425,65 @@ "thematique": "Nettoyer" }, { - "usage": "Nettoyage des façades, toitures, trottoirs, terrasses, façades imperméabilisées...", + "usage": "Nettoyage des façades, toitures, trottoirs, voiries et autres surfaces imperméabilisées", "thematique": "Nettoyer" }, { "usage": "Nettoyage des voieries", "thematique": "Nettoyer" }, + { + "usage": "Nettoyage des voiries, terrasses, façades, toitures, trottoirs et autres surfaces imperméabilisées", + "thematique": "Nettoyer" + }, + { + "usage": "Nettoyage des voiries, trottoirs et autres surfaces imperméabilisées", + "thematique": "Nettoyer" + }, + { + "usage": "Orpaillage (professionnel et amateur)", + "thematique": "Travaux et activités en cours d'eau" + }, + { + "usage": "Orpaillage (professionnel et amateur) et pratiques ou activités dans le lit ou sur les berges", + "thematique": "Travaux et activités en cours d'eau" + }, + { + "usage": "Orpaillage (professionnel et amateur) et pratiques pouvant impacter les milieux aquatiques", + "thematique": "Travaux et activités en cours d'eau" + }, + { + "usage": "Orpaillage et activités dans le lit ou sur les berges cours d'eau ", + "thematique": "Travaux et activités en cours d'eau" + }, { "usage": "Orpaillage et pêche à l’aimant.", "thematique": "Travaux et activités en cours d'eau" }, + { + "usage": "Piscines ouvertes au public", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Pratique de la navigation de loisir, y compris le canoë et le kayak*", + "thematique": "Travaux et activités en cours d'eau" + }, + { + "usage": "Pratique du canyoning sur matériaux alluvionnaires", + "thematique": "Travaux et activités en cours d'eau" + }, + { + "usage": "Pratiques ou activités dans le lit pouvant avoir un impact sur les milieux aquatiques", + "thematique": "Travaux et activités en cours d'eau" + }, + { + "usage": "Prélèvement en canaux ", + "thematique": "Prélever" + }, + { + "usage": "Prélèvement en canaux et en cours d’eau", + "thematique": "Prélever" + }, { "usage": "Rejets et travaux en rivière", "thematique": "Travaux et activités en cours d'eau" @@ -128,6 +496,54 @@ "usage": "Remplissage / vidange des plans d'eau.", "thematique": "Remplir ou vidanger" }, + { + "usage": "Remplissage / vidange des plans d’eau", + "thematique": "Prélever" + }, + { + "usage": "Remplissage de piscines accueillant du public", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage de piscines domestiques (de plus d'1m³)", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage de piscines familiales", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage de plans d’eau sauf destinés à l’AEP et soutien d’étiage permis par arrêté", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage des plans d'eau", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage des plans d'eau sauf retenues destinées à l'AEP et retenues participant au soutien d'éti", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage des plans d'eau, étangs, bassin d'agrément et manoeuvre de vannes", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage des plans d’eau", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage des plans d’eau (sauf retenues autorisées ou dédiées à l'eau potable", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage des plans d’eau sauf diverses retenues (voir arrêté)", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage des plans d’eau sauf retenues destinées à l’AEP et retenues participant au soutien d’étiage dont l’arrêté d’autorisation le permet", + "thematique": "Remplir ou vidanger" + }, { "usage": "Remplissage et vidange de piscines privées", "thematique": "Remplir ou vidanger" @@ -140,6 +556,14 @@ "usage": "Remplissage et vidange de piscines privées (de plus d'1 m3).", "thematique": "Remplir ou vidanger" }, + { + "usage": "Remplissage et vidange de piscines privées (de plus d’1m3) y compris les spas de loisirs", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Remplissage et vidange de piscines privées > 1 m3", + "thematique": "Prélever" + }, { "usage": "Travaux en cours d’eau", "thematique": "Travaux et activités en cours d'eau" @@ -148,9 +572,45 @@ "usage": "Travaux en cours d’eau.", "thematique": "Travaux et activités en cours d'eau" }, + { + "usage": "Travaux en cours d’eau. ", + "thematique": "Travaux et activités en cours d'eau" + }, { "usage": "Usages récréatifs collectifs à partir d’eau potable (dans le cadre de manifestations).", "thematique": "Remplir ou vidanger" + }, + { + "usage": "Vidange de piscines", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Vidange de plans d’eau vers le réseau hydrographique", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Vidange des plans d'eau", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Vidange plans d’eau vers le réseau hydrographique", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Vidange totale de plans d’eau vers le réseau hydrographique", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "Vidanges piscines privées", + "thematique": "Remplir ou vidanger" + }, + { + "usage": "alimentation en eau potable des populations ( usages prioritaires ; santé, salubrité, sécurité civile)", + "thematique": "Prélever" + }, + { + "usage": "orpaillage (professionnel et amateur) et pratiques ou activités dans le lit ou sur les berges", + "thematique": "Travaux et activités en cours d'eau" } ] } \ No newline at end of file diff --git a/config/custom_components/watchman/__init__.py b/config/custom_components/watchman/__init__.py index 65134dd..29842fa 100644 --- a/config/custom_components/watchman/__init__.py +++ b/config/custom_components/watchman/__init__.py @@ -1,17 +1,16 @@ """https://github.com/dummylabs/thewatchman§""" + from datetime import timedelta import logging -import os import time import json import voluptuous as vol -from homeassistant.loader import async_get_integration +from anyio import Path from homeassistant.helpers import config_validation as cv from homeassistant.components import persistent_notification from homeassistant.util import dt as dt_util from homeassistant.helpers.event import async_track_point_in_utc_time from homeassistant.exceptions import HomeAssistantError -from homeassistant.helpers.typing import HomeAssistantType from homeassistant.config_entries import ConfigEntry, SOURCE_IMPORT from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady @@ -21,7 +20,6 @@ from homeassistant.const import ( EVENT_SERVICE_REMOVED, EVENT_STATE_CHANGED, EVENT_CALL_SERVICE, - STATE_UNKNOWN, ) from .coordinator import WatchmanCoordinator @@ -33,7 +31,7 @@ from .utils import ( table_renderer, text_renderer, get_config, - get_report_path, + async_get_report_path, ) from .const import ( @@ -97,7 +95,7 @@ CONFIG_SCHEMA = vol.Schema( ) -async def async_setup(hass: HomeAssistantType, config: dict): +async def async_setup(hass: HomeAssistant, config: dict): """Set up is called when Home Assistant is loading our component.""" if config.get(DOMAIN) is None: # We get here if the integration is set up using config flow @@ -113,7 +111,7 @@ async def async_setup(hass: HomeAssistantType, config: dict): return True -async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): +async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up this integration using UI""" _LOGGER.debug(entry.options) _LOGGER.debug("Home assistant path: %s", hass.config.path("")) @@ -134,19 +132,12 @@ async def async_setup_entry(hass: HomeAssistantType, entry: ConfigEntry): await add_event_handlers(hass) if hass.is_running: # integration reloaded or options changed via UI - parse_config(hass, reason="changes in watchman configuration") + await parse_config(hass, reason="changes in watchman configuration") await coordinator.async_config_entry_first_refresh() else: # first run, home assistant is loading # parse_config will be scheduled once HA is fully loaded _LOGGER.info("Watchman started [%s]", VERSION) - - -# resources = hass.data["lovelace"]["resources"] -# await resources.async_get_info() -# for itm in resources.async_items(): -# _LOGGER.debug(itm) - return True @@ -155,9 +146,7 @@ async def update_listener(hass: HomeAssistant, entry: ConfigEntry): await hass.config_entries.async_reload(entry.entry_id) -async def async_unload_entry( - hass: HomeAssistant, config_entry -): # pylint: disable=unused-argument +async def async_unload_entry(hass: HomeAssistant, config_entry): # pylint: disable=unused-argument """Handle integration unload""" for cancel_handle in hass.data[DOMAIN].get("cancel_handlers", []): if cancel_handle: @@ -189,7 +178,7 @@ async def add_services(hass: HomeAssistant): async def async_handle_report(call): """Handle the service call""" config = hass.data.get(DOMAIN_DATA, {}) - path = get_report_path(hass, config.get(CONF_REPORT_PATH, None)) + path = await async_get_report_path(hass, config.get(CONF_REPORT_PATH, None)) send_notification = call.data.get(CONF_SEND_NOTIFICATION, False) create_file = call.data.get(CONF_CREATE_FILE, True) test_mode = call.data.get(CONF_TEST_MODE, False) @@ -211,7 +200,7 @@ async def add_services(hass: HomeAssistant): await async_notification(hass, "Watchman error", message, error=True) if call.data.get(CONF_PARSE_CONFIG, False): - parse_config(hass, reason="service call") + await parse_config(hass, reason="service call") if send_notification: chunk_size = call.data.get(CONF_CHUNK_SIZE, config.get(CONF_CHUNK_SIZE)) @@ -227,7 +216,7 @@ async def add_services(hass: HomeAssistant): error=True, ) - if onboarding(hass, service, path): + if await async_onboarding(hass, service, path): await async_notification( hass, "🖖 Achievement unlocked: first report!", @@ -271,7 +260,7 @@ async def add_event_handlers(hass: HomeAssistant): await coordinator.async_refresh() async def async_on_home_assistant_started(event): # pylint: disable=unused-argument - parse_config(hass, reason="HA restart") + await parse_config(hass, reason="HA restart") startup_delay = get_config(hass, CONF_STARTUP_DELAY, 0) await async_schedule_refresh_states(hass, startup_delay) @@ -284,12 +273,12 @@ async def add_event_handlers(hass: HomeAssistant): "reload_core_config", "reload", ]: - parse_config(hass, reason="configuration changes") + await parse_config(hass, reason="configuration changes") coordinator = hass.data[DOMAIN]["coordinator"] await coordinator.async_refresh() elif typ in [EVENT_AUTOMATION_RELOADED, EVENT_SCENE_RELOADED]: - parse_config(hass, reason="configuration changes") + await parse_config(hass, reason="configuration changes") coordinator = hass.data[DOMAIN]["coordinator"] await coordinator.async_refresh() @@ -341,14 +330,14 @@ async def add_event_handlers(hass: HomeAssistant): hass.data[DOMAIN]["cancel_handlers"] = hdlr -def parse_config(hass: HomeAssistant, reason=None): +async def parse_config(hass: HomeAssistant, reason=None): """parse home assistant configuration files""" assert hass.data.get(DOMAIN_DATA) start_time = time.time() included_folders = get_included_folders(hass) ignored_files = hass.data[DOMAIN_DATA].get(CONF_IGNORED_FILES, None) - entity_list, service_list, files_parsed, files_ignored = parse( + entity_list, service_list, files_parsed, files_ignored = await parse( hass, included_folders, ignored_files, hass.config.config_dir ) hass.data[DOMAIN]["entity_list"] = entity_list @@ -376,10 +365,10 @@ def get_included_folders(hass): config_folders = [hass.config.config_dir] for fld in config_folders: - folders.append(os.path.join(fld, "**/*.yaml")) + folders.append((fld, "**/*.yaml")) if DOMAIN_DATA in hass.data and hass.data[DOMAIN_DATA].get(CONF_CHECK_LOVELACE): - folders.append(os.path.join(hass.config.config_dir, ".storage/**/lovelace*")) + folders.append((hass.config.config_dir, ".storage/**/lovelace*")) return folders @@ -388,11 +377,16 @@ async def async_report_to_file(hass, path, test_mode): """save report to a file""" coordinator = hass.data[DOMAIN]["coordinator"] await coordinator.async_refresh() - report_chunks = report(hass, table_renderer, chunk_size=0, test_mode=test_mode) - # OSError exception is handled in async_handle_report - with open(path, "w", encoding="utf-8") as report_file: - for chunk in report_chunks: - report_file.write(chunk) + report_chunks = await report( + hass, table_renderer, chunk_size=0, test_mode=test_mode + ) + + def write(path): + with open(path, "w", encoding="utf-8") as report_file: + for chunk in report_chunks: + report_file.write(chunk) + + await hass.async_add_executor_job(write, path) async def async_report_to_notification(hass, service_str, service_data, chunk_size): @@ -423,7 +417,7 @@ async def async_report_to_notification(hass, service_str, service_data, chunk_si coordinator = hass.data[DOMAIN]["coordinator"] await coordinator.async_refresh() - report_chunks = report(hass, text_renderer, chunk_size) + report_chunks = await report(hass, text_renderer, chunk_size) for chunk in report_chunks: data["message"] = chunk # blocking=True ensures execution order @@ -446,7 +440,7 @@ async def async_notification(hass, title, message, error=False, n_id="watchman") raise HomeAssistantError(message.replace("`", "")) -def onboarding(hass, service, path): +async def async_onboarding(hass, service, path): """check if the user runs report for the first time""" service = service or get_config(hass, CONF_SERVICE_NAME, None) - return not (service or os.path.exists(path)) + return not (service or await Path(path).exists()) diff --git a/config/custom_components/watchman/config_flow.py b/config/custom_components/watchman/config_flow.py index dc3f681..5e6d0d9 100644 --- a/config/custom_components/watchman/config_flow.py +++ b/config/custom_components/watchman/config_flow.py @@ -1,4 +1,5 @@ "ConfigFlow definition for watchman" + from typing import Dict import json from json.decoder import JSONDecodeError @@ -7,7 +8,7 @@ from homeassistant.config_entries import ConfigFlow, OptionsFlow, ConfigEntry from homeassistant.core import callback from homeassistant.helpers import config_validation as cv, selector import voluptuous as vol -from .utils import is_service, get_columns_width, get_report_path +from .utils import is_service, get_columns_width, async_get_report_path from .const import ( DOMAIN, @@ -91,7 +92,7 @@ class OptionsFlowHandler(OptionsFlow): def __init__(self, config_entry: ConfigEntry) -> None: self.config_entry = config_entry - def default(self, key, uinput=None): + async def async_default(self, key, uinput=None): """provide default value for an OptionsFlow field""" if uinput and key in uinput: # supply last entered value to display an error during form validation @@ -105,7 +106,7 @@ class OptionsFlowHandler(OptionsFlow): if DEFAULT_DATA[key]: result = DEFAULT_DATA[key] elif key == CONF_REPORT_PATH: - result = get_report_path(self.hass, None) + result = await async_get_report_path(self.hass, None) if isinstance(result, list): return ", ".join([str(i) for i in result]) @@ -130,9 +131,7 @@ class OptionsFlowHandler(OptionsFlow): errors[key] = f"invalid_{key}" return val, errors - async def _show_options_form( - self, uinput=None, errors=None, placehoders=None - ): # pylint: disable=unused-argument + async def _show_options_form(self, uinput=None, errors=None, placehoders=None): # pylint: disable=unused-argument return self.async_show_form( step_id="init", data_schema=vol.Schema( @@ -140,19 +139,23 @@ class OptionsFlowHandler(OptionsFlow): vol.Optional( CONF_SERVICE_NAME, description={ - "suggested_value": self.default(CONF_SERVICE_NAME, uinput) + "suggested_value": await self.async_default( + CONF_SERVICE_NAME, uinput + ) }, ): cv.string, vol.Optional( CONF_SERVICE_DATA2, description={ - "suggested_value": self.default(CONF_SERVICE_DATA2, uinput) + "suggested_value": await self.async_default( + CONF_SERVICE_DATA2, uinput + ) }, ): selector.TemplateSelector(), vol.Optional( CONF_INCLUDED_FOLDERS, description={ - "suggested_value": self.default( + "suggested_value": await self.async_default( CONF_INCLUDED_FOLDERS, uinput ) }, @@ -162,19 +165,25 @@ class OptionsFlowHandler(OptionsFlow): vol.Optional( CONF_HEADER, description={ - "suggested_value": self.default(CONF_HEADER, uinput) + "suggested_value": await self.async_default( + CONF_HEADER, uinput + ) }, ): cv.string, vol.Optional( CONF_REPORT_PATH, description={ - "suggested_value": self.default(CONF_REPORT_PATH, uinput) + "suggested_value": await self.async_default( + CONF_REPORT_PATH, uinput + ) }, ): cv.string, vol.Optional( CONF_IGNORED_ITEMS, description={ - "suggested_value": self.default(CONF_IGNORED_ITEMS, uinput) + "suggested_value": await self.async_default( + CONF_IGNORED_ITEMS, uinput + ) }, ): selector.TextSelector( selector.TextSelectorConfig(multiline=True) @@ -182,7 +191,9 @@ class OptionsFlowHandler(OptionsFlow): vol.Optional( CONF_IGNORED_STATES, description={ - "suggested_value": self.default(CONF_IGNORED_STATES, uinput) + "suggested_value": await self.async_default( + CONF_IGNORED_STATES, uinput + ) }, ): selector.TextSelector( selector.TextSelectorConfig(multiline=True) @@ -190,13 +201,17 @@ class OptionsFlowHandler(OptionsFlow): vol.Optional( CONF_CHUNK_SIZE, description={ - "suggested_value": self.default(CONF_CHUNK_SIZE, uinput) + "suggested_value": await self.async_default( + CONF_CHUNK_SIZE, uinput + ) }, ): cv.positive_int, vol.Optional( CONF_IGNORED_FILES, description={ - "suggested_value": self.default(CONF_IGNORED_FILES, uinput) + "suggested_value": await self.async_default( + CONF_IGNORED_FILES, uinput + ) }, ): selector.TextSelector( selector.TextSelectorConfig(multiline=True) @@ -204,25 +219,33 @@ class OptionsFlowHandler(OptionsFlow): vol.Optional( CONF_COLUMNS_WIDTH, description={ - "suggested_value": self.default(CONF_COLUMNS_WIDTH, uinput) + "suggested_value": await self.async_default( + CONF_COLUMNS_WIDTH, uinput + ) }, ): cv.string, vol.Optional( CONF_STARTUP_DELAY, description={ - "suggested_value": self.default(CONF_STARTUP_DELAY, uinput) + "suggested_value": await self.async_default( + CONF_STARTUP_DELAY, uinput + ) }, ): cv.positive_int, vol.Optional( CONF_FRIENDLY_NAMES, description={ - "suggested_value": self.default(CONF_FRIENDLY_NAMES, uinput) + "suggested_value": await self.async_default( + CONF_FRIENDLY_NAMES, uinput + ) }, ): cv.boolean, vol.Optional( CONF_CHECK_LOVELACE, description={ - "suggested_value": self.default(CONF_CHECK_LOVELACE, uinput) + "suggested_value": await self.async_default( + CONF_CHECK_LOVELACE, uinput + ) }, ): cv.boolean, } diff --git a/config/custom_components/watchman/const.py b/config/custom_components/watchman/const.py index 304feb7..3af06bb 100644 --- a/config/custom_components/watchman/const.py +++ b/config/custom_components/watchman/const.py @@ -1,9 +1,10 @@ "definition of constants" + from homeassistant.const import Platform DOMAIN = "watchman" DOMAIN_DATA = "watchman_data" -VERSION = "0.6.1" +VERSION = "0.6.3" DEFAULT_REPORT_FILENAME = "watchman_report.txt" DEFAULT_HEADER = "-== WATCHMAN REPORT ==- " diff --git a/config/custom_components/watchman/entity.py b/config/custom_components/watchman/entity.py index 304c269..f7f0093 100644 --- a/config/custom_components/watchman/entity.py +++ b/config/custom_components/watchman/entity.py @@ -1,7 +1,7 @@ """Represents Watchman service in the device registry of Home Assistant""" -from homeassistant.helpers.entity import DeviceInfo, EntityDescription -from homeassistant.helpers.device_registry import DeviceEntryType +from homeassistant.helpers.entity import EntityDescription +from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo from homeassistant.helpers.update_coordinator import ( CoordinatorEntity, DataUpdateCoordinator, diff --git a/config/custom_components/watchman/manifest.json b/config/custom_components/watchman/manifest.json index d910df8..cd1106d 100644 --- a/config/custom_components/watchman/manifest.json +++ b/config/custom_components/watchman/manifest.json @@ -1,15 +1,15 @@ { "domain": "watchman", "name": "Watchman", - "documentation": "https://github.com/dummylabs/thewatchman", - "issue_tracker": "https://github.com/dummylabs/thewatchman/issues", - "iot_class": "local_push", - "version": "0.5.1", - "requirements": [ - "prettytable==3.0.0" - ], "codeowners": [ "@dummylabs" ], - "config_flow": true + "config_flow": true, + "documentation": "https://github.com/dummylabs/thewatchman", + "iot_class": "local_push", + "issue_tracker": "https://github.com/dummylabs/thewatchman/issues", + "requirements": [ + "prettytable==3.10.0" + ], + "version": "0.6.3" } \ No newline at end of file diff --git a/config/custom_components/watchman/sensor.py b/config/custom_components/watchman/sensor.py index db3550c..ddb2bac 100644 --- a/config/custom_components/watchman/sensor.py +++ b/config/custom_components/watchman/sensor.py @@ -1,9 +1,12 @@ """Watchman sensors definition""" + import logging from homeassistant.components.sensor import ( - SensorDeviceClass, SensorEntity, SensorEntityDescription, +) +from homeassistant.components.sensor.const import ( + SensorDeviceClass, SensorStateClass, ) from homeassistant.core import callback diff --git a/config/custom_components/watchman/services.yaml b/config/custom_components/watchman/services.yaml index e84f3a6..c6ad9b1 100644 --- a/config/custom_components/watchman/services.yaml +++ b/config/custom_components/watchman/services.yaml @@ -2,45 +2,33 @@ report: description: Run watchman report fields: create_file: - description: Whether report file should be created (optional, true by default) example: true - name: Create file report default: true required: false selector: boolean: send_notification: - description: Whether report should be sent via notification service (optional, false by default) example: true - name: Send notification default: false required: false selector: boolean: service: - description: Notification service to send report via (optional). Overrides "service" setting from watchman configuration example: "notify.telegram" - name: Notification service required: false selector: text: data: - description: Additional data in form of key:value pairs for notification service (optional) example: "parse_mode: html" - name: Notification service data parameters parse_config: - description: Parse configuration files before report is created. Usually this is done by watchman automatically, so this flag is not required. (optional, false by default) example: true - name: Parse configuration default: false required: false selector: boolean: chunk_size: - description: Maximum message size in bytes. If report size exceeds chunk_size, the report will be sent in several subsequent notifications. (optional, default is 3500 or whatever specified in integration settings) example: true - name: Chunk size default: false required: false selector: diff --git a/config/custom_components/watchman/translations/en.json b/config/custom_components/watchman/translations/en.json index 9002ed7..81ba047 100644 --- a/config/custom_components/watchman/translations/en.json +++ b/config/custom_components/watchman/translations/en.json @@ -41,5 +41,37 @@ "description": "[Help on settings](https://github.com/dummylabs/thewatchman#configuration)" } } + }, + "services": { + "report": { + "name": "Report", + "description": "Run the Watchman report", + "fields": { + "create_file": { + "name": "Create file report", + "description": "Whether report file should be created (optional, true by default)" + }, + "send_notification": { + "name": "Send notification", + "description": "Whether report should be sent via notification service (optional, false by default)" + }, + "service": { + "name": "Notification service", + "description": "Notification service to send report via (optional). Overrides 'service' setting from watchman configuration" + }, + "data": { + "name": "Notification service data parameters", + "description": "Additional data in form of key:value pairs for notification service (optional)" + }, + "parse_config": { + "name": "Force configuration parsing", + "description": "Parse configuration files before report is created. Usually this is done by watchman automatically, so this flag is not required. (optional, false by default)" + }, + "chunk_size": { + "name": "Report chunk size", + "description": "Maximum message size in bytes. If report size exceeds chunk_size, the report will be sent in several subsequent notifications. (optional, default is 3500 or whatever specified in integration settings)" + } + } + } } } \ No newline at end of file diff --git a/config/custom_components/watchman/utils.py b/config/custom_components/watchman/utils.py index f4141cd..ad666db 100644 --- a/config/custom_components/watchman/utils.py +++ b/config/custom_components/watchman/utils.py @@ -1,5 +1,6 @@ """Miscellaneous support functions for watchman""" -import glob + +import anyio import re import fnmatch import time @@ -58,12 +59,12 @@ def get_config(hass: HomeAssistant, key, default): return hass.data[DOMAIN_DATA].get(key, default) -def get_report_path(hass, path): +async def async_get_report_path(hass, path): """if path not specified, create report in config directory with default filename""" if not path: path = hass.config.path(DEFAULT_REPORT_FILENAME) folder, _ = os.path.split(path) - if not os.path.exists(folder): + if not await anyio.Path(folder).exists(): raise HomeAssistantError(f"Incorrect report_path: {path}.") return path @@ -147,16 +148,25 @@ def text_renderer(hass, entry_type): return f"Text render error: unknown entry type: {entry_type}" -def get_next_file(folder_list, ignored_files): +async def async_get_next_file(folder_tuples, ignored_files): """Returns next file for scan""" if not ignored_files: ignored_files = "" else: ignored_files = "|".join([f"({fnmatch.translate(f)})" for f in ignored_files]) ignored_files_re = re.compile(ignored_files) - for folder in folder_list: - for filename in glob.iglob(folder, recursive=True): - yield (filename, (ignored_files and ignored_files_re.match(filename))) + for folder_name, glob_pattern in folder_tuples: + _LOGGER.debug( + "Scan folder %s with pattern %s for configuration files", + folder_name, + glob_pattern, + ) + async for filename in anyio.Path(folder_name).glob(glob_pattern): + _LOGGER.debug("Found file %s.", filename) + yield ( + str(filename), + (ignored_files and ignored_files_re.match(str(filename))), + ) def add_entry(_list, entry, yaml_file, lineno): @@ -231,7 +241,7 @@ def check_entitites(hass): return entities_missing -def parse(hass, folders, ignored_files, root=None): +async def parse(hass, folders, ignored_files, root=None): """Parse a yaml or json file for entities/services""" files_parsed = 0 entity_pattern = re.compile( @@ -247,7 +257,7 @@ def parse(hass, folders, ignored_files, root=None): service_list = {} effectively_ignored = [] _LOGGER.debug("::parse started") - for yaml_file, ignored in get_next_file(folders, ignored_files): + async for yaml_file, ignored in async_get_next_file(folders, ignored_files): short_path = os.path.relpath(yaml_file, root) if ignored: effectively_ignored.append(short_path) @@ -255,19 +265,24 @@ def parse(hass, folders, ignored_files, root=None): continue try: - for i, line in enumerate(open(yaml_file, encoding="utf-8")): - line = re.sub(comment_pattern, "", line) - for match in re.finditer(entity_pattern, line): - typ, val = match.group(1), match.group(2) - if ( - typ != "service:" - and "*" not in val - and not val.endswith(".yaml") - ): - add_entry(entity_list, val, short_path, i + 1) - for match in re.finditer(service_pattern, line): - val = match.group(1) - add_entry(service_list, val, short_path, i + 1) + lineno = 1 + async with await anyio.open_file( + yaml_file, mode="r", encoding="utf-8" + ) as f: + async for line in f: + line = re.sub(comment_pattern, "", line) + for match in re.finditer(entity_pattern, line): + typ, val = match.group(1), match.group(2) + if ( + typ != "service:" + and "*" not in val + and not val.endswith(".yaml") + ): + add_entry(entity_list, val, short_path, lineno) + for match in re.finditer(service_pattern, line): + val = match.group(1) + add_entry(service_list, val, short_path, lineno) + lineno += 1 files_parsed += 1 _LOGGER.debug("%s parsed", yaml_file) except OSError as exception: @@ -312,9 +327,9 @@ def fill(data, width, extra=None): ) -def report(hass, render, chunk_size, test_mode=False): +async def report(hass, render, chunk_size, test_mode=False): """generates watchman report either as a table or as a list""" - if not DOMAIN in hass.data: + if DOMAIN not in hass.data: raise HomeAssistantError("No data for report, refresh required.") start_time = time.time() @@ -354,7 +369,11 @@ def report(hass, render, chunk_size, test_mode=False): rep += "your config are available!\n" else: rep += "\n-== No entities found in configuration files!\n" - timezone = pytz.timezone(hass.config.time_zone) + + def get_timezone(hass): + return pytz.timezone(hass.config.time_zone) + + timezone = await hass.async_add_executor_job(get_timezone, hass) if not test_mode: report_datetime = datetime.now(timezone).strftime("%d %b %Y %H:%M:%S") diff --git a/config/image/99e4c9606c65bc376616840729618fc5/512x512 b/config/image/99e4c9606c65bc376616840729618fc5/512x512 new file mode 100644 index 0000000..754f9eb Binary files /dev/null and b/config/image/99e4c9606c65bc376616840729618fc5/512x512 differ diff --git a/config/image/99e4c9606c65bc376616840729618fc5/original b/config/image/99e4c9606c65bc376616840729618fc5/original new file mode 100644 index 0000000..a8c7a96 Binary files /dev/null and b/config/image/99e4c9606c65bc376616840729618fc5/original differ diff --git a/config/packages/irrigation_unlimited_controls.yaml b/config/packages/irrigation_unlimited_controls.yaml index dd0324f..cc7c568 100644 --- a/config/packages/irrigation_unlimited_controls.yaml +++ b/config/packages/irrigation_unlimited_controls.yaml @@ -28,19 +28,19 @@ input_datetime: has_date: false has_time: true -automation: - - alias: Irrigation Unlimited Load UI Controls - trigger: - - platform: homeassistant - event: start - action: - - service: irrigation_unlimited.list_config - data: - entity_id: input_select.irrigation_unlimited_entities - section: entities - first: - - service: irrigation_unlimited.list_config - data: - entity_id: input_select.irrigation_unlimited_sequences - section: sequences - first: +# automation: +# - alias: Irrigation Unlimited Load UI Controls +# trigger: +# - platform: homeassistant +# event: start +# action: +# - service: irrigation_unlimited.list_config +# data: +# entity_id: input_select.irrigation_unlimited_entities +# section: entities +# first: +# - service: irrigation_unlimited.list_config +# data: +# entity_id: input_select.irrigation_unlimited_sequences +# section: sequences +# first: diff --git a/config/scripts.yaml b/config/scripts.yaml index 04a57b0..02c8d94 100644 --- a/config/scripts.yaml +++ b/config/scripts.yaml @@ -1,15 +1,68 @@ -'1717141935702': +aorus_reboot: + alias: Aorus - Reboot + sequence: + - service: mqtt.publish + data: + topic: iotlink/home/aorus/commands/reboot + payload: '' +aorus_shutdown: + alias: Aorus - Shutdown + sequence: + - service: mqtt.publish + data: + topic: iotlink/home/aorus/commands/shutdown + payload: '' +aorus_test: + alias: Aorus - test notify + sequence: + - service: mqtt.publish + data: + topic: iotlink/home/aorus/commands/notify + payload: '{ "title": "This is a test", "message": "Click here to go to flemmingss.com", + "launchParams": "toast://open/http://flemmingss.com" }' +wt32_sc01_wake_up: + alias: WT32 sc01 wake up + sequence: + - service: mqtt.publish + data: + topic: hasp/plate1/command + payload: 'backlight {"state": 1, "brightness": 232}' + qos: 0 + retain: false + mode: single +wt32_sc01_sleep: + alias: WT32 sc01 Sleep + sequence: + - service: mqtt.publish + data: + topic: hasp/plate1/command + payload: 'backlight {"state": 0, "brightness": 232}' + qos: 0 + retain: false + mode: single +purge_database: alias: purge database sequence: - service: recorder.purge data: repack: true apply_filter: true - keep_days: 5 + keep_days: 2 mode: single -'1717142010757': +clear_log: alias: clear log sequence: - service: system_log.clear data: {} mode: single + description: '' +watchman_report: + alias: watchman_report + sequence: + - service: watchman.report + data: + create_file: true + send_notification: false + parse_config: true + chunk_size: false + description: '' diff --git a/config/shopping_list.json b/config/shopping_list.json new file mode 100644 index 0000000..db367c5 --- /dev/null +++ b/config/shopping_list.json @@ -0,0 +1,252 @@ +[ + { + "name": "bananes", + "id": "ccd87c7b0ab34b2cab13e13f1c15b953", + "complete": false + }, + { + "name": "tomates en boites", + "id": "40f3826179cc4015abf05e854f0805b7", + "complete": false + }, + { + "name": "cafe", + "id": "2ab5e9e8e0bf4b66bf3709d42bf1862f", + "complete": false + }, + { + "name": "coca", + "id": "1fe1c6819f364efe8fb24f2076004884", + "complete": false + }, + { + "name": "lait", + "id": "b25f67fed52d48cd95f8e5ff2c19445a", + "complete": false + }, + { + "name": "Mandarinew", + "id": "399b702e0f5d4c668122aa443d12f5d7", + "complete": false + }, + { + "name": "pates", + "id": "a9afb0dc45d34be59590f75feaca1298", + "complete": false + }, + { + "name": "pq", + "id": "3552aa87fe114404afaa30756b915792", + "complete": false + }, + { + "name": "saussice strasbourg", + "id": "58722f42ca894eaebf69e33141747d3f", + "complete": false + }, + { + "name": "sucre", + "id": "48d77e0babb94314a53fb8eb5b982aec", + "complete": false + }, + { + "name": "tranche rostte", + "id": "77f5606c8d0f42ce956a92ea4d1a4a2e", + "complete": false + }, + { + "name": "2 pack 6 bouteille", + "id": "221a160c86e84932bb042a1c30152e3f", + "complete": false + }, + { + "name": "fromage frais1kg", + "id": "4a444956e0fa4090864d91271285faea", + "complete": false + }, + { + "name": "I3 les flottantes", + "id": "714e46d3bba245c98e996fca78aa5833", + "complete": false + }, + { + "name": "Desset pistache", + "id": "264e93338d3a4240bf4f37f845495ba4", + "complete": false + }, + { + "name": "Desset vanille", + "id": "cdd79df53003437781b11b8a63ca4f27", + "complete": false + }, + { + "name": "reIlle d or praline", + "id": "40120f3a21d545c5b55c13ff6d376cd8", + "complete": false + }, + { + "name": "Oranges", + "id": "262d56e80d5b43c29f5d1af87aff7547", + "complete": false + }, + { + "name": "Poires", + "id": "b25d659bcddb4417a855c3cf9ca52fcb", + "complete": false + }, + { + "name": "Kiwi", + "id": "8140a3c06f1c4dd4ba4d7023a1aa3d1c", + "complete": false + }, + { + "name": "2 baguette", + "id": "ae4b83ce164a4116a9dfe1cc8f2d259b", + "complete": false + }, + { + "name": "Jambon", + "id": "79f0e8fd4c7f488b9f836102c441723b", + "complete": false + }, + { + "name": "Beurre", + "id": "fa90d569094d43d39b271698a8313dae", + "complete": false + }, + { + "name": "2 boules 2 flutes", + "id": "700c9e55513e4218bef02e8eae7d2b0f", + "complete": false + }, + { + "name": "Lessives", + "id": "f10694ffdf0141b0ab25ee37e0e4c3b2", + "complete": false + }, + { + "name": "2 Sirop cassis ", + "id": "883245620efd4937af7f7609585eba7b", + "complete": false + }, + { + "name": "yaourt nature", + "id": "0bfffb52067248f9ba1864adf90a593a", + "complete": false + }, + { + "name": "2 flute", + "id": "6d61713c94734fda95d94a3476e58a48", + "complete": false + }, + { + "name": "6 bouteille demi écrémé ", + "id": "58a3db5909504e6593fdd34f6b225632", + "complete": false + }, + { + "name": "Cassis", + "id": "6a6e687e3ab8424eb358db3a21704b80", + "complete": false + }, + { + "name": "Lave vaisselle", + "id": "b872a56028fc4b5ba024b352ad0bb295", + "complete": false + }, + { + "name": "Compote", + "id": "0a6dfe1e9c92467c90dce7e4389bd991", + "complete": false + }, + { + "name": "Salade de fruit", + "id": "f2c3c5ad219843d5a036a4c18e2a2c82", + "complete": false + }, + { + "name": "Huile 3l", + "id": "2f1e4c7ad7764bccadc6450863ecce1c", + "complete": false + }, + { + "name": "Fromage bleu", + "id": "04f2c6de49a24bd687900e1027d2dd04", + "complete": false + }, + { + "name": "Fromage brebis frais", + "id": "df16358fba7b46028672e99684abb109", + "complete": false + }, + { + "name": "2 Flute ", + "id": "91f695f1522f4e798ffe43067c458f23", + "complete": false + }, + { + "name": "Nettoyant sol", + "id": "6a899a016d834f03a6148fdae419b8dc", + "complete": false + }, + { + "name": "Lave vitre", + "id": "6e34d4fd6a604c028c32a86e0732203b", + "complete": false + }, + { + "name": "Filtre n4 cafe", + "id": "4f3f5ae1015946028e9e534592ab62c5", + "complete": false + }, + { + "name": "Caroote rouge", + "id": "9ac5ffb47ea34888baeb6652272df190", + "complete": false + }, + { + "name": "Filet de truite nature ", + "id": "49901e8349ed48e2ac2e4484dc16a1ad", + "complete": false + }, + { + "name": "sirop cassis", + "id": "faa629cedc88434293e8264b909dc488", + "complete": false + }, + { + "name": "Dates", + "id": "0089116b159040ce903f4547f2ebdcce", + "complete": false + }, + { + "name": "Choco 2", + "id": "bd74c95f73f54c139ee79b017fa45c5b", + "complete": false + }, + { + "name": "Petis pois 2", + "id": "8d185228bd1048ba9e0c292fc1794586", + "complete": false + }, + { + "name": "Sucre", + "id": "3a0bea850f9b462e95598c797fc44756", + "complete": false + }, + { + "name": "Clementine", + "id": "5077437d5c1449669de10206b5ff5ff8", + "complete": false + }, + { + "name": "Haricot 2", + "id": "a7860c075ee940529bd3d41b910c1abf", + "complete": false + }, + { + "name": "Yaourt nature", + "id": "d351d89d82fd4aeba8ded4b99c63bed0", + "complete": false + } +] \ No newline at end of file diff --git a/config/ssh_keys/id_rsa_homeassistant b/config/ssh_keys/id_rsa_homeassistant new file mode 100644 index 0000000..713a0c1 --- /dev/null +++ b/config/ssh_keys/id_rsa_homeassistant @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACCjnJmRLV4OxZQCGOpuRVlDoFEia05K7XV+iWL2OPLKkwAAAJiE9VJMhPVS +TAAAAAtzc2gtZWQyNTUxOQAAACCjnJmRLV4OxZQCGOpuRVlDoFEia05K7XV+iWL2OPLKkw +AAAEC8EH07wKOzCuA5TPVu6VnHuYhDvTOPiuOJlm1r9nYcnaOcmZEtXg7FlAIY6m5FWUOg +USJrTkrtdX6JYvY48sqTAAAAEXJvb3RAYTBkN2I5NTQtc3NoAQIDBA== +-----END OPENSSH PRIVATE KEY----- diff --git a/config/ssh_keys/id_rsa_homeassistant.pub b/config/ssh_keys/id_rsa_homeassistant.pub new file mode 100644 index 0000000..b7cb4a6 --- /dev/null +++ b/config/ssh_keys/id_rsa_homeassistant.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKOcmZEtXg7FlAIY6m5FWUOgUSJrTkrtdX6JYvY48sqT root@a0d7b954-ssh diff --git a/config/watchman_report.txt b/config/watchman_report.txt index 8b71897..90c969f 100644 --- a/config/watchman_report.txt +++ b/config/watchman_report.txt @@ -1,79 +1,19 @@ -== Watchman Report ==- --== Missing 6 service(s) from 37 found in your config: -+--------------------------------+---------+--------------------------------------------------------------+ -| Service ID | State | Location | -+--------------------------------+---------+--------------------------------------------------------------+ -| tts.google_say | missing | automations.yaml:891,1543,1576,1942 | -| script.wt32_sc01_wake_up | missing | automations.yaml:952 | -| script.wt32_sc01_sleep | missing | automations.yaml:959 | -| script.purge_database | missing | automations.yaml:1839 | -| script.1714980028797 | missing | automations.yaml:2252 | -| irrigation_unlimited.list_conf | missing | packages/irrigation_unlimited_controls.yaml:37,42 | -| ig | | | -+--------------------------------+---------+--------------------------------------------------------------+ +-== Congratulations, all 43 services from your config are available! --== Missing 33 entity(ies) from 109 found in your config: +-== Missing 5 entity(ies) from 131 found in your config: +--------------------------------+---------+--------------------------------------------------------------+ | Entity ID | State | Location | +--------------------------------+---------+--------------------------------------------------------------+ -| binary_sensor.pir_sensor_2 | missing | automations.yaml:6,943 | -| climate.salon_3 | missing | automations.yaml:209,223,236,249 | -| switch.yoga | missing | automations.yaml:737,755,773 | -| sensor.battery_soc | missing | automations.yaml:1015,1048,1478 | -| select.charger_source_priority | missing | automations.yaml:1022,1053,1496,2137 | -| _2 | | | -| select.output_source_priority_ | missing | automations.yaml:1032,1063,1097,1107,1125,1506,2147 | -| 2 | | | -| sensor.load_power_3 | missing | automations.yaml:1090,1118 | -| switch.tasmota | missing | automations.yaml:1252,1422 | -| switch.ryzen | missing | automations.yaml:1261,1414,1431 | -| device_tracker.iphonex | missing | automations.yaml:1380,1399,1555,1690,1705 | -| switch.h801light_6f9188_h801_r | missing | automations.yaml:1613 | -| estart | | | -| switch.sonoff_4ch_restart_2 | missing | automations.yaml:1614 | -| switch.kc868_a8_d758d0_d758d0_ | missing | automations.yaml:1615 | -| kc868_a8_restart | | | -| switch.nmcuvoletporte_volet_po | missing | automations.yaml:1616 | -| rte_restart | | | -| switch.esp32_4_relays_garage_5 | missing | automations.yaml:1617,1618 | -| a10c8_esp32_4_relays_garage_re | | | -| start | | | -| switch.sonoff_4ch_garage_resta | missing | automations.yaml:1619 | -| rt | | | -| switch.nmcuvoletarriere1_volet | missing | automations.yaml:1621 | -| _arriere_restart | | | -| switch.nmcuvoletsalon1_volet_s | missing | automations.yaml:1622 | -| alon_1_restart | | | -| switch.nmcuvoletsalon2_volet_s | missing | automations.yaml:1623 | -| alon_2_restart | | | -| switch.sonoff_dressing_restart | missing | automations.yaml:1625 | -| _2 | | | -| switch.sonoff_escalier_restart | missing | automations.yaml:1626 | -| _2 | | | -| switch.nmcuvoletchambre1_volet | missing | automations.yaml:1627 | -| _chambre_1_restart | | | -| switch.volet_chambre_2_restart | missing | automations.yaml:1628 | -| _nmcuvoletchambre2 | | | -| switch.nmcuvoletcuisine1_volet | missing | automations.yaml:1629 | -| _cuisine_1_restart | | | -| switch.nmcuvoletcuisine2_volet | missing | automations.yaml:1630 | -| _cuisine_2_restart | | | -| switch.meuble_dashboard_restar | missing | automations.yaml:1631 | -| t | | | -| switch.wemos_pir_comble1_resta | missing | automations.yaml:1632 | -| rt_2 | | | -| switch.geiger_wemos_geiger_res | missing | automations.yaml:1633 | -| tart | | | -| automation.purge_db | unavail | automations.yaml:2239 | +| switch.yoga_12_screen_yoga12 | unavail | automations.yaml:740,749,769 | +| switch.ryzen | missing | automations.yaml:1171,1324,1341 | | sensor.alerte_pluie_inondation | unavail | 01capteur/template/template.yaml:43,177 | | sensor.alerte_grand_froid | unavail | 01capteur/template/template.yaml:123,186 | | sensor.athom_smart_plug_elegoo | missing | 01capteur/template/template.yaml:194 | | mars_1_wattage | | | -| sensor.energy_pj1203_energy_fl | missing | 01capteur/solar/solar_optimizer.yaml:12 | -| ow_b | | | +--------------------------------+---------+--------------------------------------------------------------+ --== Report created on 31 May 2024 13:03:30 --== Parsed 90 files in 0.53s., ignored 0 files --== Generated in: 0.01s. Validated in: 0.00s. +-== Report created on 08 Aug 2024 06:47:36 +-== Parsed 110 files in 11.16s., ignored 0 files +-== Generated in: 1.19s. Validated in: 0.00s. diff --git a/config/www/community/generic-remote-control-card/generic-remote-control-card.js b/config/www/community/generic-remote-control-card/generic-remote-control-card.js new file mode 100644 index 0000000..38aa549 --- /dev/null +++ b/config/www/community/generic-remote-control-card/generic-remote-control-card.js @@ -0,0 +1,106 @@ + +function loadScript(remote_template){ + if(window[`scriptLoaded_${remote_template}`]){ + return; + } + + var script = document.createElement("script"); + script.src = `/hacsfiles/generic-remote-control-card/remotes/${remote_template}/remote-html.js`; + script.type = "text/javascript"; + script.async = false; + document.head.appendChild(script); + window[`scriptLoaded_${remote_template}`] = true; + +} + + +class GenericRemotControlCard extends HTMLElement { + + constructor() { + super(); + this.attachShadow({ mode: 'open' }); + } + setConfig(config) { + loadScript(config.remote_template); + const root = this.shadowRoot; + if (root.lastChild) root.removeChild(root.lastChild); + + const cardConfig = Object.assign({}, config); + this._config = cardConfig + } + + + set hass(hass) { + + + + const config = this._config; + + loadScript(config.remote_template); + + try{ + const html = window[`getRemoteHtml_${config.remote_template}`](config); + const css = window[`getRemoteStyle_${config.remote_template}`](config); + + + const root = this.shadowRoot; + this._hass = hass; + // root.lastChild.hass = hass; + + const card = document.createElement('ha-card'); + if(!this.content && window[`scriptLoaded_${config.remote_template}`]){ + this.content = document.createElement('div'); + const style = document.createElement('style'); + style.textContent = css; + this.content.innerHTML = html; + card.appendChild(this.content); + card.appendChild(style); + + root.appendChild(card); + + this._bindButtons(card, this._hass, this._config); + } + + } catch(err){ + console.log('waiting for remote load'); + loadScript(config.remote_template); + } + + } + + + + _bindButtons(card, hass, config){ + var buttons = card.getElementsByClassName(`myButton-${config.remote_template}`); + var i; + for (i = 0; i < buttons.length; i++) { + let button = buttons[i] + button.addEventListener('click', function(source){ + console.log(button.id); + let buttonData = getButtonData(button.id, config); + console.log(buttonData) + let domain = buttonData.call.split(".")[0] + let action = buttonData.call.split(".")[1] + hass.callService(domain, + action, + buttonData.data + ); + }); + } + } + + + + + getCardSize() { + return 3; + } + + +} + + function getButtonData(buttonId, config){ + return config.buttons[buttonId]; + } + +customElements.define('generic-remote-control-card', GenericRemotControlCard); diff --git a/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/icn-backspace.png b/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/icn-backspace.png new file mode 100644 index 0000000..ec08f65 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/icn-backspace.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/icn-enter.png b/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/icn-enter.png new file mode 100644 index 0000000..2acff66 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/icn-enter.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/remote-html.js b/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/remote-html.js new file mode 100644 index 0000000..6c57543 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/android_custom_keyboard/remote-html.js @@ -0,0 +1,231 @@ +//definition section +var lineTable = [ + ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"], + ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"], + ["A", "S", "D", "F", "G", "H", "J", "K", "L"], + ["Z", "X", "C", "V", "B", "N", "M", "BACKSPACE"], + ["COMMA", "SPACE", "DOTT", "ENTER"], +]; +var noLetterKeys = [ + [8, "BACKSPACE"], + [13, "ENTER"], + [20, "CAPSLOCK"], + [32, "SPACE"], + [188, "COMMA"], + [190, "DOTT"], +]; +var linesNames = [ + "firstLine", + "secondLine", + "thirdLine", + "fourthLine", + "fivethLine", +]; + +function getRemoteStyle_android_custom_keyboard(config) { + const template = config.remote_template; + + return ` +ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } +body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +#remote-control-android_custom_keyboard { + position: relative; + width: 464px; + height: 244px +} + +#remote-control-android_custom_keyboard h2 { + position: absolute; + left: -5000px +} + +#remote-control-android_custom_keyboard div { + margin: 0; + padding: 0; + list-style-type: none +} + +.container { + width: 464px; + margin: 0 auto +} +div.buttonsLine { + height: 50px; + width: 464px; + margin: 5px; +} +div.keyButton { + position: relative; + height: 90%; + width: 9.85%; + border-radius: 20%; + color: white; + text-align: center; + font-family: Arial; + font-size: 100%; + margin-left: 1%; + margin-right: 1%; + float: left; +} +div.specialCharsField { + position: absolute; + border-radius: 20%; + background: #222222; + display: none; +} +div.specialChar { + position: relative; + background: none; + color: white; + text-align: center; + font-family: Arial; + font-size: 100%; + margin: 2px; + padding: 3px; + float: left; +} +div.letter { + user-select: none; + cursor: default; + position: relative; + top: 30%; +} +div.specialChar:hover, .specialCharHover { + background: #969696; +} +div.keyButton:active, .keyButtonActive { + transform: translate(1px, 1px); +} +div#btn-1, #btn-2, #btn-3, #btn-4, #btn-5, #btn-6, #btn-7, #btn-8, #btn-9, #btn-0, #btn-Q, #btn-W, #btn-E, #btn-R, #btn-T, #btn-Y, #btn-U, #btn-I, #btn-O, #btn-P{ + background: #333333; +} +div#btn-Z, #btn-X, #btn-C, #btn-V, #btn-B, #btn-N, #btn-M, #btn-COMMA, #btn-DOTT { + background: #333333; + left: 14.85%; +} +div#btn-A, #btn-S, #btn-D, #btn-F, #btn-G, #btn-H, #btn-J, #btn-K, #btn-L { + background: #333333; + left: 5%; +} +div#btn-BACKSPACE, #btn-ENTER { + background: #333333; + width: 14.85%; + border-radius: 12.3%; + left: 14.85%; +} +div#btn-SPACE { + background: #333333; + width: 49.85%; + border-radius: 4%; + left: 14.85%; +} +`; +} + +function getRemoteHtml_android_custom_keyboard(config) { + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}/`; + + var mainContainer = document.createElement("div"); + mainContainer.setAttribute("class", "container"); + + var newDiv = document.createElement("div"); + newDiv.setAttribute("style", "text-align:center"); + mainContainer.appendChild(newDiv); + + var newElement = document.createElement("h1"); + newElement.innerHTML = config.name; + newDiv.appendChild(newElement); + + var mainDiv = document.createElement("div"); + mainDiv.setAttribute("id", "remote-control-android_custom_keyboard"); + mainContainer.appendChild(mainDiv); + + newElement = document.createElement("h2"); + newElement.innerHTML = "Main navigation"; + mainDiv.appendChild(newElement); + + linesNames.forEach((itemId) => { + newDiv = document.createElement("div"); + newDiv.setAttribute("id", itemId); + newDiv.setAttribute("class", "buttonsLine"); + mainDiv.appendChild(newDiv); + }); + + var hyperlink = document.createElement("a"); + hyperlink.setAttribute("id", "hyperlink"); + hyperlink.setAttribute("href", "#"); + hyperlink.setAttribute("title", ""); + mainDiv.appendChild(hyperlink); + + //all letters' buttons printing + lineTable.forEach((lineLetters, index) => { + lineLetters.forEach((item) => { + createDivWithContent( + linesNames[index], + "btn-" + String(item), + "keyButton", + "sub-" + String(item), + "letter", + getContent(item, true) + ); + }); + }); + + //creates Button with Key name as a content + function createDivWithContent( + parentId, + mainId, + mainClass, + subId, + subClass, + content + ) { + var parentObj = mainContainer.querySelector("#" + parentId); + if (parentObj != null) { + var newDiv = document.createElement("div"); + newDiv.setAttribute("id", mainId); + newDiv.setAttribute("class", mainClass); + newDiv.classList.add("myButton-" + String(template)); + if (subId != null && subId != "") { + var subDiv = document.createElement("div"); + subDiv.setAttribute("id", subId); + subDiv.setAttribute("class", "letter"); + subDiv.appendChild(content); + newDiv.appendChild(subDiv); + } else newDiv.appendChild(content); + parentObj.appendChild(newDiv); + } else + console.log( + "createDivWithContent function couldn't create new div, because parent object with ID: " + + String(parentId) + + " doesn't exist." + ); + } + + function getContent(item, condition) { + if (item === "BACKSPACE") { + var content = document.createElement("img"); + content.setAttribute("src", base_url + "icn-backspace.png"); + } else if (item === "COMMA") var content = document.createTextNode(","); + else if (item === "SPACE") var content = document.createTextNode(" "); + else if (item === "DOTT") var content = document.createTextNode("."); + else if (item === "ENTER") { + var content = document.createElement("img"); + content.setAttribute("src", base_url + "icn-enter.png"); + } else + var content = document.createTextNode( + condition ? String(item).toUpperCase() : String(item).toLowerCase() + ); + return content; + } + return mainContainer.outerHTML; +} diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/apple4kremote.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/apple4kremote.png new file mode 100644 index 0000000..b01034f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/apple4kremote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/down.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/down.png new file mode 100644 index 0000000..aed0adf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/down.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/down_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/down_over.png new file mode 100644 index 0000000..4e0d645 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/down_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/left.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/left.png new file mode 100644 index 0000000..4bb6c2d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/left_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/left_over.png new file mode 100644 index 0000000..999a79c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/menu.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/menu.png new file mode 100644 index 0000000..d5ff6f4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/menu.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/menu_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/menu_over.png new file mode 100644 index 0000000..d3ded7f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/menu_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/play.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/play.png new file mode 100644 index 0000000..d2dd38b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/play.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/play_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/play_over.png new file mode 100644 index 0000000..72d556d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/play_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/remote-html.js b/config/www/community/generic-remote-control-card/remotes/apple4kremote/remote-html.js new file mode 100644 index 0000000..6f7c723 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/apple4kremote/remote-html.js @@ -0,0 +1,211 @@ +function getRemoteStyle_apple4kremote(config) { + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 360px; + margin: 0 auto +} + +#remote-control-apple4kremote { + position: relative; + background: url(${base_url}/apple4kremote.png) no-repeat; + width: 349px; + height: 930px; + margin: auto; +} + +#remote-control-apple4kremote ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#up a, +#left a, +#select a, +#right a, +#down a, +#menu a, +#tv a, +#siri a, +#volup a, +#play a, +#voldown a { + position: absolute; + display: block +} + +#remote-control-apple4kremote li#up a { + left: 124px; + top: 51px; + width: 101px; + height: 53px; + background: url(${base_url}/up.png) no-repeat +} + +#remote-control-apple4kremote li#left a { + left: 56px; + top: 106px; + width: 53px; + height: 101px; + background: url(${base_url}/left.png) no-repeat +} + +#remote-control-apple4kremote li#select a { + left: 121px; + top: 111px; + width: 104px; + height: 103px; + background: url(${base_url}/select.png) no-repeat +} + +#remote-control-apple4kremote li#right a { + left: 245px; + top: 106px; + width: 53px; + height: 101px; + background: url(${base_url}/right.png) no-repeat +} + +#remote-control-apple4kremote li#down a { + left: 124px; + top: 230px; + width: 101px; + height: 53px; + background: url(${base_url}/down.png) no-repeat +} + +#remote-control-apple4kremote li#menu a { + left: 63px; + top: 295px; + width: 101px; + height: 102px; + background: url(${base_url}/menu.png) no-repeat +} + +#remote-control-apple4kremote li#tv a { + left: 186px; + top: 295px; + width: 101px; + height: 102px; + background: url(${base_url}/tv.png) no-repeat +} + +#remote-control-apple4kremote li#siri a { + left: 63px; + top: 415px; + width: 100px; + height: 102px; + background: url(${base_url}/siri.png) no-repeat +} + +#remote-control-apple4kremote li#volup a { + left: 191px; + top: 417px; + width: 92px; + height: 101px; + background: url(${base_url}/volup.png) no-repeat +} + +#remote-control-apple4kremote li#play a { + left: 63px; + top: 536px; + width: 100px; + height: 100px; + background: url(${base_url}/play.png) no-repeat +} + +#remote-control-apple4kremote li#voldown a { + left: 191px; + top: 534px; + width: 92px; + height: 100px; + background: url(${base_url}/voldown.png) no-repeat +} + + +#remote-control-apple4kremote li#up a:hover { + background: url(${base_url}/up_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#left a:hover { + background: url(${base_url}/left_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#select a:hover { + background: url(${base_url}/select_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#right a:hover { + background: url(${base_url}/right_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#down a:hover { + background: url(${base_url}/down_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#menu a:hover { + background: url(${base_url}/menu_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#tv a:hover { + background: url(${base_url}/tv_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#siri a:hover { + background: url(${base_url}/siri_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#volup a:hover { + background: url(${base_url}/volup_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#play a:hover { + background: url(${base_url}/play_over.png) no-repeat 0px 0px +} + +#remote-control-apple4kremote li#voldown a:hover { + background: url(${base_url}/voldown_over.png) no-repeat 0px 0px +} + `; +} + +function getRemoteHtml_apple4kremote(config){ + const template = config.remote_template; + return ` + +
+
+

${config.name}

+
+
+
    +
  • +
  • +
  • + +
  • + +
  • +
  • +
  • +
  • +
  • +
+
+
+ +`; +} diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/right.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/right.png new file mode 100644 index 0000000..490ecca Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/right_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/right_over.png new file mode 100644 index 0000000..f732075 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/right_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/select.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/select.png new file mode 100644 index 0000000..05b4ed0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/select.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/select_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/select_over.png new file mode 100644 index 0000000..1b77dca Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/select_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/siri.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/siri.png new file mode 100644 index 0000000..27ccfcf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/siri.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/siri_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/siri_over.png new file mode 100644 index 0000000..e44f9e5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/siri_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/tv.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/tv.png new file mode 100644 index 0000000..e6b561b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/tv.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/tv_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/tv_over.png new file mode 100644 index 0000000..632a9da Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/tv_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/up.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/up.png new file mode 100644 index 0000000..ab87d0d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/up.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/up_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/up_over.png new file mode 100644 index 0000000..9fe7a71 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/up_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/voldown.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/voldown.png new file mode 100644 index 0000000..1a4312e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/voldown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/voldown_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/voldown_over.png new file mode 100644 index 0000000..5b94e8e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/voldown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/volup.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/volup.png new file mode 100644 index 0000000..f9014fe Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/volup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/apple4kremote/volup_over.png b/config/www/community/generic-remote-control-card/remotes/apple4kremote/volup_over.png new file mode 100644 index 0000000..fbd89d3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/apple4kremote/volup_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/assistant.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/assistant.png new file mode 100644 index 0000000..6171bfd Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/assistant.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/bottom.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/bottom.png new file mode 100644 index 0000000..9b00fb3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/bottom.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/clickleft.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/clickleft.png new file mode 100644 index 0000000..86d5e2f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/clickleft.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/clickright.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/clickright.png new file mode 100644 index 0000000..f9799e2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/clickright.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/go_back.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/go_back.png new file mode 100644 index 0000000..06af6e2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/go_back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/home.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/home.png new file mode 100644 index 0000000..9709d46 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/home.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/mute.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/mute.png new file mode 100644 index 0000000..7613f51 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/netflix.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/netflix.png new file mode 100644 index 0000000..b4a7052 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/netflix.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/ok.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/ok.png new file mode 100644 index 0000000..79dec31 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/ok.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/power.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/power.png new file mode 100644 index 0000000..f179358 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/remote-html.js b/config/www/community/generic-remote-control-card/remotes/ccwgtv/remote-html.js new file mode 100644 index 0000000..998f510 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/ccwgtv/remote-html.js @@ -0,0 +1,229 @@ +function getRemoteStyle_ccwgtv(config) { + + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif; + } + + .container { + width: 224px; + margin: 0 auto + } + + #remote-control-ccwgtv { + position: relative; + background: url(${base_url}/remote.png) no-repeat; + width: 178px; + height: 559px; + } + + span { + display: none; + } + + li { + display: inline-block; + } + + ul a { + display: inline-block; + position: absolute; + width: 29px; + height: 29px; + background-repeat: no-repeat; + background-position: center center; + background-size: contain; + border-radius: 50%; + transition: opacity 0.3s ease-in-out; + } + + ul a:hover, + ul a:focus, + ul a:active { + opacity: 0.3; + } + + #remote-control-ccwgtv ul { + margin: 0; + padding: 0; + list-style-type: none; + } + + #top a { + top: 15px; + background-image: url(${base_url}/top.png); + } + + #bottom a, + #ok a, + #top a { + left: 68px; + } + + #bottom a { + top: 117px; + background-image: url(${base_url}/bottom.png); + } + + + #clickleft a, + #clickright a, + #ok a { + top: 63px; + } + + #ok a { + background-image: url(${base_url}/ok.png); + } + + #clickleft a { + left: 18px; + background-image: url(${base_url}/clickleft.png); + } + + #clickright a { + left: 119px; + background-image: url(${base_url}/clickright.png); + } + + #back a { + left: 27px; + top: 174px; + width: 26px; + background-image: url(${base_url}/go_back.png); + } + + #assistant a { + left: 107px; + top: 167px; + width: 39px; + height: 41px; + background-image: url(${base_url}/assistant.png); + } + + + #home a, + #mute a { + top: 240px; + width: 26px; + height: 41px; + } + + #home a { + left: 30px; + background-image: url(${base_url}/home.png); + } + + #mute a { + left: 113px; + background-image: url(${base_url}/mute.png); + } + + #youtube a, + #netflix a { + top: 313px; + width: 45px; + height: 41px; + } + + #youtube a { + left: 20px; + background-image: url(${base_url}/youtube.png); + } + + #netflix a { + left: 109px; + background-image: url(${base_url}/netflix.png); + } + + + #power a, + #source a { + top: 388px; + transform: scale(0.8); + } + + #power a { + left: 30px; + background-image: url(${base_url}/power.png); + } + + #source a { + left: 118px; + background-image: url(${base_url}/source.png); + } + + .audio-controls { + position: absolute; + left: 168px; + top: 100px; + width: 40px; + background: #b8b7b1; + border-radius: 0; + border-top-right-radius: 14px; + border-bottom-right-radius: 14px; + height: 70px; + transform: rotate(-1.5deg); + } + + #volumeup a, + #volumedown a { + left: 5.5px; + width: 25px; + } + + #volumeup a { + top: 5px; + background-image: url(${base_url}/volume.png); + } + + #volumedown a { + bottom: 5px; + background-image: url(${base_url}/volumedown.png); + } + `; +} + +function getRemoteHtml_ccwgtv(config){ + const template = config.remote_template; + + return ` + + `; +} \ No newline at end of file diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/remote.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/remote.png new file mode 100644 index 0000000..cb8af8b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/remote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/source.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/source.png new file mode 100644 index 0000000..759ed3c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/source.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/top.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/top.png new file mode 100644 index 0000000..ee7f44a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/top.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/volume.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/volume.png new file mode 100644 index 0000000..c900c26 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/volume.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/volumedown.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/volumedown.png new file mode 100644 index 0000000..7fc1009 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/volumedown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/ccwgtv/youtube.png b/config/www/community/generic-remote-control-card/remotes/ccwgtv/youtube.png new file mode 100644 index 0000000..242e6eb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/ccwgtv/youtube.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-aux.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-aux.png new file mode 100644 index 0000000..81e5c93 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-aux.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-aux_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-aux_over.png new file mode 100644 index 0000000..48c9be4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-aux_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-bluetooth.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-bluetooth.png new file mode 100644 index 0000000..32f7fa3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-bluetooth.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-bluetooth_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-bluetooth_over.png new file mode 100644 index 0000000..8fbf239 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-bluetooth_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-cox.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-cox.png new file mode 100644 index 0000000..4e98e9a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-cox.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-cox_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-cox_over.png new file mode 100644 index 0000000..cb52e1c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-cox_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-next.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-next.png new file mode 100644 index 0000000..08954b4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-next.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-next_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-next_over.png new file mode 100644 index 0000000..37d6f29 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-next_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-opt.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-opt.png new file mode 100644 index 0000000..939dbd2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-opt.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-opt_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-opt_over.png new file mode 100644 index 0000000..4ca1f96 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-opt_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-pc.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-pc.png new file mode 100644 index 0000000..68a63f2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-pc.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-pc_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-pc_over.png new file mode 100644 index 0000000..d287c4d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-pc_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-play.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-play.png new file mode 100644 index 0000000..9289907 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-play.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-play_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-play_over.png new file mode 100644 index 0000000..4ed2efa Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-play_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-power.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-power.png new file mode 100644 index 0000000..956a1cb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-power_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-power_over.png new file mode 100644 index 0000000..b7f86ae Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-power_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-previous.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-previous.png new file mode 100644 index 0000000..7d611a1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-previous.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-previous_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-previous_over.png new file mode 100644 index 0000000..77a3837 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-previous_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-down.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-down.png new file mode 100644 index 0000000..a712851 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-down.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-down_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-down_over.png new file mode 100644 index 0000000..12384ee Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-down_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-up.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-up.png new file mode 100644 index 0000000..d357df1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-up.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-up_over.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-up_over.png new file mode 100644 index 0000000..a7926d2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/btn-vol-up_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/remote-background.png b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/remote-background.png new file mode 100644 index 0000000..a2794fe Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/remote-background.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/remote-html.js b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/remote-html.js new file mode 100644 index 0000000..12f8ebc --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/edifier_rc600a/remote-html.js @@ -0,0 +1,240 @@ +function getRemoteStyle_edifier_rc600a(config) { + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } +body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 492px; + margin: 0 auto +} + +#remote-control-edifier_rc600a { + position: relative; + background: url(${base_url}/remote-background.png) no-repeat; + width: 492px; + height: 517px +} + +#remote-control-edifier_rc600a h2, +#remote-control-edifier_rc600a span { + position: absolute; + left: -5000px +} + +#remote-control-edifier_rc600a div { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#opt a, +#pc a, +#cox a, +#aux a, +#bluetooth a, +#volumeup a, +#volumedown a, +#previous a, +#next a, +#play a { + position: absolute; + display: block +} + +#remote-control-edifier_rc600a div#power a { + left: 234px; + bottom: 396px; + width: 17px; + height: 20px; + background: url(${base_url}/btn-power.png) no-repeat +} + +#remote-control-edifier_rc600a div#power a:hover { + background-image: url(${base_url}/btn-power_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#opt a { + left: 105px; + bottom: 296px; + width: 27px; + height: 38px; + background: url(${base_url}/btn-opt.png) no-repeat +} + +#remote-control-edifier_rc600a div#opt a:hover { + background-image: url(${base_url}/btn-opt_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#pc a { + left: 358px; + bottom: 305px; + width: 22px; + height: 27px; + background: url(${base_url}/btn-pc.png) no-repeat +} + +#remote-control-edifier_rc600a div#pc a:hover { + background-image: url(${base_url}/btn-pc_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-edifier_rc600a div#cox a { + left: 107px; + bottom: 188px; + width: 25px; + height: 40px; + background: url(${base_url}/btn-cox.png) no-repeat +} + +#remote-control-edifier_rc600a div#cox a:hover { + background-image: url(${base_url}/btn-cox_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#aux a { + left: 357px; + bottom: 190px; + width: 25px; + height: 41px; + background: url(${base_url}/btn-aux.png) no-repeat +} + +#remote-control-edifier_rc600a div#aux a:hover { + background-image: url(${base_url}/btn-aux_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-edifier_rc600a div#bluetooth a { + left: 237px; + bottom: 114px; + width: 15px; + height: 20px; + background: url(${base_url}/btn-bluetooth.png) no-repeat +} + +#remote-control-edifier_rc600a div#bluetooth a:hover { + background-image: url(${base_url}/btn-bluetooth_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#volumeup a { + left: 233px; + bottom: 326px; + width: 21px; + height: 21px; + background: url(${base_url}/btn-vol-up.png) no-repeat +} + +#remote-control-edifier_rc600a div#volumeup a:hover { + background-image: url(${base_url}/btn-vol-up_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#volumedown a { + left: 234px; + bottom: 187px; + width: 20px; + height: 7px; + background: url(${base_url}/btn-vol-down.png) no-repeat +} + +#remote-control-edifier_rc600a div#volumedown a:hover { + background-image: url(${base_url}/btn-vol-down_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#previous a { + left: 160px; + bottom: 253px; + width: 27px; + height: 18px; + background: url(${base_url}/btn-previous.png) no-repeat +} + +#remote-control-edifier_rc600a div#previous a:hover { + background-image: url(${base_url}/btn-previous_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#next a { + left: 300px; + bottom: 255px; + width: 27px; + height: 18px; + background: url(${base_url}/btn-next.png) no-repeat +} + +#remote-control-edifier_rc600a div#next a:hover { + background-image: url(${base_url}/btn-next_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-edifier_rc600a div#play a { + left: 232px; + bottom: 255px; + width: 23px; + height: 18px; + background: url(${base_url}/btn-play.png) no-repeat +} + +#remote-control-edifier_rc600a div#play a:hover { + background-image: url(${base_url}/btn-play_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + `; +} + +function getRemoteHtml_edifier_rc600a(config){ + const template = config.remote_template; + return ` +
+
+

${config.name}

+
+
+

+ Main navigation +

+ + + + + + + + + + + + + +
+
+`; +} \ No newline at end of file diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/back.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/back.png new file mode 100644 index 0000000..6dadcb0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/back_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/back_over.png new file mode 100644 index 0000000..fd75382 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/back_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/down.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/down.png new file mode 100644 index 0000000..27c1c1e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/down.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/down_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/down_over.png new file mode 100644 index 0000000..c731f97 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/down_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/firestick4kremote.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/firestick4kremote.png new file mode 100644 index 0000000..88aef6d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/firestick4kremote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/fwd.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/fwd.png new file mode 100644 index 0000000..fcb82ca Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/fwd.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/fwd_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/fwd_over.png new file mode 100644 index 0000000..32375f9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/fwd_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/home.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/home.png new file mode 100644 index 0000000..dc844ca Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/home.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/home_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/home_over.png new file mode 100644 index 0000000..477c409 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/home_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/left.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/left.png new file mode 100644 index 0000000..521a04f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/left_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/left_over.png new file mode 100644 index 0000000..845b2d1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/menu.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/menu.png new file mode 100644 index 0000000..711a194 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/menu.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/menu_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/menu_over.png new file mode 100644 index 0000000..59aeff6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/menu_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mic.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mic.png new file mode 100644 index 0000000..d11297d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mic.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mic_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mic_over.png new file mode 100644 index 0000000..1e6a0a2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mic_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mute.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mute.png new file mode 100644 index 0000000..3b1a6aa Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mute_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mute_over.png new file mode 100644 index 0000000..702376b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/mute_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/play.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/play.png new file mode 100644 index 0000000..2e3e835 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/play.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/play_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/play_over.png new file mode 100644 index 0000000..3544830 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/play_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/pwr.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/pwr.png new file mode 100644 index 0000000..d6a7ac0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/pwr.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/pwr_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/pwr_over.png new file mode 100644 index 0000000..7e2cfa7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/pwr_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/remote-html.js b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/remote-html.js new file mode 100644 index 0000000..5c239cd --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/remote-html.js @@ -0,0 +1,289 @@ +function getRemoteStyle_firestick4kremote(config) { + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 360px; + margin: 0 auto +} + +#remote-control-firestick4kremote { + position: relative; + background: url(${base_url}/firestick4kremote.png) no-repeat; + width: 332px; + height: 768px +} + +#remote-control-firestick4kremote h2, +#remote-control-firestick4kremote span { + position: absolute; + left: 5000px +} + +#remote-control-firestick4kremote ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#pwr a, +#mic a, +#volup a, +#voldown a, +#mute a, +#up a, +#left a, +#select a, +#right a, +#down a, +#back a, +#home a, +#menu a, +#rwd a, +#play a, +#fwd a { + position: absolute; + disrwd: block +} + +#remote-control-firestick4kremote li#pwr a { + left: 86px; + top: 27px; + width: 36px; + height: 36px; + background: url(${base_url}/pwr.png) no-repeat +} + +#remote-control-firestick4kremote li#mic a { + left: 140px; + top: 68px; + width: 49px; + height: 49px; + background: url(${base_url}/mic.png) no-repeat +} + +#remote-control-firestick4kremote li#volup a { + left: 140px; + top: 428px; + width: 49px; + height: 49px; + background: url(${base_url}/volup.png) no-repeat +} + +#remote-control-firestick4kremote li#voldown a { + left: 140px; + top: 485px; + width: 49px; + height: 49px; + background: url(${base_url}/voldown.png) no-repeat +} + +#remote-control-firestick4kremote li#mute a { + left: 140px; + top: 548px; + width: 49px; + height: 49px; + background: url(${base_url}/mute.png) no-repeat +} + +#remote-control-firestick4kremote li#up a { + left: 120px; + top: 129px; + width: 89px; + height: 42px; + background: url(${base_url}/up.png) no-repeat +} + +#remote-control-firestick4kremote li#left a { + left: 78px; + top: 169px; + width: 42px; + height: 89px; + background: url(${base_url}/left.png) no-repeat +} + +#remote-control-firestick4kremote li#select a { + left: 134px; + top: 183px; + width: 63px; + height: 63px; + background: url(${base_url}/select.png) no-repeat +} + +#remote-control-firestick4kremote li#right a { + left: 210px; + top: 169px; + width: 42px; + height: 89px; + background: url(${base_url}/right.png) no-repeat +} + +#remote-control-firestick4kremote li#down a { + left: 120px; + top: 256px; + width: 89px; + height: 42px; + background: url(${base_url}/down.png) no-repeat +} + +#remote-control-firestick4kremote li#back a { + left: 80px; + top: 308px; + width: 49px; + height: 49px; + background: url(${base_url}/back.png) no-repeat +} + +#remote-control-firestick4kremote li#home a { + left: 140px; + top: 308px; + width: 49px; + height: 49px; + background: url(${base_url}/home.png) no-repeat +} + +#remote-control-firestick4kremote li#menu a { + left: 200px; + top: 308px; + width: 49px; + height: 49px; + background: url(${base_url}/menu.png) no-repeat +} + +#remote-control-firestick4kremote li#rwd a { + left: 80px; + top: 367px; + width: 49px; + height: 49px; + background: url(${base_url}/rwd.png) no-repeat +} + +#remote-control-firestick4kremote li#play a { + left: 140px; + top: 367px; + width: 49px; + height: 49px; + background: url(${base_url}/play.png) no-repeat +} + +#remote-control-firestick4kremote li#fwd a { + left: 200px; + top: 367px; + width: 49px; + height: 49px; + background: url(${base_url}/fwd.png) no-repeat +} + + +#remote-control-firestick4kremote li#pwr a:hover { + background: url(${base_url}/pwr_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#mic a:hover { + background: url(${base_url}/mic_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#volup a:hover { + background: url(${base_url}/volup_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#voldown a:hover { + background: url(${base_url}/voldown_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#mute a:hover { + background: url(${base_url}/mute_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#up a:hover { + background: url(${base_url}/up_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#left a:hover { + background: url(${base_url}/left_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#select a:hover { + background: url(${base_url}/select_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#right a:hover { + background: url(${base_url}/right_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#down a:hover { + background: url(${base_url}/down_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#back a:hover { + background: url(${base_url}/back_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#home a:hover { + background: url(${base_url}/home_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#menu a:hover { + background: url(${base_url}/menu_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#rwd a:hover { + background: url(${base_url}/rwd_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#play a:hover { + background: url(${base_url}/play_over.png) no-repeat 0px 0px +} + +#remote-control-firestick4kremote li#fwd a:hover { + background: url(${base_url}/fwd_over.png) no-repeat 0px 0px +} + `; +} + +function getRemoteHtml_firestick4kremote(config){ + const template = config.remote_template; + return ` + +
+
+

${config.name}

+
+
+

+ Main navigation +

+ +
+
+ +`; +} diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/right.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/right.png new file mode 100644 index 0000000..331ca0e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/right_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/right_over.png new file mode 100644 index 0000000..fbe1c52 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/right_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/rwd.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/rwd.png new file mode 100644 index 0000000..75f3792 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/rwd.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/rwd_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/rwd_over.png new file mode 100644 index 0000000..a197cf9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/rwd_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/select.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/select.png new file mode 100644 index 0000000..bb08b7b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/select.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/select_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/select_over.png new file mode 100644 index 0000000..58e529a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/select_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/up.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/up.png new file mode 100644 index 0000000..6e7ebe1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/up.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/up_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/up_over.png new file mode 100644 index 0000000..b997151 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/up_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/voldown.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/voldown.png new file mode 100644 index 0000000..aafb7b0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/voldown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/voldown_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/voldown_over.png new file mode 100644 index 0000000..a129129 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/voldown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/volup.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/volup.png new file mode 100644 index 0000000..f0a2580 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/volup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/firestick4kremote/volup_over.png b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/volup_over.png new file mode 100644 index 0000000..414ebab Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/firestick4kremote/volup_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-av-mute.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-av-mute.png new file mode 100644 index 0000000..0f51dae Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-av-mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-av-mute_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-av-mute_over.png new file mode 100644 index 0000000..5d52f08 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-av-mute_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-down.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-down.png new file mode 100644 index 0000000..3228058 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-down.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-down_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-down_over.png new file mode 100644 index 0000000..ab81cc7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-down_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eco.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eco.png new file mode 100644 index 0000000..a61dd09 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eco.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eco_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eco_over.png new file mode 100644 index 0000000..3861593 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eco_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eight.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eight.png new file mode 100644 index 0000000..bb1bcda Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eight.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eight_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eight_over.png new file mode 100644 index 0000000..0fec2c5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-eight_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-enter.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-enter.png new file mode 100644 index 0000000..08ee130 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-enter.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-enter_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-enter_over.png new file mode 100644 index 0000000..5f32da4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-enter_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-five.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-five.png new file mode 100644 index 0000000..5d038f5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-five.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-five_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-five_over.png new file mode 100644 index 0000000..e85187c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-five_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-four.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-four.png new file mode 100644 index 0000000..1857fc7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-four.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-four_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-four_over.png new file mode 100644 index 0000000..df49032 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-four_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-left.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-left.png new file mode 100644 index 0000000..724b361 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-left_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-left_over.png new file mode 100644 index 0000000..19d71e7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-menu.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-menu.png new file mode 100644 index 0000000..bcd5727 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-menu.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-menu_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-menu_over.png new file mode 100644 index 0000000..5dee769 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-menu_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-minus.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-minus.png new file mode 100644 index 0000000..a4c5f3b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-minus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-minus_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-minus_over.png new file mode 100644 index 0000000..ddbebf8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-minus_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-nine.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-nine.png new file mode 100644 index 0000000..5f00b94 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-nine.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-nine_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-nine_over.png new file mode 100644 index 0000000..80b73c3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-nine_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-one.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-one.png new file mode 100644 index 0000000..2ba6981 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-one.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-one_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-one_over.png new file mode 100644 index 0000000..fc29763 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-one_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-over.png new file mode 100644 index 0000000..32f655e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-plus.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-plus.png new file mode 100644 index 0000000..2115c26 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-plus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-plus_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-plus_over.png new file mode 100644 index 0000000..d8fb557 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-plus_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-power.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-power.png new file mode 100644 index 0000000..cd4aabc Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-power_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-power_over.png new file mode 100644 index 0000000..fbf3756 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-power_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-re-sync_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-re-sync_over.png new file mode 100644 index 0000000..ca8a3fa Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-re-sync_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-right.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-right.png new file mode 100644 index 0000000..c77e19d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-right_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-right_over.png new file mode 100644 index 0000000..3fb2bef Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-right_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-seven.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-seven.png new file mode 100644 index 0000000..e1e61e8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-seven.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-seven_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-seven_over.png new file mode 100644 index 0000000..92206e0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-seven_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-six.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-six.png new file mode 100644 index 0000000..ae0283a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-six.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-six_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-six_over.png new file mode 100644 index 0000000..0b5bde4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-six_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-source_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-source_over.png new file mode 100644 index 0000000..49305d8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-source_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-three.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-three.png new file mode 100644 index 0000000..a27c6f8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-three.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-three_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-three_over.png new file mode 100644 index 0000000..5315462 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-three_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-two.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-two.png new file mode 100644 index 0000000..49cf6bb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-two.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-two_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-two_over.png new file mode 100644 index 0000000..c209efd Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-two_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-up.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-up.png new file mode 100644 index 0000000..8d24649 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-up.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-up_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-up_over.png new file mode 100644 index 0000000..5eee0dd Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-up_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-zero.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-zero.png new file mode 100644 index 0000000..50c1306 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-zero.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-zero_over.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-zero_over.png new file mode 100644 index 0000000..c997477 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/btn-zero_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/remote-background.png b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/remote-background.png new file mode 100644 index 0000000..e196114 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/remote-background.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/remote-html.js b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/remote-html.js new file mode 100644 index 0000000..0aaeb02 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/infocus_ir1109r1/remote-html.js @@ -0,0 +1,511 @@ +function getRemoteStyle_infocus_ir1109r1(config) { + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } +body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 215px; + margin: 0 auto +} + +#remote-control-infocus_ir1109r1 { + position: relative; + background: url(${base_url}/remote-background.png) no-repeat; + width: 215px; + height: 717px +} + +#remote-control-infocus_ir1109r1 h2, +#remote-control-infocus_ir1109r1 span { + position: absolute; + left: -5000px +} + +#remote-control-infocus_ir1109r1 div { + margin: 0; + padding: 0; + list-style-type: none +} + +#avmute a, +#power a, +#resync a, +#source a, +#up a, +#left a, +#enter a, +#right a, +#down a, +#menu a, +#eco a, +#keystoneup a, +#keystonedown a, +#volumeup a, +#volumedown a, +#one a, +#two a, +#three a, +#four a, +#five a, +#six a, +#seven a, +#eight a, +#nine a, +#mouse a, +#zero a, +#mute a { + position: absolute; + display: block +} + +#remote-control-infocus_ir1109r1 div#avmute a { + left: 39px; + bottom: 661px; + width: 45px; + height: 12px; + background: url(${base_url}/btn-av-mute.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#avmute a:hover { + background-image: url(${base_url}/btn-av-mute_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#power a { + left: 139px; + bottom: 655px; + width: 21px; + height: 25px; + background: url(${base_url}/btn-power.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#power a:hover { + background-image: url(${base_url}/btn-power_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#resync a { + left: 19px; + bottom: 584px; + width: 34px; + height: 36px; + background: none +} + +#remote-control-infocus_ir1109r1 div#resync a:hover { + background-image: url(${base_url}/btn-re-sync_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#source a { + left: 157px; + bottom: 585px; + width: 34px; + height: 36px; + background: none +} + +#remote-control-infocus_ir1109r1 div#source a:hover { + background-image: url(${base_url}/btn-source_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#up a { + left: 98px; + bottom: 593px; + width: 17px; + height: 17px; + background: url(${base_url}/btn-up.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#up a:hover { + background-image: url(${base_url}/btn-up_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#left a { + left: 27px; + bottom: 523px; + width: 17px; + height: 17px; + background: url(${base_url}/btn-left.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#left a:hover { + background-image: url(${base_url}/btn-left_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#enter a { + left: 90px; + bottom: 526px; + width: 31px; + height: 12px; + background: url(${base_url}/btn-enter.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#enter a:hover { + background-image: url(${base_url}/btn-enter_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#right a { + left: 168px; + bottom: 525px; + width: 17px; + height: 17px; + background: url(${base_url}/btn-right.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#right a:hover { + background-image: url(${base_url}/btn-right_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#down a { + left: 97px; + bottom: 456px; + width: 17px; + height: 17px; + background: url(${base_url}/btn-down.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#down a:hover { + background-image: url(${base_url}/btn-down_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#menu a { + left: 22px; + bottom: 446px; + width: 30px; + height: 12px; + background: url(${base_url}/btn-menu.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#menu a:hover { + background-image: url(${base_url}/btn-menu_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#eco a { + left: 163px; + bottom: 447px; + width: 27px; + height: 12px; + background: url(${base_url}/btn-eco.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#eco a:hover { + background-image: url(${base_url}/btn-eco_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#keystoneup a { + left: 50px; + bottom: 385px; + width: 24px; + height: 24px; + background: url(${base_url}/btn-plus.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#keystoneup a:hover { + background-image: url(${base_url}/btn-plus_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#keystonedown a { + left: 51px; + bottom: 326px; + width: 21px; + height: 7px; + background: url(${base_url}/btn-minus.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#keystonedown a:hover { + background-image: url(${base_url}/btn-minus_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} +#remote-control-infocus_ir1109r1 div#volumeup a { + left: 139px; + bottom: 385px; + width: 24px; + height: 24px; + background: url(${base_url}/btn-plus.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#volumeup a:hover { + background-image: url(${base_url}/btn-plus_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#volumedown a { + left: 140px; + bottom: 326px; + width: 21px; + height: 7px; + background: url(${base_url}/btn-minus.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#volumedown a:hover { + background-image: url(${base_url}/btn-minus_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#one a { + left: 42px; + bottom: 254px; + width: 6px; + height: 11px; + background: url(${base_url}/btn-one.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#one a:hover { + background-image: url(${base_url}/btn-one_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#two a { + left: 102px; + bottom: 253px; + width: 8px; + height: 10px; + background: url(${base_url}/btn-two.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#two a:hover { + background-image: url(${base_url}/btn-two_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-infocus_ir1109r1 div#three a { + left: 162px; + bottom: 253px; + width: 8px; + height: 11px; + background: url(${base_url}/btn-three.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#three a:hover { + background-image: url(${base_url}/btn-three_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#four a { + left: 35px; + bottom: 206px; + width: 21px; + height: 12px; + background: url(${base_url}/btn-four.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#four a:hover { + background-image: url(${base_url}/btn-four_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#five a { + left: 101px; + bottom: 206px; + width: 9px; + height: 12px; + background: url(${base_url}/btn-five.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#five a:hover { + background-image: url(${base_url}/btn-five_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#six a { + left: 154px; + bottom: 206px; + width: 25px; + height: 12px; + background: url(${base_url}/btn-six.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#six a:hover { + background-image: url(${base_url}/btn-six_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#seven a { + left: 34px; + bottom: 160px; + width: 21px; + height: 12px; + background: url(${base_url}/btn-seven.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#seven a:hover { + background-image: url(${base_url}/btn-seven_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#eight a { + left: 102px; + bottom: 160px; + width: 9px; + height: 12px; + background: url(${base_url}/btn-eight.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#eight a:hover { + background-image: url(${base_url}/btn-eight_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#nine a { + left: 155px; + bottom: 160px; + width: 21px; + height: 12px; + background: url(${base_url}/btn-nine.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#nine a:hover { + background-image: url(${base_url}/btn-nine_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#mouse a { + left: 28px; + bottom: 106px; + width: 36px; + height: 28px; + background: none +} + +#remote-control-infocus_ir1109r1 div#mouse a:hover { + background-image: url(${base_url}/btn-over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#zero a { + left: 102px; + bottom: 114px; + width: 9px; + height: 12px; + background: url(${base_url}/btn-zero.png) no-repeat +} + +#remote-control-infocus_ir1109r1 div#zero a:hover { + background-image: url(${base_url}/btn-zero_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-infocus_ir1109r1 div#mute a { + left: 148px; + bottom: 106px; + width: 36px; + height: 28px; + background: none +} + +#remote-control-infocus_ir1109r1 div#mute a:hover { + background-image: url(${base_url}/btn-over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + + + `; +} + +function getRemoteHtml_infocus_ir1109r1(config){ + const template = config.remote_template; + return ` +
+
+

${config.name}

+
+ +
+`; +} diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/auxiliar.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/auxiliar.png new file mode 100644 index 0000000..328940f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/auxiliar.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/auxiliar_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/auxiliar_over.png new file mode 100644 index 0000000..893b728 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/auxiliar_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/bluetooth.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/bluetooth.png new file mode 100644 index 0000000..379077a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/bluetooth.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/bluetooth_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/bluetooth_over.png new file mode 100644 index 0000000..6c652af Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/bluetooth_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/feedback.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/feedback.png new file mode 100644 index 0000000..ae25028 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/feedback.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/feedback_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/feedback_over.png new file mode 100644 index 0000000..fd4f1a4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/feedback_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/harman.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/harman.png new file mode 100644 index 0000000..511a4bb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/harman.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/harman_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/harman_over.png new file mode 100644 index 0000000..325a2d0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/harman_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/jblsoundbar_remote.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/jblsoundbar_remote.png new file mode 100644 index 0000000..2286e1b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/jblsoundbar_remote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/minus.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/minus.png new file mode 100644 index 0000000..4ab7b68 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/minus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/minus_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/minus_over.png new file mode 100644 index 0000000..fdc39e8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/minus_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/mute.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/mute.png new file mode 100644 index 0000000..61d706b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/mute_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/mute_over.png new file mode 100644 index 0000000..d9103e7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/mute_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/optical.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/optical.png new file mode 100644 index 0000000..278cc23 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/optical.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/optical_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/optical_over.png new file mode 100644 index 0000000..2e1ccc1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/optical_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/plus.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/plus.png new file mode 100644 index 0000000..75b0a26 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/plus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/plus_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/plus_over.png new file mode 100644 index 0000000..d4f36c0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/plus_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/power.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/power.png new file mode 100644 index 0000000..4255245 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/power_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/power_over.png new file mode 100644 index 0000000..4c9c6e0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/power_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/remote-html.js b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/remote-html.js new file mode 100644 index 0000000..d8aac01 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/remote-html.js @@ -0,0 +1,260 @@ +function getRemoteStyle_jblsoundbar(config) { + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 360px; + margin: 0 auto +} + +#remote-control-jblsoundbar { + position: relative; + background: url(${base_url}/jblsoundbar_remote.png) no-repeat; + width: 327px; + height: 692px +} + +#remote-control-jblsoundbar h2, +#remote-control-jblsoundbar span { + position: absolute; + left: 5000px +} + +#remote-control-jblsoundbar ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#bluetooth a, +#source a, +#aux a, +#optical a, +#feedback a, +#minus a, +#mute a, +#plus a, +#bassminus a, +#bassplus a, +#surround a, +#stereo a, +#harman a { + position: absolute; + display: block +} + +#remote-control-jblsoundbar li#power a { + left: 56px; + top: 75px; + width: 38px; + height: 38px; + background: url(${base_url}/power.png) no-repeat +} + +#remote-control-jblsoundbar li#bluetooth a { + left: 147px; + top: 74px; + width: 38px; + height: 38px; + background: url(${base_url}/bluetooth.png) no-repeat +} + +#remote-control-jblsoundbar li#source a { + left: 240px; + top: 74px; + width: 38px; + height: 38px; + background: url(${base_url}/source.png) no-repeat +} + +#remote-control-jblsoundbar li#aux a { + left: 56px; + top: 158px; + width: 38px; + height: 38px; + background: url(${base_url}/auxiliar.png) no-repeat +} + +#remote-control-jblsoundbar li#optical a { + left: 148px; + top: 157px; + width: 38px; + height: 38px; + background: url(${base_url}/optical.png) no-repeat +} + +#remote-control-jblsoundbar li#feedback a { + left: 233px; + top: 157px; + width: 46px; + height: 38px; + background: url(${base_url}/feedback.png) no-repeat +} + +#remote-control-jblsoundbar li#minus a { + left: 55px; + top: 239px; + width: 38px; + height: 38px; + background: url(${base_url}/minus.png) no-repeat +} + +#remote-control-jblsoundbar li#mute a { + left: 148px; + top: 239px; + width: 38px; + height: 38px; + background: url(${base_url}/mute.png) no-repeat +} + +#remote-control-jblsoundbar li#plus a { + left: 238px; + top: 239px; + width: 38px; + height: 38px; + background: url(${base_url}/plus.png) no-repeat +} + +#remote-control-jblsoundbar li#bassminus a { + left: 55px; + top: 322px; + width: 38px; + height: 38px; + background: url(${base_url}/minus.png) no-repeat +} + +#remote-control-jblsoundbar li#bassplus a { + left: 238px; + top: 322px; + width: 38px; + height: 38px; + background: url(${base_url}/plus.png) no-repeat +} + +#remote-control-jblsoundbar li#surround a { + left: 55px; + top: 405px; + width: 38px; + height: 38px; + background: url(${base_url}/surround.png) no-repeat +} + +#remote-control-jblsoundbar li#stereo a { + left: 147px; + top: 406px; + width: 38px; + height: 38px; + background: url(${base_url}/stereo.png) no-repeat +} + +#remote-control-jblsoundbar li#harman a { + left: 238px; + top: 406px; + width: 38px; + height: 38px; + background: url(${base_url}/harman.png) no-repeat +} + +#remote-control-jblsoundbar li#power a:hover { + background: url(${base_url}/power_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#bluetooth a:hover { + background: url(${base_url}/bluetooth_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#source a:hover { + background: url(${base_url}/source_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#aux a:hover { + background: url(${base_url}/auxiliar_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#optical a:hover { + background: url(${base_url}/optical_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#feedback a:hover { + background: url(${base_url}/feedback_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#minus a:hover { + background: url(${base_url}/minus_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#mute a:hover { + background: url(${base_url}/mute_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#plus a:hover { + background: url(${base_url}/plus_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#bassminus a:hover { + background: url(${base_url}/minus_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#bassplus a:hover { + background: url(${base_url}/plus_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#surround a:hover { + background: url(${base_url}/surround_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#stereo a:hover { + background: url(${base_url}/stereo_over.png) no-repeat 0px 0px +} + +#remote-control-jblsoundbar li#harman a:hover { + background: url(${base_url}/harman_over.png) no-repeat 0px 0px +} + `; +} + +function getRemoteHtml_jblsoundbar(config){ + const template = config.remote_template; + return ` + +
+
+

${config.name}

+
+
+

+ Main navigation +

+ +
+
+ +`; +} diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/source.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/source.png new file mode 100644 index 0000000..030c2b9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/source.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/source_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/source_over.png new file mode 100644 index 0000000..77ece00 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/source_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/stereo.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/stereo.png new file mode 100644 index 0000000..f01f1d2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/stereo.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/stereo_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/stereo_over.png new file mode 100644 index 0000000..99bb188 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/stereo_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/surround.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/surround.png new file mode 100644 index 0000000..2b68198 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/surround.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/jblsoundbar/surround_over.png b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/surround_over.png new file mode 100644 index 0000000..929c2cd Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/jblsoundbar/surround_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/back.png b/config/www/community/generic-remote-control-card/remotes/lg/back.png new file mode 100644 index 0000000..eb08c08 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/back_over.png b/config/www/community/generic-remote-control-card/remotes/lg/back_over.png new file mode 100644 index 0000000..c00458c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/back_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/bottom.png b/config/www/community/generic-remote-control-card/remotes/lg/bottom.png new file mode 100644 index 0000000..d85b248 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/bottom.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/bottom_over.png b/config/www/community/generic-remote-control-card/remotes/lg/bottom_over.png new file mode 100644 index 0000000..c2d315b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/bottom_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/channeldown.png b/config/www/community/generic-remote-control-card/remotes/lg/channeldown.png new file mode 100644 index 0000000..9cf8227 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/channeldown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/channeldown_over.png b/config/www/community/generic-remote-control-card/remotes/lg/channeldown_over.png new file mode 100644 index 0000000..9be8e48 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/channeldown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/channelup.png b/config/www/community/generic-remote-control-card/remotes/lg/channelup.png new file mode 100644 index 0000000..35706d3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/channelup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/channelup_over.png b/config/www/community/generic-remote-control-card/remotes/lg/channelup_over.png new file mode 100644 index 0000000..7a519e3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/channelup_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/exit.png.png b/config/www/community/generic-remote-control-card/remotes/lg/exit.png.png new file mode 100644 index 0000000..b08baa8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/exit.png.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/left.png b/config/www/community/generic-remote-control-card/remotes/lg/left.png new file mode 100644 index 0000000..97b65fb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/left_over.png b/config/www/community/generic-remote-control-card/remotes/lg/left_over.png new file mode 100644 index 0000000..733ca39 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/lg_remote.png b/config/www/community/generic-remote-control-card/remotes/lg/lg_remote.png new file mode 100644 index 0000000..e492f4d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/lg_remote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/menu.png b/config/www/community/generic-remote-control-card/remotes/lg/menu.png new file mode 100644 index 0000000..50e886d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/menu.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/menu_over.png b/config/www/community/generic-remote-control-card/remotes/lg/menu_over.png new file mode 100644 index 0000000..2f3d0f9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/menu_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/netflix.png b/config/www/community/generic-remote-control-card/remotes/lg/netflix.png new file mode 100644 index 0000000..8c5e4ff Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/netflix.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/ok.png b/config/www/community/generic-remote-control-card/remotes/lg/ok.png new file mode 100644 index 0000000..285ab45 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/ok.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/ok_over.png b/config/www/community/generic-remote-control-card/remotes/lg/ok_over.png new file mode 100644 index 0000000..1e5493f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/ok_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/remote-button.png b/config/www/community/generic-remote-control-card/remotes/lg/remote-button.png new file mode 100644 index 0000000..4b59630 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/remote-button.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/remote-home.png b/config/www/community/generic-remote-control-card/remotes/lg/remote-home.png new file mode 100644 index 0000000..8c3f01c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/remote-home.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/remote-html.js b/config/www/community/generic-remote-control-card/remotes/lg/remote-html.js new file mode 100644 index 0000000..ff433de --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/lg/remote-html.js @@ -0,0 +1,402 @@ +function getRemoteStyle_lg(config) { + + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 360px; + margin: 0 auto +} + +#remote-control-lg { + position: relative; + background: url(${base_url}/lg_remote.png) no-repeat; + width: 158px; + height: 596px +} + +#remote-control-lg h2, +#remote-control-lg span { + position: absolute; + left: 5000px +} + +#remote-control-lg ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#volmin a, +#volplus a, +#mute a, +#source a, +#button1 a, +#button2 a, +#button3 a, +#button4 a, +#button5 a, +#button6 a, +#button7 a, +#button8 a, +#button9 a, +#buttonClear a, +#button0 a, +#buttonEnter a, +#exit a, +#netflix a, +#channel a, +#channeldown a, +#menu a, +#back a, +#left a, +#right a, +#top a, +#bottom a, +#ok a { + position: absolute; + display: block +} + +#remote-control-lg li#power a { + left: 28px; + top: 35px; + width: 37px; + height: 37px; + background: url(${base_url}/remote-home.png) no-repeat +} + +#remote-control-lg li#power a:hover { + background-image: url(${base_url}/remote-home.png); + background-repeat: no-repeat; + background-position: 0 -37px +} + + +#remote-control-lg li#source a { + left: 59px; + top: 406px; + width: 35px; + height: 37px; + background: url(${base_url}/source.png) no-repeat +} + +#remote-control-lg li#source a:hover { + background-image: url(${base_url}/source_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-lg li#volmin a, +#remote-control-lg li#volplus a, +#remote-control-lg li#mute a, +#remote-control-lg li#button1 a, +#remote-control-lg li#button2 a, +#remote-control-lg li#button3 a, +#remote-control-lg li#button4 a, +#remote-control-lg li#button5 a, +#remote-control-lg li#button6 a, +#remote-control-lg li#button7 a, +#remote-control-lg li#button8 a, +#remote-control-lg li#button9 a, +#remote-control-lg li#buttonClear a, +#remote-control-lg li#button0 a, +#remote-control-lg li#buttonEnter a { + width: 41px; + height: 21px; + background: url(${base_url}/remote-button.png) no-repeat +} + +#remote-control-lg li#volmin a { + left: 18px; + top: 239px +} + +#remote-control-lg li#volplus a { + left: 18px; + top: 195px +} + +#remote-control-lg li#mute a { + left: 57px; + top: 194px +} + +#remote-control-lg li#button1 a { + left: 24px; + top: 82px; +} + +#remote-control-lg li#button2 a { + left: 55px; + top: 81px; +} + +#remote-control-lg li#button3 a { + left: 92px; + top: 81px +} + +#remote-control-lg li#button4 a { + left: 21px; + top: 108px +} + +#remote-control-lg li#button5 a { + left: 57px; + top: 108px +} + +#remote-control-lg li#button6 a { + left: 90px; + top: 107px +} + +#remote-control-lg li#button7 a { + left: 22px; + top: 134px +} + +#remote-control-lg li#button8 a { + left: 56px; + top: 134px +} + +#remote-control-lg li#button9 a { + left: 93px; + top: 134px +} + +#remote-control-lg li#buttonClear a { + left: 20px; + top: 161px +} + +#remote-control-lg li#button0 a { + left: 56px; + top: 161px +} + +#remote-control-lg li#buttonEnter a { + left: 91px; + top: 160px +} + +#remote-control-lg li#volmin a:hover, +#remote-control-lg li#volplus a:hover, +#remote-control-lg li#mute a:hover, +#remote-control-lg li#button1 a:hover, +#remote-control-lg li#button2 a:hover, +#remote-control-lg li#button3 a:hover, +#remote-control-lg li#button4 a:hover, +#remote-control-lg li#button5 a:hover, +#remote-control-lg li#button6 a:hover, +#remote-control-lg li#button7 a:hover, +#remote-control-lg li#button8 a:hover, +#remote-control-lg li#button9 a:hover, +#remote-control-lg li#buttonClear a:hover, +#remote-control-lg li#button0 a:hover, +#remote-control-lg li#buttonEnter a:hover { + background-image: url(${base_url}/remote-button.png); + background-repeat: no-repeat; + background-position: 0 -21px +} + +#remote-control-lg li#exit a { + width: 20px; + height: 12px; + left: 22px; + top: 408px; + background: url(${base_url}/exit.png) no-repeat 0 0px +} + + +#remote-control-lg li#menu a { + width: 33px; + height: 23px; + left: 15px; + top: 267px; + background: url(${base_url}/menu.png) no-repeat 0px 0px; +} + +#remote-control-lg li#back a { + width: 37px; + height: 23px; + left: 13px; + top: 388px; + background: url(${base_url}/back.png) no-repeat 0px 0px; +} + +#remote-control-lg li#left a { + width: 13px; + height: 22px; + left: 32px; + top: 329px; + background: url(${base_url}/left.png) no-repeat 0px 0px +} + +#remote-control-lg li#right a { + width: 13px; + height: 22px; + left: 114px; + top: 329px; + background: url(${base_url}/right.png) no-repeat 0px 0px +} + +#remote-control-lg li#top a { + width: 22px; + height: 13px; + left: 67px; + top: 294px; + background: url(${base_url}/top.png) no-repeat 0px 0px +} + +#remote-control-lg li#bottom a { + width: 22px; + height: 13px; + left: 68px; + top: 374px; + background: url(${base_url}/bottom.png) no-repeat 0px 0px +} + +#remote-control-lg li#netflix a { + width: 36px; + height: 23px; + left: 17px; + top: 418px; + background: url(${base_url}/netflix.png) no-repeat 0px 0px +} + +#remote-control-lg li#channel a { + width: 37px; + height: 36px; + left: 98px; + top: 190px; + background: url(${base_url}/channelup.png) no-repeat 0px 0px +} + +#remote-control-lg li#channeldown a { + width: 37px; + height: 36px; + left: 98px; + top: 234px; + background: url(${base_url}/channeldown.png) no-repeat 0px 0px +} + +#remote-control-lg li#ok a { + width: 20px; + height: 47px; + left: 68px; + top: 317px; + background: url(${base_url}/ok.png) no-repeat 0px 0px +} + +#remote-control-lg li#exit a:hover { + background: url(${base_url}/exit.png) no-repeat 0 0px +} + + +#remote-control-lg li#menu a:hover { + background: url(${base_url}/menu_over.png) no-repeat 0px 0px +} + +#remote-control-lg li#back a:hover { + background: url(${base_url}/back_over.png) no-repeat 0px 0px; +} + +#remote-control-lg li#left a:hover { + background: url(${base_url}/left_over.png) no-repeat 0px 0px +} + +#remote-control-lg li#right a:hover { + background: url(${base_url}/right_over.png) no-repeat 0px 0px +} + +#remote-control-lg li#top a:hover { + background: url(${base_url}/top_over.png) no-repeat 0px 0px +} + +#remote-control-lg li#bottom a:hover { + background: url(${base_url}/bottom_over.png) no-repeat 0px 0px +} + +#remote-control-lg li#netflix a:hover { + background: url(${base_url}/netflix.png) no-repeat 0px 0px +} + +#remote-control-lg li#channel a:hover { + background: url(${base_url}/channelup_over.png) no-repeat 0px 0px +} + +#remote-control-lg li#channeldown a:hover { + background: url(${base_url}/channeldown_over.png) no-repeat 0px 0px +} + +#remote-control-lg li#ok a:hover { + background: url(${base_url}/ok_over.png) no-repeat 0px 0px +} + `; +} + +function getRemoteHtml_lg(config){return ` + + +`; +} \ No newline at end of file diff --git a/config/www/community/generic-remote-control-card/remotes/lg/right.png b/config/www/community/generic-remote-control-card/remotes/lg/right.png new file mode 100644 index 0000000..518afc9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/right_over.png b/config/www/community/generic-remote-control-card/remotes/lg/right_over.png new file mode 100644 index 0000000..ca01965 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/right_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/source.png b/config/www/community/generic-remote-control-card/remotes/lg/source.png new file mode 100644 index 0000000..11c4238 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/source.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/source_over.png b/config/www/community/generic-remote-control-card/remotes/lg/source_over.png new file mode 100644 index 0000000..2d8306a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/source_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/top.png b/config/www/community/generic-remote-control-card/remotes/lg/top.png new file mode 100644 index 0000000..9921bed Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/top.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/top_over.png b/config/www/community/generic-remote-control-card/remotes/lg/top_over.png new file mode 100644 index 0000000..934aab8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/top_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg/volplus.png b/config/www/community/generic-remote-control-card/remotes/lg/volplus.png new file mode 100644 index 0000000..09bedab Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg/volplus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/amazon.png b/config/www/community/generic-remote-control-card/remotes/lg_new/amazon.png new file mode 100644 index 0000000..a3f5498 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/amazon.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/back.png b/config/www/community/generic-remote-control-card/remotes/lg_new/back.png new file mode 100644 index 0000000..55eb49a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/back_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/back_over.png new file mode 100644 index 0000000..cef90b9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/back_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/bottom.png b/config/www/community/generic-remote-control-card/remotes/lg_new/bottom.png new file mode 100644 index 0000000..d85b248 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/bottom.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/bottom_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/bottom_over.png new file mode 100644 index 0000000..c2d315b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/bottom_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/channeldown.png b/config/www/community/generic-remote-control-card/remotes/lg_new/channeldown.png new file mode 100644 index 0000000..2f10bdf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/channeldown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/channeldown_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/channeldown_over.png new file mode 100644 index 0000000..def3565 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/channeldown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/channelup.png b/config/www/community/generic-remote-control-card/remotes/lg_new/channelup.png new file mode 100644 index 0000000..5c67065 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/channelup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/channelup_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/channelup_over.png new file mode 100644 index 0000000..b6cc0e6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/channelup_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/eight.png b/config/www/community/generic-remote-control-card/remotes/lg_new/eight.png new file mode 100644 index 0000000..b0ffc5a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/eight.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/eight_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/eight_over.png new file mode 100644 index 0000000..58bac02 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/eight_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/exit.png b/config/www/community/generic-remote-control-card/remotes/lg_new/exit.png new file mode 100644 index 0000000..b2060ca Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/exit.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/exit_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/exit_over.png new file mode 100644 index 0000000..e33da49 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/exit_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/five.png b/config/www/community/generic-remote-control-card/remotes/lg_new/five.png new file mode 100644 index 0000000..a849d99 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/five.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/five_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/five_over.png new file mode 100644 index 0000000..a07d022 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/five_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/four.png b/config/www/community/generic-remote-control-card/remotes/lg_new/four.png new file mode 100644 index 0000000..e128ea7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/four.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/four_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/four_over.png new file mode 100644 index 0000000..570bcfa Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/four_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/left.png b/config/www/community/generic-remote-control-card/remotes/lg_new/left.png new file mode 100644 index 0000000..97b65fb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/left_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/left_over.png new file mode 100644 index 0000000..733ca39 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/lg_remote.png b/config/www/community/generic-remote-control-card/remotes/lg_new/lg_remote.png new file mode 100644 index 0000000..b208123 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/lg_remote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/list.png b/config/www/community/generic-remote-control-card/remotes/lg_new/list.png new file mode 100644 index 0000000..7331e0a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/list.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/list_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/list_over.png new file mode 100644 index 0000000..e6dcaa3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/list_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/menu.png b/config/www/community/generic-remote-control-card/remotes/lg_new/menu.png new file mode 100644 index 0000000..deb5397 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/menu.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/menu_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/menu_over.png new file mode 100644 index 0000000..eeee735 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/menu_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/microphone.png b/config/www/community/generic-remote-control-card/remotes/lg_new/microphone.png new file mode 100644 index 0000000..68862cf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/microphone.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/microphone_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/microphone_over.png new file mode 100644 index 0000000..f8b99ac Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/microphone_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/mute.png b/config/www/community/generic-remote-control-card/remotes/lg_new/mute.png new file mode 100644 index 0000000..8a4dc2a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/mute_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/mute_over.png new file mode 100644 index 0000000..6b6174f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/mute_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/netflix.png b/config/www/community/generic-remote-control-card/remotes/lg_new/netflix.png new file mode 100644 index 0000000..0730a91 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/netflix.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/nine.png b/config/www/community/generic-remote-control-card/remotes/lg_new/nine.png new file mode 100644 index 0000000..b8cfd78 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/nine.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/nine_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/nine_over.png new file mode 100644 index 0000000..d5d20d6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/nine_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/ok.png b/config/www/community/generic-remote-control-card/remotes/lg_new/ok.png new file mode 100644 index 0000000..25c4e44 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/ok.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/ok_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/ok_over.png new file mode 100644 index 0000000..feec364 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/ok_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/one.png b/config/www/community/generic-remote-control-card/remotes/lg_new/one.png new file mode 100644 index 0000000..cf9bd5e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/one.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/one_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/one_over.png new file mode 100644 index 0000000..73cba49 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/one_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/power.png b/config/www/community/generic-remote-control-card/remotes/lg_new/power.png new file mode 100644 index 0000000..3d1a14b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/power_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/power_over.png new file mode 100644 index 0000000..8abc91d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/power_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/remote-html.js b/config/www/community/generic-remote-control-card/remotes/lg_new/remote-html.js new file mode 100644 index 0000000..d4889d5 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/lg_new/remote-html.js @@ -0,0 +1,510 @@ +function getRemoteStyle_lg_new(config) { + + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 360px; + margin: 0 auto +} + +#remote-control-lg_new { + position: relative; + background: url(${base_url}/lg_remote.png) no-repeat; + width: 159px; + height: 596px +} + +#remote-control-lg_new h2, +#remote-control-lg_new span { + position: absolute; + left: 5000px +} + +#remote-control-lg_new ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#volmin a, +#volplus a, +#mute a, +#source a, +#one a, +#two a, +#three a, +#four a, +#five a, +#six a, +#seven a, +#eight a, +#nine a, +#zero a, +#list a, +#section12 a, +#exit a, +#netflix a, +#amazon a, +#channelup a, +#channeldown a, +#microphone a, +#settings a, +#menu a, +#back a, +#left a, +#right a, +#top a, +#bottom a, +#ok a { + position: absolute; + display: block +} + +#remote-control-lg_new li#power a { + left: 32px; + top: 36px; + width: 30px; + height: 32px; + background: url(${base_url}/power.png) no-repeat +} + +#remote-control-lg_new li#source a { + left: 66px; + top: 418px; + width: 24px; + height: 15px; + background: url(${base_url}/source.png) no-repeat +} + +#remote-control-lg_new li#source a:hover { + background-image: url(${base_url}/source_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-lg_new li#exit a { + width: 24px; + height: 10px; + left: 22px; + top: 410px; + background: url(${base_url}/exit.png) no-repeat 0 0px +} + + +#remote-control-lg_new li#one a { + width: 6px; + height: 13px; + left: 39px; + top: 85px; + background: url(${base_url}/one.png) no-repeat 0 0px +} + +#remote-control-lg_new li#two a { + width: 11px; + height: 15px; + left: 74px; + top: 83px; + background: url(${base_url}/two.png) no-repeat 0 0px +} + +#remote-control-lg_new li#three a { + width: 9px; + height: 13px; + left: 110px; + top: 85px; + background: url(${base_url}/three.png) no-repeat 0 0px +} + +#remote-control-lg_new li#four a { + width: 8px; + height: 11px; + left: 37px; + top: 111px; + background: url(${base_url}/four.png) no-repeat 0 0px +} + +#remote-control-lg_new li#five a { + width: 8px; + height: 13px; + left: 75px; + top: 110px; + background: url(${base_url}/five.png) no-repeat 0 0px +} + + +#remote-control-lg_new li#six a { + width: 8px; + height: 13px; + left: 110px; + top: 111px; + background: url(${base_url}/six.png) no-repeat 0 0px +} + + +#remote-control-lg_new li#seven a { + width: 8px; + height: 13px; + left: 39px; + top: 137px; + background: url(${base_url}/seven.png) no-repeat 0 0px +} + +#remote-control-lg_new li#eight a { + width: 8px; + height: 13px; + left: 75px; + top: 136px; + background: url(${base_url}/eight.png) no-repeat 0 0px +} + +#remote-control-lg_new li#nine a { + width: 8px; + height: 13px; + left: 110px; + top: 136px; + background: url(${base_url}/nine.png) no-repeat 0 0px +} + + +#remote-control-lg_new li#zero a { + width: 11px; + height: 15px; + left: 75px; + top: 164px; + background: url(${base_url}/zero.png) no-repeat 0 0px +} + +#remote-control-lg_new li#volplus a { + width: 28px; + height: 28px; + left: 25px; + top: 191px; + background: url(${base_url}/volplus.png) no-repeat 0 0px +} + +#remote-control-lg_new li#volmin a { + width: 28px; + height: 5px; + left: 25px; + top: 246px; + background: url(${base_url}/volmin.png) no-repeat 0 0px +} + +#remote-control-lg_new li#menu a { + width: 16px; + height: 16px; + left: 23px; + top: 270px; + background: url(${base_url}/menu.png) no-repeat 0px 0px; +} + +#remote-control-lg_new li#mute a { + width: 18px; + height: 18px; + left: 70px; + top: 195px; + background: url(${base_url}/mute.png) no-repeat 0 0px +} + +#remote-control-lg_new li#list a { + width: 40px; + height: 40px; + left: 23px; + top: 150px; + background: url(${base_url}/list.png) no-repeat 0 0px +} + +#remote-control-lg_new li#section12 a { + width: 15px; + height: 16px; + left: 107px; + top: 162px; + background: url(${base_url}/section12.png) no-repeat 0 0px +} + +#remote-control-lg_new li#back a { + width: 26px; + height: 8px; + left: 19px; + top: 394px; + background: url(${base_url}/back.png) no-repeat 0px 0px; +} + +#remote-control-lg_new li#left a { + width: 13px; + height: 22px; + left: 32px; + top: 329px; + background: url(${base_url}/left.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#right a { + width: 13px; + height: 22px; + left: 114px; + top: 329px; + background: url(${base_url}/right.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#top a { + width: 22px; + height: 13px; + left: 67px; + top: 294px; + background: url(${base_url}/top.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#bottom a { + width: 22px; + height: 13px; + left: 68px; + top: 374px; + background: url(${base_url}/bottom.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#netflix a { + width: 36px; + height: 23px; + left: 17px; + top: 418px; + background: url(${base_url}/netflix.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#amazon a { + width: 36px; + height: 23px; + left: 105px; + top: 418px; + background: url(${base_url}/amazon.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#channelup a { + width: 24px; + height: 22px; + left: 105px; + top: 195px; + background: url(${base_url}/channelup.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#channeldown a { + width: 24px; + height: 22px; + left: 105px; + top: 238px; + background: url(${base_url}/channeldown.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#microphone a { + width: 24px; + height: 22px; + left: 70px; + top: 242px; + background: url(${base_url}/microphone.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#settings a { + width: 15px; + height: 15px; + left: 118px; + top: 269px; + background: url(${base_url}/settings.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#ok a { + width: 18px; + height: 42px; + left: 68px; + top: 317px; + background: url(${base_url}/ok.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#exit a:hover { + background: url(${base_url}/exit_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#one a:hover { + background: url(${base_url}/one_over.png) no-repeat 0 0px +} + + +#remote-control-lg_new li#two a:hover { + background: url(${base_url}/two_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#three a:hover { + background: url(${base_url}/three_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#four a:hover { + background: url(${base_url}/four_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#five a:hover { + background: url(${base_url}/five_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#six a:hover { + background: url(${base_url}/six_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#seven a:hover { + background: url(${base_url}/seven_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#eight a:hover { + background: url(${base_url}/eight_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#nine a:hover { + background: url(${base_url}/nine_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#zero a:hover { + background: url(${base_url}/zero_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#volplus a:hover { + background: url(${base_url}/volplus_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#volmin a:hover { + background: url(${base_url}/volmin_over.png) no-repeat 0 0px +} + +#remote-control-lg_new li#menu a:hover { + background: url(${base_url}/menu_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#list a:hover { + background: url(${base_url}/list_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#section12 a:hover { + background: url(${base_url}/section12_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#back a:hover { + background: url(${base_url}/back_over.png) no-repeat 0px 0px; +} + +#remote-control-lg_new li#left a:hover { + background: url(${base_url}/left_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#right a:hover { + background: url(${base_url}/right_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#top a:hover { + background: url(${base_url}/top_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#bottom a:hover { + background: url(${base_url}/bottom_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#netflix a:hover { + background: url(${base_url}/netflix.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#amazon a:hover { + background: url(${base_url}/amazon.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#channelup a:hover { + background: url(${base_url}/channelup_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#channeldown a:hover { + background: url(${base_url}/channeldown_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#mute a:hover { + background: url(${base_url}/mute_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#microphone a:hover { + background: url(${base_url}/microphone_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#settings a:hover { + background: url(${base_url}/settings_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#power a:hover { + background: url(${base_url}/power_over.png) no-repeat 0px 0px +} + +#remote-control-lg_new li#ok a:hover { + background: url(${base_url}/ok_over.png) no-repeat 0px 0px +} + `; +} + +function getRemoteHtml_lg_new(config){return ` +
+
+

${config.name}

+
+
+

+ Main navigation +

+ + + + + +
+
+ +`; +} \ No newline at end of file diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/right.png b/config/www/community/generic-remote-control-card/remotes/lg_new/right.png new file mode 100644 index 0000000..518afc9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/right_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/right_over.png new file mode 100644 index 0000000..ca01965 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/right_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/section12.png b/config/www/community/generic-remote-control-card/remotes/lg_new/section12.png new file mode 100644 index 0000000..fa61311 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/section12.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/section12_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/section12_over.png new file mode 100644 index 0000000..269b39f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/section12_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/settings.png b/config/www/community/generic-remote-control-card/remotes/lg_new/settings.png new file mode 100644 index 0000000..170d53a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/settings.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/settings_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/settings_over.png new file mode 100644 index 0000000..67782fe Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/settings_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/seven.png b/config/www/community/generic-remote-control-card/remotes/lg_new/seven.png new file mode 100644 index 0000000..828d180 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/seven.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/seven_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/seven_over.png new file mode 100644 index 0000000..517942b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/seven_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/six.png b/config/www/community/generic-remote-control-card/remotes/lg_new/six.png new file mode 100644 index 0000000..69c0b55 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/six.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/six_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/six_over.png new file mode 100644 index 0000000..45eae29 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/six_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/source.png b/config/www/community/generic-remote-control-card/remotes/lg_new/source.png new file mode 100644 index 0000000..288f6b5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/source.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/source_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/source_over.png new file mode 100644 index 0000000..d9d68b8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/source_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/three.png b/config/www/community/generic-remote-control-card/remotes/lg_new/three.png new file mode 100644 index 0000000..e0ce1a4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/three.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/three_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/three_over.png new file mode 100644 index 0000000..001cc1b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/three_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/top.png b/config/www/community/generic-remote-control-card/remotes/lg_new/top.png new file mode 100644 index 0000000..9921bed Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/top.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/top_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/top_over.png new file mode 100644 index 0000000..83ed489 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/top_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/two.png b/config/www/community/generic-remote-control-card/remotes/lg_new/two.png new file mode 100644 index 0000000..ffef8d2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/two.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/two_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/two_over.png new file mode 100644 index 0000000..7828c4f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/two_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/volmin.png b/config/www/community/generic-remote-control-card/remotes/lg_new/volmin.png new file mode 100644 index 0000000..f40bec9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/volmin.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/volmin_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/volmin_over.png new file mode 100644 index 0000000..c3c2055 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/volmin_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/volplus.png b/config/www/community/generic-remote-control-card/remotes/lg_new/volplus.png new file mode 100644 index 0000000..3ece726 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/volplus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/volplus_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/volplus_over.png new file mode 100644 index 0000000..7cd2fac Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/volplus_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/zero.png b/config/www/community/generic-remote-control-card/remotes/lg_new/zero.png new file mode 100644 index 0000000..d06721c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/zero.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/lg_new/zero_over.png b/config/www/community/generic-remote-control-card/remotes/lg_new/zero_over.png new file mode 100644 index 0000000..5f9d7c2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/lg_new/zero_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/bottom.png b/config/www/community/generic-remote-control-card/remotes/mibox/bottom.png new file mode 100644 index 0000000..9b00fb3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/bottom.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/bottom_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/bottom_over.png new file mode 100644 index 0000000..6e28feb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/bottom_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/clickleft.png b/config/www/community/generic-remote-control-card/remotes/mibox/clickleft.png new file mode 100644 index 0000000..86d5e2f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/clickleft.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/clickleft_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/clickleft_over.png new file mode 100644 index 0000000..87502a3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/clickleft_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/clickright.png b/config/www/community/generic-remote-control-card/remotes/mibox/clickright.png new file mode 100644 index 0000000..f9799e2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/clickright.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/clickright_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/clickright_over.png new file mode 100644 index 0000000..b167be0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/clickright_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/home.png b/config/www/community/generic-remote-control-card/remotes/mibox/home.png new file mode 100644 index 0000000..fb88327 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/home.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/home_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/home_over.png new file mode 100644 index 0000000..731a08a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/home_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/left.png b/config/www/community/generic-remote-control-card/remotes/mibox/left.png new file mode 100644 index 0000000..c82b9f6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/left_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/left_over.png new file mode 100644 index 0000000..f8c390d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/microphone.png b/config/www/community/generic-remote-control-card/remotes/mibox/microphone.png new file mode 100644 index 0000000..3625d07 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/microphone.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/microphone_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/microphone_over.png new file mode 100644 index 0000000..55491c4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/microphone_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/ok.png b/config/www/community/generic-remote-control-card/remotes/mibox/ok.png new file mode 100644 index 0000000..79dec31 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/ok.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/ok_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/ok_over.png new file mode 100644 index 0000000..0e2bb2b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/ok_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/power.png b/config/www/community/generic-remote-control-card/remotes/mibox/power.png new file mode 100644 index 0000000..ba85574 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/power_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/power_over.png new file mode 100644 index 0000000..c6ffe97 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/power_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/remote-back.png b/config/www/community/generic-remote-control-card/remotes/mibox/remote-back.png new file mode 100644 index 0000000..26ac788 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/remote-back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/remote-html.js b/config/www/community/generic-remote-control-card/remotes/mibox/remote-html.js new file mode 100644 index 0000000..aa2982b --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/mibox/remote-html.js @@ -0,0 +1,261 @@ +function getRemoteStyle_mibox(config) { + + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 224px; + margin: 0 auto +} + +#remote-control-mibox { + position: relative; + background: url(${base_url}/remote-back.png) no-repeat; + width: 153px; + height: 608px +} + +#remote-control-mibox h2, +#remote-control-mibox span { + position: absolute; + left: -5000px +} + +#remote-control-mibox ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#volumeup a, +#volumedown a, +#clickleft a, +#clickright a, +#mute a, +#microphone a, +#info a, +#home a, +#back a, +#top a, +#bottom a, +#ok a { + position: absolute; + display: block +} + +#remote-control-mibox li#power a { + left: 17px; + top: 37px; + width: 29px; + height: 30px; + background: url(${base_url}/power.png) no-repeat +} + +#remote-control-mibox li#power a:hover { + background-image: url(${base_url}/power_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-mibox li#microphone a { + left: 119px; + top: 250px; + width: 28px; + height: 29px; + background: url(${base_url}/microphone.png) no-repeat +} + +#remote-control-mibox li#microphone a:hover { + background-image: url(${base_url}/microphone_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-mibox li#volumeup a { + left: 69px; + top: 299px; + width: 29px; + height: 30px; + background: url(${base_url}/volume.png) no-repeat +} + +#remote-control-mibox li#volumeup a:hover { + background-image: url(${base_url}/volume_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-mibox li#volumedown a { + left: 69px; + top: 359px; + width: 29px; + height: 30px; + background: url(${base_url}/volumedown.png) no-repeat +} + +#remote-control-mibox li#volumedown a:hover { + background-image: url(${base_url}/volumedown_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-mibox li#clickleft a { + left: 21px; + top: 137px; + width: 29px; + height: 29px; + background: url(${base_url}/clickleft.png) no-repeat +} + +#remote-control-mibox li#clickleft a:hover { + background-image: url(${base_url}/clickleft_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-mibox li#clickright a { + left: 116px; + top: 138px; + width: 29px; + height: 29px; + background: url(${base_url}/clickright.png) no-repeat +} + +#remote-control-mibox li#clickright a:hover { + background-image: url(${base_url}/clickright_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-mibox li#source a:hover { + background-image: url(${base_url}/remote-sitemap.jpg); + background-repeat: no-repeat; + background-position: 0 -37px +} + + +#remote-control-mibox li#home a { + width: 29px; + height: 30px; + left: 69px; + top: 248px; + background: url(${base_url}/home.png) no-repeat 0px 0 +} + + + +#remote-control-mibox li#back a { + width: 28px; + height: 29px; + left: 17px; + top: 249px; + background: url(${base_url}/left.png) no-repeat 0px 0px +} + +#remote-control-mibox li#right a { + width: 23px; + height: 59px; + left: 139px; + top: 365px; + background: url(${base_url}/remote-circle3.png) no-repeat -98px -53px +} + +#remote-control-mibox li#top a { + width: 29px; + height: 29px; + left: 69px; + top: 93px; + background: url(${base_url}/top.png) no-repeat 0px 0px +} + +#remote-control-mibox li#bottom a { + width: 29px; + height: 29px; + left: 68px; + top: 190px; + background: url(${base_url}/bottom.png) no-repeat 0px 0px +} + +#remote-control-mibox li#ok a { + width: 29px; + height: 29px; + left: 68px; + top: 141px; + background: url(${base_url}/ok.png) no-repeat 0px 0px +} + + +#remote-control-mibox li#home a:hover { + background: url(${base_url}/home_over.png) no-repeat 0px 0px +} + +#remote-control-mibox li#back a:hover { + background: url(${base_url}/left_over.png) no-repeat 0px 0px +} + + +#remote-control-mibox li#top a:hover { + background: url(${base_url}/top_over.png) no-repeat 0px 0px +} + +#remote-control-mibox li#bottom a:hover { + background: url(${base_url}/bottom_over.png) no-repeat 0px 0px +} + +#remote-control-mibox li#ok a:hover { + background: url(${base_url}/ok_over.png) no-repeat 0px 0px +} + + `; +} + +function getRemoteHtml_mibox(config){ + const template = config.remote_template; + return ` + +
+
+

${config.name}

+
+ +
+ +`; +} \ No newline at end of file diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/top.png b/config/www/community/generic-remote-control-card/remotes/mibox/top.png new file mode 100644 index 0000000..ee7f44a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/top.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/top_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/top_over.png new file mode 100644 index 0000000..8fa533e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/top_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/volume.png b/config/www/community/generic-remote-control-card/remotes/mibox/volume.png new file mode 100644 index 0000000..c900c26 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/volume.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/volume_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/volume_over.png new file mode 100644 index 0000000..83385f3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/volume_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/volumedown.png b/config/www/community/generic-remote-control-card/remotes/mibox/volumedown.png new file mode 100644 index 0000000..7fc1009 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/volumedown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/mibox/volumedown_over.png b/config/www/community/generic-remote-control-card/remotes/mibox/volumedown_over.png new file mode 100644 index 0000000..e5a9cef Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/mibox/volumedown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/.smbdeleteAAA1cc13c0 b/config/www/community/generic-remote-control-card/remotes/partner/.smbdeleteAAA1cc13c0 new file mode 100644 index 0000000..31c9e9a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/.smbdeleteAAA1cc13c0 differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/.smbdeleteAAA1cc13c1 b/config/www/community/generic-remote-control-card/remotes/partner/.smbdeleteAAA1cc13c1 new file mode 100644 index 0000000..f714dec Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/.smbdeleteAAA1cc13c1 differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/back.png b/config/www/community/generic-remote-control-card/remotes/partner/back.png new file mode 100644 index 0000000..4d47be5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/back_over.png b/config/www/community/generic-remote-control-card/remotes/partner/back_over.png new file mode 100644 index 0000000..ff8882a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/back_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/bottom.png b/config/www/community/generic-remote-control-card/remotes/partner/bottom.png new file mode 100644 index 0000000..d85b248 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/bottom.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/bottom_over.png b/config/www/community/generic-remote-control-card/remotes/partner/bottom_over.png new file mode 100644 index 0000000..c2d315b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/bottom_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/channeldown.png b/config/www/community/generic-remote-control-card/remotes/partner/channeldown.png new file mode 100644 index 0000000..96bbe85 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/channeldown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/channeldown_over.png b/config/www/community/generic-remote-control-card/remotes/partner/channeldown_over.png new file mode 100644 index 0000000..4c0c90b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/channeldown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/channelup.png b/config/www/community/generic-remote-control-card/remotes/partner/channelup.png new file mode 100644 index 0000000..f16f0df Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/channelup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/channelup_over.png b/config/www/community/generic-remote-control-card/remotes/partner/channelup_over.png new file mode 100644 index 0000000..d160ced Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/channelup_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/circle.png b/config/www/community/generic-remote-control-card/remotes/partner/circle.png new file mode 100644 index 0000000..3cb122e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/circle.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/circle_over.png b/config/www/community/generic-remote-control-card/remotes/partner/circle_over.png new file mode 100644 index 0000000..ebd33e1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/circle_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/eight.png b/config/www/community/generic-remote-control-card/remotes/partner/eight.png new file mode 100644 index 0000000..b0ffc5a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/eight.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/eight_over.png b/config/www/community/generic-remote-control-card/remotes/partner/eight_over.png new file mode 100644 index 0000000..58bac02 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/eight_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/fastforward.png b/config/www/community/generic-remote-control-card/remotes/partner/fastforward.png new file mode 100644 index 0000000..d64b73c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/fastforward.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/fastforward_over.png b/config/www/community/generic-remote-control-card/remotes/partner/fastforward_over.png new file mode 100644 index 0000000..060a42f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/fastforward_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/five.png b/config/www/community/generic-remote-control-card/remotes/partner/five.png new file mode 100644 index 0000000..a849d99 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/five.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/five_over.png b/config/www/community/generic-remote-control-card/remotes/partner/five_over.png new file mode 100644 index 0000000..a07d022 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/five_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/four.png b/config/www/community/generic-remote-control-card/remotes/partner/four.png new file mode 100644 index 0000000..e128ea7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/four.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/four_over.png b/config/www/community/generic-remote-control-card/remotes/partner/four_over.png new file mode 100644 index 0000000..570bcfa Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/four_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/home.png b/config/www/community/generic-remote-control-card/remotes/partner/home.png new file mode 100644 index 0000000..7f56e75 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/home.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/home_over.png b/config/www/community/generic-remote-control-card/remotes/partner/home_over.png new file mode 100644 index 0000000..e32f2bf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/home_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/lastch.png b/config/www/community/generic-remote-control-card/remotes/partner/lastch.png new file mode 100644 index 0000000..8e3d64c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/lastch.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/lastch_over.png b/config/www/community/generic-remote-control-card/remotes/partner/lastch_over.png new file mode 100644 index 0000000..d45accd Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/lastch_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/left.png b/config/www/community/generic-remote-control-card/remotes/partner/left.png new file mode 100644 index 0000000..97b65fb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/left_over.png b/config/www/community/generic-remote-control-card/remotes/partner/left_over.png new file mode 100644 index 0000000..733ca39 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/mute.png b/config/www/community/generic-remote-control-card/remotes/partner/mute.png new file mode 100644 index 0000000..68c7f6c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/mute_over.png b/config/www/community/generic-remote-control-card/remotes/partner/mute_over.png new file mode 100644 index 0000000..c4b04b0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/mute_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/myrec.png b/config/www/community/generic-remote-control-card/remotes/partner/myrec.png new file mode 100644 index 0000000..830159c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/myrec.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/myrec_over.png b/config/www/community/generic-remote-control-card/remotes/partner/myrec_over.png new file mode 100644 index 0000000..b653350 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/myrec_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/netflix.png b/config/www/community/generic-remote-control-card/remotes/partner/netflix.png new file mode 100644 index 0000000..dcd1595 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/netflix.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/nine.png b/config/www/community/generic-remote-control-card/remotes/partner/nine.png new file mode 100644 index 0000000..b8cfd78 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/nine.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/nine_over.png b/config/www/community/generic-remote-control-card/remotes/partner/nine_over.png new file mode 100644 index 0000000..d5d20d6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/nine_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/ok.png b/config/www/community/generic-remote-control-card/remotes/partner/ok.png new file mode 100644 index 0000000..f1cb3c0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/ok.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/ok_over.png b/config/www/community/generic-remote-control-card/remotes/partner/ok_over.png new file mode 100644 index 0000000..795e58b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/ok_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/one.png b/config/www/community/generic-remote-control-card/remotes/partner/one.png new file mode 100644 index 0000000..cf9bd5e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/one.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/one_over.png b/config/www/community/generic-remote-control-card/remotes/partner/one_over.png new file mode 100644 index 0000000..73cba49 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/one_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/partner.png b/config/www/community/generic-remote-control-card/remotes/partner/partner.png new file mode 100644 index 0000000..c8bda0c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/partner.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/partner_over.png b/config/www/community/generic-remote-control-card/remotes/partner/partner_over.png new file mode 100644 index 0000000..c8bda0c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/partner_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/partner_remote.png b/config/www/community/generic-remote-control-card/remotes/partner/partner_remote.png new file mode 100644 index 0000000..7c1f59d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/partner_remote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/play.png b/config/www/community/generic-remote-control-card/remotes/partner/play.png new file mode 100644 index 0000000..0d1b799 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/play.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/play_over.png b/config/www/community/generic-remote-control-card/remotes/partner/play_over.png new file mode 100644 index 0000000..02d37f6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/play_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/power.png b/config/www/community/generic-remote-control-card/remotes/partner/power.png new file mode 100644 index 0000000..25dfaaa Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/power_over.png b/config/www/community/generic-remote-control-card/remotes/partner/power_over.png new file mode 100644 index 0000000..c8bda0c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/power_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/record.png b/config/www/community/generic-remote-control-card/remotes/partner/record.png new file mode 100644 index 0000000..56544ce Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/record.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/record_over.png b/config/www/community/generic-remote-control-card/remotes/partner/record_over.png new file mode 100644 index 0000000..90fea3e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/record_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/remote-html.js b/config/www/community/generic-remote-control-card/remotes/partner/remote-html.js new file mode 100644 index 0000000..9317537 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/partner/remote-html.js @@ -0,0 +1,606 @@ +function getRemoteStyle_partner(config) { + + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 360px; + margin: 0 auto +} + +#remote-control-partner { + position: relative; + background: url(${base_url}/partner_remote.png) no-repeat; + width: 159px; + height: 618px +} + +#remote-control-partner h2, +#remote-control-partner span { + position: absolute; + left: 5000px +} + +#remote-control-partner ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#partner a, +#volmin a, +#volplus a, +#mute a, +#record a, +#source a, +#one a, +#two a, +#three a, +#four a, +#five a, +#six a, +#seven a, +#eight a, +#nine a, +#zero a, +#section12 a, +#lastch a, +#fastforward a, +#rewind a, +#play a, +#stop a, +#vod a, +#myrec a, +#netflix a, +#youtube a, +#channelup a, +#channeldown a, +#home a, +#menu a, +#back a, +#circle a, +#left a, +#right a, +#top a, +#bottom a, +#ok a { + position: absolute; + display: block +} + +#remote-control-partner li#power a { + left: 33px; + top: 43px; + width: 15px; + height: 16px; + background: url(${base_url}/power.png) no-repeat +} + +#remote-control-partner li#source a { + left: 74px; + top: 43px; + width: 14px; + height: 16px; + background: url(${base_url}/source.png) no-repeat +} + +#remote-control-partner li#source a:hover { + background-image: url(${base_url}/source_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-partner li#partner a { + left: 118px; + top: 43px; + width: 15px; + height: 16px; + background: url(${base_url}/partner.png) no-repeat +} + +#remote-control-partner li#one a { + width: 6px; + height: 13px; + left: 36px; + top: 436px; + background: url(${base_url}/one.png) no-repeat 0 0px +} + +#remote-control-partner li#two a { + width: 11px; + height: 15px; + left: 80px; + top: 436px; + background: url(${base_url}/two.png) no-repeat 0 0px +} + +#remote-control-partner li#three a { + width: 9px; + height: 13px; + left: 123px; + top: 436px; + background: url(${base_url}/three.png) no-repeat 0 0px +} + +#remote-control-partner li#four a { + width: 8px; + height: 11px; + left: 36px; + top: 470px; + background: url(${base_url}/four.png) no-repeat 0 0px +} + +#remote-control-partner li#five a { + width: 8px; + height: 13px; + left: 80px; + top: 470px; + background: url(${base_url}/five.png) no-repeat 0 0px +} + + +#remote-control-partner li#six a { + width: 8px; + height: 13px; + left: 123px; + top: 470px; + background: url(${base_url}/six.png) no-repeat 0 0px +} + + +#remote-control-partner li#seven a { + width: 8px; + height: 13px; + left: 36px; + top: 502px; + background: url(${base_url}/seven.png) no-repeat 0 0px +} + +#remote-control-partner li#eight a { + width: 8px; + height: 13px; + left: 80px; + top: 502px; + background: url(${base_url}/eight.png) no-repeat 0 0px +} + +#remote-control-partner li#nine a { + width: 8px; + height: 13px; + left: 123px; + top: 502px; + background: url(${base_url}/nine.png) no-repeat 0 0px +} + + +#remote-control-partner li#zero a { + width: 11px; + height: 15px; + left: 80px; + top: 535px; + background: url(${base_url}/zero.png) no-repeat 0 0px +} + +#remote-control-partner li#volplus a { + width: 15px; + height: 15px; + left: 31px; + top: 261px; + background: url(${base_url}/volplus.png) no-repeat 0 0px +} + +#remote-control-partner li#volmin a { + width: 15px; + height: 3px; + left: 32px; + top: 299px; + background: url(${base_url}/volmin.png) no-repeat 0 0px +} + +#remote-control-partner li#menu a { + width: 16px; + height: 16px; + left: 23px; + top: 270px; + background: url(${base_url}/menu.png) no-repeat 0px 0px; +} + +#remote-control-partner li#mute a { + width: 15px; + height: 15px; + left: 75px; + top: 253px; + background: url(${base_url}/mute.png) no-repeat 0 0px +} + +#remote-control-partner li#record a { + width: 13px; + height: 13px; + left: 77px; + top: 298px; + background: url(${base_url}/record.png) no-repeat 0 0px +} + +#remote-control-partner li#section12 a { + width: 18px; + height: 14px; + left: 120px; + top: 536px; + background: url(${base_url}/section12.png) no-repeat 0 0px +} + +#remote-control-partner li#lastch a { + width: 14px; + height: 16px; + left: 32px; + top: 533px; + background: url(${base_url}/lastch.png) no-repeat 0 0px +} + +#remote-control-partner li#back a { + width: 17px; + height: 16px; + left: 30px; + top: 214px; + background: url(${base_url}/back.png) no-repeat 0px 0px; +} + +#remote-control-partner li#circle a { + width: 17px; + height: 17px; + left: 119px; + top: 213px; + background: url(${base_url}/circle.png) no-repeat 0px 0px; +} + +#remote-control-partner li#left a { + width: 13px; + height: 22px; + left: 35px; + top: 126px; + background: url(${base_url}/left.png) no-repeat 0px 0px +} + +#remote-control-partner li#right a { + width: 13px; + height: 22px; + left: 118px; + top: 126px; + background: url(${base_url}/right.png) no-repeat 0px 0px +} + +#remote-control-partner li#top a { + width: 22px; + height: 13px; + left: 72px; + top: 88px; + background: url(${base_url}/top.png) no-repeat 0px 0px +} + +#remote-control-partner li#bottom a { + width: 22px; + height: 13px; + left: 73px; + top: 170px; + background: url(${base_url}/bottom.png) no-repeat 0px 0px +} + +#remote-control-partner li#netflix a { + width: 50px; + height: 14px; + left: 25px; + top: 367px; + background: url(${base_url}/netflix.png) no-repeat 0px 0px +} + +#remote-control-partner li#fastforward a { + width: 16px; + height: 9px; + left: 123px; + top: 336px; + background: url(${base_url}/fastforward.png) no-repeat 0px 0px +} + +#remote-control-partner li#rewind a { + width: 16px; + height: 9px; + left: 25px; + top: 336px; + background: url(${base_url}/rewind.png) no-repeat 0px 0px +} + +#remote-control-partner li#play a { + width: 16px; + height: 10px; + left: 91px; + top: 336px; + background: url(${base_url}/play.png) no-repeat 0px 0px +} + +#remote-control-partner li#stop a { + width: 10px; + height: 10px; + left: 63px; + top: 336px; + background: url(${base_url}/stop.png) no-repeat 0px 0px +} + +#remote-control-partner li#vod a { + width: 28px; + height: 11px; + left: 102px; + top: 367px; + background: url(${base_url}/vod.png) no-repeat 0px 0px +} + +#remote-control-partner li#myrec a { + width: 35px; + height: 8px; + left: 102px; + top: 399px; + background: url(${base_url}/myrec.png) no-repeat 0px 0px +} + +#remote-control-partner li#youtube a { + width: 35px; + height: 15px; + left: 32px; + top: 396px; + background: url(${base_url}/youtube.png) no-repeat 0px 0px +} + +#remote-control-partner li#channelup a { + width: 15px; + height: 14px; + left: 120px; + top: 260px; + background: url(${base_url}/channelup.png) no-repeat 0px 0px +} + +#remote-control-partner li#channeldown a { + width: 15px; + height: 14px; + left: 120px; + top: 294px; + background: url(${base_url}/channeldown.png) no-repeat 0px 0px +} + +#remote-control-partner li#home a { + width: 25px; + height: 23px; + left: 70px; + top: 211px; + background: url(${base_url}/home.png) no-repeat 0px 0px +} + +#remote-control-partner li#ok a { + width: 23px; + height: 24px; + left: 71px; + top: 126px; + background: url(${base_url}/ok.png) no-repeat 0px 0px +} + +#remote-control-partner li#partner a:hover { + background: url(${base_url}/partner_over.png) no-repeat 0 0px +} + +#remote-control-partner li#one a:hover { + background: url(${base_url}/one_over.png) no-repeat 0 0px +} + + +#remote-control-partner li#two a:hover { + background: url(${base_url}/two_over.png) no-repeat 0 0px +} + +#remote-control-partner li#three a:hover { + background: url(${base_url}/three_over.png) no-repeat 0 0px +} + +#remote-control-partner li#four a:hover { + background: url(${base_url}/four_over.png) no-repeat 0 0px +} + +#remote-control-partner li#five a:hover { + background: url(${base_url}/five_over.png) no-repeat 0 0px +} + +#remote-control-partner li#six a:hover { + background: url(${base_url}/six_over.png) no-repeat 0 0px +} + +#remote-control-partner li#seven a:hover { + background: url(${base_url}/seven_over.png) no-repeat 0 0px +} + +#remote-control-partner li#eight a:hover { + background: url(${base_url}/eight_over.png) no-repeat 0 0px +} + +#remote-control-partner li#nine a:hover { + background: url(${base_url}/nine_over.png) no-repeat 0 0px +} + +#remote-control-partner li#zero a:hover { + background: url(${base_url}/zero_over.png) no-repeat 0 0px +} + +#remote-control-partner li#volplus a:hover { + background: url(${base_url}/volplus_over.png) no-repeat 0 0px +} + +#remote-control-partner li#volmin a:hover { + background: url(${base_url}/volmin_over.png) no-repeat 0 0px +} + +#remote-control-partner li#menu a:hover { + background: url(${base_url}/menu_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#section12 a:hover { + background: url(${base_url}/section12_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#lastch a:hover { + background: url(${base_url}/lastch_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#back a:hover { + background: url(${base_url}/back_over.png) no-repeat 0px 0px; +} + +#remote-control-partner li#circle a:hover { + background: url(${base_url}/circle_over.png) no-repeat 0px 0px; +} + +#remote-control-partner li#left a:hover { + background: url(${base_url}/left_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#right a:hover { + background: url(${base_url}/right_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#top a:hover { + background: url(${base_url}/top_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#bottom a:hover { + background: url(${base_url}/bottom_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#netflix a:hover { + background: url(${base_url}/netflix.png) no-repeat 0px 0px +} +#remote-control-partner li#fastforward a:hover { + background: url(${base_url}/fastforward_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#rewind a:hover { + background: url(${base_url}/rewind_over.png) no-repeat 0px 0px +} +#remote-control-partner li#play a:hover { + background: url(${base_url}/play_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#vod a:hover { + background: url(${base_url}/vod_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#myrec a:hover { + background: url(${base_url}/myrec_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#stop a:hover { + background: url(${base_url}/stop_over.png) no-repeat 0px 0px +} +#remote-control-partner li#youtube a:hover { + background: url(${base_url}/youtube.png) no-repeat 0px 0px +} + +#remote-control-partner li#channelup a:hover { + background: url(${base_url}/channelup_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#channeldown a:hover { + background: url(${base_url}/channeldown_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#mute a:hover { + background: url(${base_url}/mute_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#record a:hover { + background: url(${base_url}/record_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#home a:hover { + background: url(${base_url}/home_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#power a:hover { + background: url(${base_url}/power_over.png) no-repeat 0px 0px +} + +#remote-control-partner li#ok a:hover { + background: url(${base_url}/ok_over.png) no-repeat 0px 0px +} + `; +} + +function getRemoteHtml_partner(config){ + const template = config.remote_template; + return ` + +
+
+

${config.name}

+
+
+

+ Main navigation +

+ + + + + +
+
+ +`; +} diff --git a/config/www/community/generic-remote-control-card/remotes/partner/remote-screen-shot.png b/config/www/community/generic-remote-control-card/remotes/partner/remote-screen-shot.png new file mode 100644 index 0000000..3109f90 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/remote-screen-shot.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/rewind.png b/config/www/community/generic-remote-control-card/remotes/partner/rewind.png new file mode 100644 index 0000000..5226243 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/rewind.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/rewind_over.png b/config/www/community/generic-remote-control-card/remotes/partner/rewind_over.png new file mode 100644 index 0000000..a74a625 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/rewind_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/right.png b/config/www/community/generic-remote-control-card/remotes/partner/right.png new file mode 100644 index 0000000..518afc9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/right_over.png b/config/www/community/generic-remote-control-card/remotes/partner/right_over.png new file mode 100644 index 0000000..2b68448 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/right_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/section12.png b/config/www/community/generic-remote-control-card/remotes/partner/section12.png new file mode 100644 index 0000000..6ac1de4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/section12.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/section12_over.png b/config/www/community/generic-remote-control-card/remotes/partner/section12_over.png new file mode 100644 index 0000000..71478d7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/section12_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/seven.png b/config/www/community/generic-remote-control-card/remotes/partner/seven.png new file mode 100644 index 0000000..828d180 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/seven.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/seven_over.png b/config/www/community/generic-remote-control-card/remotes/partner/seven_over.png new file mode 100644 index 0000000..517942b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/seven_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/six.png b/config/www/community/generic-remote-control-card/remotes/partner/six.png new file mode 100644 index 0000000..69c0b55 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/six.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/six_over.png b/config/www/community/generic-remote-control-card/remotes/partner/six_over.png new file mode 100644 index 0000000..45eae29 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/six_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/source.png b/config/www/community/generic-remote-control-card/remotes/partner/source.png new file mode 100644 index 0000000..f3fd0b6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/source.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/source_over.png b/config/www/community/generic-remote-control-card/remotes/partner/source_over.png new file mode 100644 index 0000000..42f75bf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/source_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/stop.png b/config/www/community/generic-remote-control-card/remotes/partner/stop.png new file mode 100644 index 0000000..b0f9cbc Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/stop.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/stop_over.png b/config/www/community/generic-remote-control-card/remotes/partner/stop_over.png new file mode 100644 index 0000000..952c8bd Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/stop_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/three.png b/config/www/community/generic-remote-control-card/remotes/partner/three.png new file mode 100644 index 0000000..e0ce1a4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/three.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/three_over.png b/config/www/community/generic-remote-control-card/remotes/partner/three_over.png new file mode 100644 index 0000000..001cc1b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/three_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/top.png b/config/www/community/generic-remote-control-card/remotes/partner/top.png new file mode 100644 index 0000000..9921bed Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/top.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/top_over.png b/config/www/community/generic-remote-control-card/remotes/partner/top_over.png new file mode 100644 index 0000000..83ed489 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/top_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/two.png b/config/www/community/generic-remote-control-card/remotes/partner/two.png new file mode 100644 index 0000000..ffef8d2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/two.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/two_over.png b/config/www/community/generic-remote-control-card/remotes/partner/two_over.png new file mode 100644 index 0000000..7828c4f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/two_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/vod.png b/config/www/community/generic-remote-control-card/remotes/partner/vod.png new file mode 100644 index 0000000..4dfc7de Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/vod.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/vod_over.png b/config/www/community/generic-remote-control-card/remotes/partner/vod_over.png new file mode 100644 index 0000000..4839447 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/vod_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/volmin.png b/config/www/community/generic-remote-control-card/remotes/partner/volmin.png new file mode 100644 index 0000000..9928cdf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/volmin.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/volmin_over.png b/config/www/community/generic-remote-control-card/remotes/partner/volmin_over.png new file mode 100644 index 0000000..d0aba75 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/volmin_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/volplus.png b/config/www/community/generic-remote-control-card/remotes/partner/volplus.png new file mode 100644 index 0000000..d2d7e7c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/volplus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/volplus_over.png b/config/www/community/generic-remote-control-card/remotes/partner/volplus_over.png new file mode 100644 index 0000000..0ade8e6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/volplus_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/youtube.png b/config/www/community/generic-remote-control-card/remotes/partner/youtube.png new file mode 100644 index 0000000..f48f89f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/youtube.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/zero.png b/config/www/community/generic-remote-control-card/remotes/partner/zero.png new file mode 100644 index 0000000..d06721c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/zero.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/partner/zero_over.png b/config/www/community/generic-remote-control-card/remotes/partner/zero_over.png new file mode 100644 index 0000000..5f9d7c2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/partner/zero_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/back.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/back.png new file mode 100644 index 0000000..2f06b40 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/back_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/back_over.png new file mode 100644 index 0000000..34b7c28 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/back_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/bottom.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/bottom.png new file mode 100644 index 0000000..e2747b8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/bottom.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/bottom_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/bottom_over.png new file mode 100644 index 0000000..69fb4d5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/bottom_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/channeldown.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/channeldown.png new file mode 100644 index 0000000..7f88612 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/channeldown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/channeldown_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/channeldown_over.png new file mode 100644 index 0000000..62c8516 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/channeldown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/channelup.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/channelup.png new file mode 100644 index 0000000..b8958fe Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/channelup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/channelup_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/channelup_over.png new file mode 100644 index 0000000..d3708ab Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/channelup_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/eight.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/eight.png new file mode 100644 index 0000000..6872146 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/eight.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/eight_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/eight_over.png new file mode 100644 index 0000000..92d2c1f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/eight_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/exit.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/exit.png new file mode 100644 index 0000000..19a9391 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/exit.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/exit_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/exit_over.png new file mode 100644 index 0000000..c28b35f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/exit_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/fastforward.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/fastforward.png new file mode 100644 index 0000000..213898b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/fastforward.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/fastforward_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/fastforward_over.png new file mode 100644 index 0000000..dfebf35 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/fastforward_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/five.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/five.png new file mode 100644 index 0000000..7b3f79b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/five.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/five_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/five_over.png new file mode 100644 index 0000000..66053ba Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/five_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/four.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/four.png new file mode 100644 index 0000000..216dacf Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/four.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/four_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/four_over.png new file mode 100644 index 0000000..b8605e0 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/four_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/guide.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/guide.png new file mode 100644 index 0000000..b896c04 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/guide.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/guide_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/guide_over.png new file mode 100644 index 0000000..8b16966 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/guide_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/info.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/info.png new file mode 100644 index 0000000..77941c5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/info.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/info_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/info_over.png new file mode 100644 index 0000000..58a7553 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/info_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/lastch.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/lastch.png new file mode 100644 index 0000000..5ee8105 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/lastch.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/lastch_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/lastch_over.png new file mode 100644 index 0000000..1290910 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/lastch_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/left.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/left.png new file mode 100644 index 0000000..236b886 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/left_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/left_over.png new file mode 100644 index 0000000..2dc87a6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/left_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/menu.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/menu.png new file mode 100644 index 0000000..b93d86f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/menu.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/menu_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/menu_over.png new file mode 100644 index 0000000..343c142 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/menu_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/mute.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/mute.png new file mode 100644 index 0000000..a5e841e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/mute_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/mute_over.png new file mode 100644 index 0000000..2a093a3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/mute_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/nine.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/nine.png new file mode 100644 index 0000000..5cf173e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/nine.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/nine_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/nine_over.png new file mode 100644 index 0000000..2f86736 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/nine_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/ok.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/ok.png new file mode 100644 index 0000000..7c5821a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/ok.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/ok_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/ok_over.png new file mode 100644 index 0000000..894d72a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/ok_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/one.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/one.png new file mode 100644 index 0000000..072d99f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/one.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/one_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/one_over.png new file mode 100644 index 0000000..62938b6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/one_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/partner.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/partner.png new file mode 100644 index 0000000..c8bda0c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/partner.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/partner_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/partner_over.png new file mode 100644 index 0000000..c8bda0c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/partner_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/pause.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/pause.png new file mode 100644 index 0000000..5edb3c6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/pause.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/pause_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/pause_over.png new file mode 100644 index 0000000..f88959c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/pause_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/play.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/play.png new file mode 100644 index 0000000..f1be0b9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/play.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/play_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/play_over.png new file mode 100644 index 0000000..f03cf60 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/play_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/power.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/power.png new file mode 100644 index 0000000..d0caa8f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/power.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/power_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/power_over.png new file mode 100644 index 0000000..ba2de38 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/power_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-back.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-back.png new file mode 100644 index 0000000..ef02c89 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-html.js b/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-html.js new file mode 100644 index 0000000..4d7488e --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-html.js @@ -0,0 +1,521 @@ +function getRemoteStyle_samsungtv(config) { + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 360px; + margin: 0 auto +} + +#remote-control-samsungtv { + position: relative; + background: url(${base_url}/samsungtv_remote.png) no-repeat; + width: 224px; + height: 728px; + margin: auto; +} + +#remote-control-samsungtv ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#samsungtv a, +#volmin a, +#volplus a, +#mute a, +#source a, +#one a, +#two a, +#three a, +#four a, +#five a, +#six a, +#seven a, +#eight a, +#nine a, +#zero a, +#lastch a, +#fastforward a, +#rewind a, +#play a, +#pause a, +#stop a, +#channelup a, +#channeldown a, +#menu a, +#back a, +#exit a, +#guide a, +#info a, +#left a, +#right a, +#top a, +#bottom a, +#ok a { + position: absolute; + display: block +} + +#remote-control-samsungtv li#power a { + left: 33px; + top: 34px; + width: 27px; + height: 29px; + background: url(${base_url}/power.png) no-repeat +} + +#remote-control-samsungtv li#source a { + left: 152px; + top: 33px; + width: 40px; + height: 29px; + background: url(${base_url}/source.png) no-repeat +} + +#remote-control-samsungtv li#source a:hover { + background-image: url(${base_url}/source_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-samsungtv li#one a { + left: 42px; + top: 92px; + width: 10px; + height: 21px; + background: url(${base_url}/one.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#two a { + left: 103px; + top: 92px; + width: 14px; + height: 20px; + background: url(${base_url}/two.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#three a { + left: 166px; + top: 91px; + width: 14px; + height: 21px; + background: url(${base_url}/three.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#four a { + width: 15px; + height: 20px; + left: 41px; + top: 138px; + background: url(${base_url}/four.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#five a { + width: 14px; + height: 21px; + left: 104px; + top: 137px; + background: url(${base_url}/five.png) no-repeat 0 0px +} + + +#remote-control-samsungtv li#six a { + width: 14px; + height: 20px; + left: 166px; + top: 137px; + background: url(${base_url}/six.png) no-repeat 0 0px +} + + +#remote-control-samsungtv li#seven a { + width: 14px; + height: 21px; + left: 42px; + top: 183px; + background: url(${base_url}/seven.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#eight a { + width: 14px; + height: 21px; + left: 104px; + top: 183px; + background: url(${base_url}/eight.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#nine a { + width: 14px; + height: 20px; + left: 166px; + top: 183px; + background: url(${base_url}/nine.png) no-repeat 0 0px +} + + +#remote-control-samsungtv li#zero a { + width: 14px; + height: 20px; + left: 104px; + top: 229px; + background: url(${base_url}/zero.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#volplus a { + width: 25px; + height: 26px; + left: 39px; + top: 286px; + background: url(${base_url}/volplus.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#volmin a { + width: 26px; + height: 5px; + left: 39px; + top: 369px; + background: url(${base_url}/volmin.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#menu a { + width: 38px; + height: 28px; + left: 31px; + top: 415px; + background: url(${base_url}/menu.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#mute a { + width: 29px; + height: 28px; + left: 96px; + top: 288px; + background: url(${base_url}/mute.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#lastch a { + width: 40px; + height: 28px; + left: 153px; + top: 225px; + background: url(${base_url}/lastch.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#back a { + width: 38px; + height: 29px; + left: 31px; + top: 576px; + background: url(${base_url}/back.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#exit a { + width: 35px; + height: 28px; + left: 152px; + top: 577px; + background: url(${base_url}/exit.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#guide a { + width: 38px; + height: 14px; + left: 151px; + top: 423px; + background: url(${base_url}/guide.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#info a { + width: 27px; + height: 28px; + left: 157px; + top: 467px; + background: url(${base_url}/info.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#left a { + width: 20px; + height: 16px; + left: 44px; + top: 527px; + background: url(${base_url}/left.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#right a { + width: 19px; + height: 15px; + left: 157px; + top: 529px; + background: url(${base_url}/right.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#top a { + left: 102px; + top: 475px; + width: 16px; + height: 19px; + background: url(${base_url}/top.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#bottom a { + width: 16px; + height: 20px; + left: 102px; + top: 576px; + background: url(${base_url}/bottom.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#fastforward a { + width: 18px; + height: 12px; + left: 168px; + top: 703px; + background: url(${base_url}/fastforward.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#rewind a { + width: 18px; + height: 12px; + left: 35px; + top: 702px; + background: url(${base_url}/rewind.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#play a { + width: 11px; + height: 12px; + left: 81px; + top: 702px; + background: url(${base_url}/play.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#pause a { + width: 10px; + height: 11px; + left: 128px; + top: 703px; + background: url(${base_url}/pause.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#stop a { + width: 13px; + height: 13px; + left: 172px; + top: 666px; + background: url(${base_url}/stop.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#channelup a { + width: 34px; + height: 34px; + left: 152px; + top: 289px; + background: url(${base_url}/channelup.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#channeldown a { + width: 33px; + height: 33px; + left: 153px; + top: 349px; + background: url(${base_url}/channeldown.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#ok a { + width: 23px; + height: 15px; + left: 99px; + top: 528px; + background: url(${base_url}/ok.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#one a:hover { + background: url(${base_url}/one_over.png) no-repeat 0 0px +} + + +#remote-control-samsungtv li#two a:hover { + background: url(${base_url}/two_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#three a:hover { + background: url(${base_url}/three_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#four a:hover { + background: url(${base_url}/four_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#five a:hover { + background: url(${base_url}/five_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#six a:hover { + background: url(${base_url}/six_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#seven a:hover { + background: url(${base_url}/seven_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#eight a:hover { + background: url(${base_url}/eight_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#nine a:hover { + background: url(${base_url}/nine_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#zero a:hover { + background: url(${base_url}/zero_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#volplus a:hover { + background: url(${base_url}/volplus_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#volmin a:hover { + background: url(${base_url}/volmin_over.png) no-repeat 0 0px +} + +#remote-control-samsungtv li#menu a:hover { + background: url(${base_url}/menu_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#lastch a:hover { + background: url(${base_url}/lastch_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#back a:hover { + background: url(${base_url}/back_over.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#exit a:hover { + background: url(${base_url}/exit_over.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#guide a:hover { + background: url(${base_url}/guide_over.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#info a:hover { + background: url(${base_url}/info_over.png) no-repeat 0px 0px; +} + +#remote-control-samsungtv li#left a:hover { + background: url(${base_url}/left_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#right a:hover { + background: url(${base_url}/right_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#top a:hover { + background: url(${base_url}/top_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#bottom a:hover { + background: url(${base_url}/bottom_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#fastforward a:hover { + background: url(${base_url}/fastforward_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#rewind a:hover { + background: url(${base_url}/rewind_over.png) no-repeat 0px 0px +} +#remote-control-samsungtv li#play a:hover { + background: url(${base_url}/play_over.png) no-repeat 0px 0px +} +#remote-control-samsungtv li#pause a:hover { + background: url(${base_url}/pause_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#stop a:hover { + background: url(${base_url}/stop_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#channelup a:hover { + background: url(${base_url}/channelup_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#channeldown a:hover { + background: url(${base_url}/channeldown_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#mute a:hover { + background: url(${base_url}/mute_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#power a:hover { + background: url(${base_url}/power_over.png) no-repeat 0px 0px +} + +#remote-control-samsungtv li#ok a:hover { + background: url(${base_url}/ok_over.png) no-repeat 0px 0px +} + `; +} + +function getRemoteHtml_samsungtv(config){ + const template = config.remote_template; + return ` +
+
+

${config.name}

+
+
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
  • + +
  • +
  • +
  • +
  • +
  • + +
  • +
  • +
  • +
+
+
+`; +} diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-screen-shot.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-screen-shot.png new file mode 100644 index 0000000..9a7ae83 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/remote-screen-shot.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/rewind.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/rewind.png new file mode 100644 index 0000000..7d99579 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/rewind.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/rewind_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/rewind_over.png new file mode 100644 index 0000000..dbed1b4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/rewind_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/right.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/right.png new file mode 100644 index 0000000..98fc0e7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/right_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/right_over.png new file mode 100644 index 0000000..2807549 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/right_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/samsungtv_remote.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/samsungtv_remote.png new file mode 100644 index 0000000..9a7ae83 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/samsungtv_remote.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/seven.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/seven.png new file mode 100644 index 0000000..2351e59 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/seven.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/seven_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/seven_over.png new file mode 100644 index 0000000..f848ce5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/seven_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/six.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/six.png new file mode 100644 index 0000000..10b32e8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/six.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/six_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/six_over.png new file mode 100644 index 0000000..4da7fa7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/six_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/source.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/source.png new file mode 100644 index 0000000..2ee9e4b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/source.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/source_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/source_over.png new file mode 100644 index 0000000..7704b06 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/source_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/stop.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/stop.png new file mode 100644 index 0000000..6f50c21 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/stop.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/stop_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/stop_over.png new file mode 100644 index 0000000..151a391 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/stop_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/three.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/three.png new file mode 100644 index 0000000..1f644fd Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/three.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/three_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/three_over.png new file mode 100644 index 0000000..2d93e81 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/three_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/top.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/top.png new file mode 100644 index 0000000..49491af Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/top.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/top_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/top_over.png new file mode 100644 index 0000000..e2ec6f7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/top_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/two.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/two.png new file mode 100644 index 0000000..6cc8591 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/two.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/two_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/two_over.png new file mode 100644 index 0000000..3dc5a70 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/two_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/volmin.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/volmin.png new file mode 100644 index 0000000..f7a07fa Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/volmin.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/volmin_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/volmin_over.png new file mode 100644 index 0000000..33ba05d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/volmin_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/volplus.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/volplus.png new file mode 100644 index 0000000..8860b5d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/volplus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/volplus_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/volplus_over.png new file mode 100644 index 0000000..1505362 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/volplus_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/zero.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/zero.png new file mode 100644 index 0000000..7d3d512 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/zero.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/samsungtv/zero_over.png b/config/www/community/generic-remote-control-card/remotes/samsungtv/zero_over.png new file mode 100644 index 0000000..e26f524 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/samsungtv/zero_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/simple/remote-back2.png b/config/www/community/generic-remote-control-card/remotes/simple/remote-back2.png new file mode 100644 index 0000000..4ca2dc3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/simple/remote-back2.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/simple/remote-button.jpg b/config/www/community/generic-remote-control-card/remotes/simple/remote-button.jpg new file mode 100644 index 0000000..b5de765 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/simple/remote-button.jpg differ diff --git a/config/www/community/generic-remote-control-card/remotes/simple/remote-circle3.jpg b/config/www/community/generic-remote-control-card/remotes/simple/remote-circle3.jpg new file mode 100644 index 0000000..fd7d0c5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/simple/remote-circle3.jpg differ diff --git a/config/www/community/generic-remote-control-card/remotes/simple/remote-circle3.jpgx b/config/www/community/generic-remote-control-card/remotes/simple/remote-circle3.jpgx new file mode 100644 index 0000000..fd7d0c5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/simple/remote-circle3.jpgx differ diff --git a/config/www/community/generic-remote-control-card/remotes/simple/remote-home.jpg b/config/www/community/generic-remote-control-card/remotes/simple/remote-home.jpg new file mode 100644 index 0000000..4dddd3e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/simple/remote-home.jpg differ diff --git a/config/www/community/generic-remote-control-card/remotes/simple/remote-html.js b/config/www/community/generic-remote-control-card/remotes/simple/remote-html.js new file mode 100644 index 0000000..14e79d4 --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/simple/remote-html.js @@ -0,0 +1,378 @@ +function getRemoteStyle_simple(config) { + + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 224px; + margin: 0 auto +} + +#remote-control-simple { + position: relative; + background: url(${base_url}/remote-back2.png) no-repeat; + width: 224px; + height: 507px +} + +#remote-control-simple h2, +#remote-control-simple span { + position: absolute; + left: -5000px +} + +#remote-control-simple ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#power a, +#volmin a, +#volplus a, +#mute a, +#source a, +#button1 a, +#button2 a, +#button3 a, +#button4 a, +#button5 a, +#button6 a, +#button7 a, +#button8 a, +#button9 a, +#buttonClear a, +#button0 a, +#buttonEnter a, +#exit a, +#info a, +#menu a, +#back a, +#left a, +#right a, +#top a, +#bottom a, +#ok a { + position: absolute; + display: block +} + +#remote-control-simple li#power a { + left: 33px; + top: 29px; + width: 37px; + height: 37px; + background: url(${base_url}/remote-home.jpg) no-repeat +} + +#remote-control-simple li#power a:hover { + background-image: url(${base_url}/remote-home.jpg); + background-repeat: no-repeat; + background-position: 0 -37px +} + + +#remote-control-simple li#source a { + left: 154px; + top: 29px; + width: 37px; + height: 37px; + background: url(${base_url}/remote-sitemap.jpg) no-repeat +} + +#remote-control-simple li#source a:hover { + background-image: url(${base_url}/remote-sitemap.jpg); + background-repeat: no-repeat; + background-position: 0 -37px +} + +#remote-control-simple li#volmin a, +#remote-control-simple li#volplus a, +#remote-control-simple li#mute a, +#remote-control-simple li#button1 a, +#remote-control-simple li#button2 a, +#remote-control-simple li#button3 a, +#remote-control-simple li#button4 a, +#remote-control-simple li#button5 a, +#remote-control-simple li#button6 a, +#remote-control-simple li#button7 a, +#remote-control-simple li#button8 a, +#remote-control-simple li#button9 a, +#remote-control-simple li#buttonClear a, +#remote-control-simple li#button0 a, +#remote-control-simple li#buttonEnter a { + width: 41px; + height: 21px; + background: url(${base_url}/remote-button.jpg) no-repeat +} + +#remote-control-simple li#volmin a { + left: 91px; + top: 40px +} + +#remote-control-simple li#volplus a { + left: 91px; + top: 10px +} + +#remote-control-simple li#mute a { + left: 91px; + top: 72px +} + +#remote-control-simple li#button1 a { + left: 38px; + top: 114px +} + +#remote-control-simple li#button2 a { + left: 91px; + top: 114px +} + +#remote-control-simple li#button3 a { + left: 145px; + top: 114px +} + +#remote-control-simple li#button4 a { + left: 38px; + top: 155px +} + +#remote-control-simple li#button5 a { + left: 91px; + top: 155px +} + +#remote-control-simple li#button6 a { + left: 145px; + top: 155px +} + +#remote-control-simple li#button7 a { + left: 38px; + top: 196px +} + +#remote-control-simple li#button8 a { + left: 91px; + top: 196px +} + +#remote-control-simple li#button9 a { + left: 145px; + top: 196px +} + +#remote-control-simple li#buttonClear a { + left: 38px; + top: 237px +} + +#remote-control-simple li#button0 a { + left: 91px; + top: 237px +} + +#remote-control-simple li#buttonEnter a { + left: 145px; + top: 237px +} + +#remote-control-simple li#volmin a:hover, +#remote-control-simple li#volplus a:hover, +#remote-control-simple li#mute a:hover, +#remote-control-simple li#button1 a:hover, +#remote-control-simple li#button2 a:hover, +#remote-control-simple li#button3 a:hover, +#remote-control-simple li#button4 a:hover, +#remote-control-simple li#button5 a:hover, +#remote-control-simple li#button6 a:hover, +#remote-control-simple li#button7 a:hover, +#remote-control-simple li#button8 a:hover, +#remote-control-simple li#button9 a:hover, +#remote-control-simple li#buttonClear a:hover, +#remote-control-simple li#button0 a:hover, +#remote-control-simple li#buttonEnter a:hover { + background-image: url(${base_url}/remote-button.jpg); + background-repeat: no-repeat; + background-position: 0 -21px +} + +#remote-control-simple li#exit a { + width: 29px; + height: 30px; + left: 41px; + top: 331px; + background: url(${base_url}/remote-circle3.jpg) no-repeat 0 -19px +} + +#remote-control-simple li#info a { + width: 33px; + height: 23px; + left: 74px; + top: 312px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -33px 0px +} + +#remote-control-simple li#menu a { + width: 33px; + height: 23px; + left: 116px; + top: 312px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -75px 0 +} + +#remote-control-simple li#back a { + width: 29px; + height: 30px; + left: 154px; + top: 332px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -113px -20px +} + +#remote-control-simple li#left a { + width: 25px; + height: 59px; + left: 58px; + top: 365px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -17px -53px +} + +#remote-control-simple li#right a { + width: 23px; + height: 59px; + left: 139px; + top: 365px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -98px -53px +} + +#remote-control-simple li#top a { + width: 62px; + height: 23px; + left: 80px; + top: 342px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -39px -30px +} + +#remote-control-simple li#bottom a { + width: 61px; + height: 22px; + left: 80px; + top: 424px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -39px -112px +} + +#remote-control-simple li#ok a { + width: 44px; + height: 46px; + left: 89px; + top: 371px; + background: url(${base_url}/remote-circle3.jpg) no-repeat -48px -59px +} + +#remote-control-simple li#exit a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat 0 -157px +} + +#remote-control-simple li#info a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -33px -138px +} + +#remote-control-simple li#menu a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -75px -138px +} + +#remote-control-simple li#back a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -113px -158px +} + +#remote-control-simple li#left a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -17px -191px +} + +#remote-control-simple li#right a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -98px -191px +} + +#remote-control-simple li#top a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -39px -168px +} + +#remote-control-simple li#bottom a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -39px -250px +} + +#remote-control-simple li#ok a:hover { + background: url(${base_url}/remote-circle3.jpg) no-repeat -48px -197px +} + + `; +} + +function getRemoteHtml_simple(config){return ` + +
+
+

${config.name}

+
+
+

+ Main navigation +

+ +

+ Select a site section +

+ +

+ Photo gallery exiter +

+ +
+
+ +`; +} \ No newline at end of file diff --git a/config/www/community/generic-remote-control-card/remotes/simple/remote-sitemap.jpg b/config/www/community/generic-remote-control-card/remotes/simple/remote-sitemap.jpg new file mode 100644 index 0000000..9978c98 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/simple/remote-sitemap.jpg differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/amp.png b/config/www/community/generic-remote-control-card/remotes/yesremote/amp.png new file mode 100644 index 0000000..1949a10 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/amp.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/amp_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/amp_over.png new file mode 100644 index 0000000..2855a5c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/amp_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/av.png b/config/www/community/generic-remote-control-card/remotes/yesremote/av.png new file mode 100644 index 0000000..79f7bf8 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/av.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/av_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/av_over.png new file mode 100644 index 0000000..f54f3c6 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/av_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/blue.png b/config/www/community/generic-remote-control-card/remotes/yesremote/blue.png new file mode 100644 index 0000000..1df728f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/blue.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/chdown.png b/config/www/community/generic-remote-control-card/remotes/yesremote/chdown.png new file mode 100644 index 0000000..13be2fe Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/chdown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/chup.png b/config/www/community/generic-remote-control-card/remotes/yesremote/chup.png new file mode 100644 index 0000000..233204e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/chup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/down.png b/config/www/community/generic-remote-control-card/remotes/yesremote/down.png new file mode 100644 index 0000000..2799007 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/down.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/eight.png b/config/www/community/generic-remote-control-card/remotes/yesremote/eight.png new file mode 100644 index 0000000..9056119 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/eight.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/eight_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/eight_over.png new file mode 100644 index 0000000..3153129 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/eight_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/five.png b/config/www/community/generic-remote-control-card/remotes/yesremote/five.png new file mode 100644 index 0000000..9777900 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/five.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/five_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/five_over.png new file mode 100644 index 0000000..db10873 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/five_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/forward.png b/config/www/community/generic-remote-control-card/remotes/yesremote/forward.png new file mode 100644 index 0000000..0c7bddc Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/forward.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/four.png b/config/www/community/generic-remote-control-card/remotes/yesremote/four.png new file mode 100644 index 0000000..729db7f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/four.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/four_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/four_over.png new file mode 100644 index 0000000..7b23753 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/four_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/green.png b/config/www/community/generic-remote-control-card/remotes/yesremote/green.png new file mode 100644 index 0000000..1f92db4 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/green.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/in.png b/config/www/community/generic-remote-control-card/remotes/yesremote/in.png new file mode 100644 index 0000000..a46f66b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/in.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/in_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/in_over.png new file mode 100644 index 0000000..8937ed7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/in_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/info.png b/config/www/community/generic-remote-control-card/remotes/yesremote/info.png new file mode 100644 index 0000000..ae6f568 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/info.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/lef.png b/config/www/community/generic-remote-control-card/remotes/yesremote/lef.png new file mode 100644 index 0000000..61620ae Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/lef.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/left.png b/config/www/community/generic-remote-control-card/remotes/yesremote/left.png new file mode 100644 index 0000000..2a44f2b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/left.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/mute.png b/config/www/community/generic-remote-control-card/remotes/yesremote/mute.png new file mode 100644 index 0000000..ee076b3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/mute.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/mytv.png b/config/www/community/generic-remote-control-card/remotes/yesremote/mytv.png new file mode 100644 index 0000000..fc0b735 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/mytv.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/mytv_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/mytv_over.png new file mode 100644 index 0000000..157f06c Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/mytv_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/nine.png b/config/www/community/generic-remote-control-card/remotes/yesremote/nine.png new file mode 100644 index 0000000..b9dbc30 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/nine.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/nine_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/nine_over.png new file mode 100644 index 0000000..c25b114 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/nine_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/ok.png b/config/www/community/generic-remote-control-card/remotes/yesremote/ok.png new file mode 100644 index 0000000..e8a7908 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/ok.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/one.png b/config/www/community/generic-remote-control-card/remotes/yesremote/one.png new file mode 100644 index 0000000..efe877b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/one.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/one_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/one_over.png new file mode 100644 index 0000000..3cdfa0d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/one_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/orange.png b/config/www/community/generic-remote-control-card/remotes/yesremote/orange.png new file mode 100644 index 0000000..d9cc7d1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/orange.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/pause.png b/config/www/community/generic-remote-control-card/remotes/yesremote/pause.png new file mode 100644 index 0000000..6b234be Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/pause.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/play.png b/config/www/community/generic-remote-control-card/remotes/yesremote/play.png new file mode 100644 index 0000000..cc1da28 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/play.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/plus.png b/config/www/community/generic-remote-control-card/remotes/yesremote/plus.png new file mode 100644 index 0000000..f55590a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/plus.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/rec.png b/config/www/community/generic-remote-control-card/remotes/yesremote/rec.png new file mode 100644 index 0000000..3fa9752 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/rec.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/remote-back.png b/config/www/community/generic-remote-control-card/remotes/yesremote/remote-back.png new file mode 100644 index 0000000..2e02058 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/remote-back.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/remote-html.js b/config/www/community/generic-remote-control-card/remotes/yesremote/remote-html.js new file mode 100644 index 0000000..80407fe --- /dev/null +++ b/config/www/community/generic-remote-control-card/remotes/yesremote/remote-html.js @@ -0,0 +1,776 @@ +function getRemoteStyle_yesremote(config) { + + const template = config.remote_template; + const base_url = `/hacsfiles/generic-remote-control-card/remotes/${template}`; + + return ` + ha-card{ + background-color:transparent; + box-shadow:var(--paper-material-elevation-0_-_box-shadow); + } + body { + margin: 0; + padding: 20px; + font-family: "Helvetica Neue", Helvetica, Arial, Geneva, sans-serif +} + +.container { + width: 224px; + margin: 0 auto +} + +#remote-control-yesremote { + position: relative; + background: url(${base_url}/remote-back.png) no-repeat; + width: 245px; + height: 900px +} + +#remote-control-yesremote h2, +#remote-control-yesremote span { + position: absolute; + left: -5000px +} + +#remote-control-yesremote ul { + margin: 0; + padding: 0; + list-style-type: none +} + +#tvpower a, +#yespower a, +#amppower a, +#av a, +#in a, +#button1 a, +#button2 a, +#button3 a, +#button4 a, +#button5 a, +#button6 a, +#button7 a, +#button8 a, +#button9 a, +#button0 a, +#vod a, +#mytv a, +#volumeup a, +#volumedown a, +#mute a, +#return a, +#channelup a, +#channeldown a, +#clickleft a, +#clickright a, +#clickup a, +#clickdown a, +#plus a, +#info a, +#ok a, +#table a, +#schedule a, +#orange a, +#green a, +#yellow a, +#blue a, +#rewind a, +#play a, +#forward a, +#rec a, +#stop a, +#pause a, +#microphone a { + position: absolute; + display: block +} + +#remote-control-yesremote li#tvpower a { + left: 57px; + top: 52px; + width: 37px; + height: 32px; + background: url(${base_url}/tv.png) no-repeat +} + +#remote-control-yesremote li#tvpower a:hover { + background-image: url(${base_url}/tv_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-yesremote li#yespower a { + left: 105px; + top: 50px; + width: 37px; + height: 32px; + background: url(${base_url}/yes.png) no-repeat +} + +#remote-control-yesremote li#yespower a:hover { + background-image: url(${base_url}/yes_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#amppower a { + left: 155px; + top: 51px; + width: 39px; + height: 32px; + background: url(${base_url}/amp.png) no-repeat +} + +#remote-control-yesremote li#amppower a:hover { + background-image: url(${base_url}/amp_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-yesremote li#av a { + left: 51px; + top: 94px; + width: 38px; + height: 38px; + background: url(${base_url}/av.png) no-repeat +} + +#remote-control-yesremote li#av a:hover { + background-image: url(${base_url}/av_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-yesremote li#in a { + left: 162px; + top: 93px; + width: 38px; + height: 38px; + background: url(${base_url}/in.png) no-repeat +} + +#remote-control-yesremote li#in a:hover { + background-image: url(${base_url}/in_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button1 a { + left: 47px; + top: 145px; + width: 47px; + height: 34px; + background: url(${base_url}/one.png) no-repeat +} + +#remote-control-yesremote li#button1 a:hover { + background-image: url(${base_url}/one_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button2 a { + left: 104px; + top: 144px; + width: 41px; + height: 36px; + background: url(${base_url}/two.png) no-repeat +} + +#remote-control-yesremote li#button2 a:hover { + background-image: url(${base_url}/two_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button3 a { + left: 155px; + top: 145px; + width: 48px; + height: 34px; + background: url(${base_url}/three.png) no-repeat +} + +#remote-control-yesremote li#button3 a:hover { + background-image: url(${base_url}/three_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button4 a { + left: 47px; + top: 194px; + width: 46px; + height: 34px; + background: url(${base_url}/four.png) no-repeat +} + +#remote-control-yesremote li#button4 a:hover { + background-image: url(${base_url}/four_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button5 a { + left: 104px; + top: 193px; + width: 41px; + height: 36px; + background: url(${base_url}/five.png) no-repeat +} + +#remote-control-yesremote li#button5 a:hover { + background-image: url(${base_url}/five_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button6 a { + left: 156px; + top: 193px; + width: 46px; + height: 35px; + background: url(${base_url}/six.png) no-repeat +} + +#remote-control-yesremote li#button6 a:hover { + background-image: url(${base_url}/six_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button7 a { + left: 47px; + top: 243px; + width: 46px; + height: 34px; + background: url(${base_url}/seven.png) no-repeat +} + +#remote-control-yesremote li#button7 a:hover { + background-image: url(${base_url}/seven_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button8 a { + left: 104px; + top: 241px; + width: 41px; + height: 36px; + background: url(${base_url}/eight.png) no-repeat +} + +#remote-control-yesremote li#button8 a:hover { + background-image: url(${base_url}/eight_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button9 a { + left: 156px; + top: 241px; + width: 46px; + height: 35px; + background: url(${base_url}/nine.png) no-repeat +} + +#remote-control-yesremote li#button9 a:hover { + background-image: url(${base_url}/nine_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#button0 a { + left: 104px; + top: 289px; + width: 38px; + height: 38px; + background: url(${base_url}/zero.png) no-repeat +} + +#remote-control-yesremote li#button0 a:hover { + background-image: url(${base_url}/zero_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-yesremote li#vod a { + left: 48px; + top: 291px; + width: 46px; + height: 34px; + background: url(${base_url}/vod.png) no-repeat +} + +#remote-control-yesremote li#vod a:hover { + background-image: url(${base_url}/vod_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-yesremote li#mytv a { + left: 154px; + top: 289px; + width: 46px; + height: 35px; + background: url(${base_url}/mytv.png) no-repeat +} + +#remote-control-yesremote li#mytv a:hover { + background-image: url(${base_url}/mytv_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + + +#remote-control-yesremote li#volumeup a { + left: 33px; + top: 340px; + width: 44px; + height: 41px; + background: url(${base_url}/vup.png) no-repeat +} + +#remote-control-yesremote li#volumeup a:hover { + background-image: url(${base_url}/vup_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-yesremote li#volumedown a { + left: 38px; + top: 379px; + width: 43px; + height: 40px; + background: url(${base_url}/vdown.png) no-repeat +} + +#remote-control-yesremote li#volumedown a:hover { + background-image: url(${base_url}/vdown_over.png); + background-repeat: no-repeat; + background-position: 0 0px +} + +#remote-control-yesremote li#channelup a { + left: 173px; + top: 339px; + width: 43px; + height: 42px; + background: url(${base_url}/chup.png) no-repeat +} + +#remote-control-yesremote li#channelup a:hover { + background-image: url(${base_url}/chup.png); + background-repeat: no-repeat; + background-position: 0 -42px +} + +#remote-control-yesremote li#channeldown a { + left: 168px; + top: 378px; + width: 43px; + height: 40px; + background: url(${base_url}/chdown.png) no-repeat +} + +#remote-control-yesremote li#channeldown a:hover { + background-image: url(${base_url}/chdown.png); + background-repeat: no-repeat; + background-position: 0 -42px +} + +#remote-control-yesremote li#mute a { + left: 96px; + top: 340px; + width: 57px; + height: 35px; + background: url(${base_url}/mute.png) no-repeat +} + +#remote-control-yesremote li#mute a:hover { + background-image: url(${base_url}/mute.png); + background-repeat: no-repeat; + background-position: 0 -35px +} + +#remote-control-yesremote li#return a { + left: 104px; + top: 385px; + width: 42px; + height: 37px; + background: url(${base_url}/return.png) no-repeat +} + +#remote-control-yesremote li#return a:hover { + background-image: url(${base_url}/return.png); + background-repeat: no-repeat; + background-position: 0 -37px +} + +#remote-control-yesremote li#plus a { + left: 170px; + top: 439px; + width: 40px; + height: 39px; + background: url(${base_url}/plus.png) no-repeat +} + +#remote-control-yesremote li#plus a:hover { + background-image: url(${base_url}/plus.png); + background-repeat: no-repeat; + background-position: 0 -40px +} + + +#remote-control-yesremote li#info a { + left: 171px; + top: 568px; + width: 40px; + height: 39px; + background: url(${base_url}/info.png) no-repeat +} + +#remote-control-yesremote li#info a:hover { + background-image: url(${base_url}/info.png); + background-repeat: no-repeat; + background-position: 0 -40px +} + +#remote-control-yesremote li#table a { + left: 40px; + top: 568px; + width: 40px; + height: 39px; + background: url(${base_url}/table.png) no-repeat +} + +#remote-control-yesremote li#table a:hover { + background-image: url(${base_url}/table.png); + background-repeat: no-repeat; + background-position: 0 -40px +} + +#remote-control-yesremote li#schedule a { + left: 40px; + top: 439px; + width: 40px; + height: 39px; + background: url(${base_url}/schedule.png) no-repeat +} + +#remote-control-yesremote li#schedule a:hover { + background-image: url(${base_url}/schedule.png); + background-repeat: no-repeat; + background-position: 0 -40px +} + +#remote-control-yesremote li#ok a { + left: 97px; + top: 493px; + width: 57px; + height: 60px; + background: url(${base_url}/ok.png) no-repeat +} + +#remote-control-yesremote li#ok a:hover { + background-image: url(${base_url}/ok.png); + background-repeat: no-repeat; + background-position: 0 -60px +} + +#remote-control-yesremote li#clickup a { + left: 71px; + top: 448px; + width: 113px; + height: 75px; + background: url(${base_url}/up.png) no-repeat +} + +#remote-control-yesremote li#clickup a:hover { + background-image: url(${base_url}/up.png); + background-repeat: no-repeat; + background-position: 0 -75px +} + +#remote-control-yesremote li#clickdown a { + left: 68px; + top: 522px; + width: 113px; + height: 75px; + background: url(${base_url}/down.png) no-repeat +} + +#remote-control-yesremote li#clickdown a:hover { + background-image: url(${base_url}/down.png); + background-repeat: no-repeat; + background-position: 0 -75px +} + + +#remote-control-yesremote li#clickright a { + left: 122px; + top: 475px; + width: 76px; + height: 95px; + background: url(${base_url}/right.png) no-repeat +} + +#remote-control-yesremote li#clickright a:hover { + background-image: url(${base_url}/right.png); + background-repeat: no-repeat; + background-position: 0 -95px +} + + +#remote-control-yesremote li#clickleft a { + left: 49px; + top: 468px; + width: 80px; + height: 109px; + background: url(${base_url}/left.png) no-repeat +} + +#remote-control-yesremote li#clickleft a:hover { + background-image: url(${base_url}/left.png); + background-repeat: no-repeat; + background-position: 0 -109px +} + +#remote-control-yesremote li#orange a { + left: 46px; + top: 632px; + width: 32px; + height: 30px; + background: url(${base_url}/orange.png) no-repeat +} + +#remote-control-yesremote li#orange a:hover { + background-image: url(${base_url}/orange.png); + background-repeat: no-repeat; + background-position: 0 -30px +} + +#remote-control-yesremote li#green a { + left: 87px; + top: 623px; + width: 32px; + height: 30px; + background: url(${base_url}/green.png) no-repeat +} + +#remote-control-yesremote li#green a:hover { + background-image: url(${base_url}/green.png); + background-repeat: no-repeat; + background-position: 0 -30px +} +#remote-control-yesremote li#yellow a { + left: 131px; + top: 623px; + width: 32px; + height: 30px; + background: url(${base_url}/yellow.png) no-repeat +} + +#remote-control-yesremote li#yellow a:hover { + background-image: url(${base_url}/yellow.png); + background-repeat: no-repeat; + background-position: 0 -30px +} +#remote-control-yesremote li#blue a { + left: 172px; + top: 633px; + width: 32px; + height: 30px; + background: url(${base_url}/blue.png) no-repeat +} + +#remote-control-yesremote li#blue a:hover { + background-image: url(${base_url}/blue.png); + background-repeat: no-repeat; + background-position: 0 -30px +} + +#remote-control-yesremote li#play a { + left: 103px; + top: 672px; + width: 44px; + height: 43px; + background: url(${base_url}/play.png) no-repeat +} + +#remote-control-yesremote li#play a:hover { + background-image: url(${base_url}/play.png); + background-repeat: no-repeat; + background-position: 0 -43px +} + +#remote-control-yesremote li#stop a { + left: 103px; + top: 721px; + width: 44px; + height: 43px; + background: url(${base_url}/stop.png) no-repeat +} + +#remote-control-yesremote li#stop a:hover { + background-image: url(${base_url}/stop.png); + background-repeat: no-repeat; + background-position: 0 -43px +} + +#remote-control-yesremote li#rewind a { + left: 51px; + top: 676px; + width: 45px; + height: 38px; + background: url(${base_url}/rewind.png) no-repeat +} + +#remote-control-yesremote li#rewind a:hover { + background-image: url(${base_url}/rewind.png); + background-repeat: no-repeat; + background-position: 0 -41px +} + +#remote-control-yesremote li#rec a { + left: 53px; + top: 721px; + width: 45px; + height: 38px; + background: url(${base_url}/rec.png) no-repeat +} + +#remote-control-yesremote li#rec a:hover { + background-image: url(${base_url}/rec.png); + background-repeat: no-repeat; + background-position: 0 -43px +} + +#remote-control-yesremote li#forward a { + left: 153px; + top: 676px; + width: 45px; + height: 38px; + background: url(${base_url}/forward.png) no-repeat +} + +#remote-control-yesremote li#forward a:hover { + background-image: url(${base_url}/forward.png); + background-repeat: no-repeat; + background-position: 0 -43px +} + +#remote-control-yesremote li#pause a { + left: 153px; + top: 721px; + width: 45px; + height: 38px; + background: url(${base_url}/pause.png) no-repeat +} + +#remote-control-yesremote li#pause a:hover { + background-image: url(${base_url}/pause.png); + background-repeat: no-repeat; + background-position: 0 -38px +} + + `; +} + +function getRemoteHtml_yesremote(config){ + const template = config.remote_template; + return ` + +
+
+

${config.name}

+
+
+

+ Power and Source +

+ + +

+ Numbers +

+ +

+ Volume and Channels +

+ +
  • volumeup
  • +
  • volumedown
  • +
  • channelup
  • +
  • channeldown
  • +
  • mute
  • +
  • return
  • +
    +

    + Navigation +

    + +
  • clickdown
  • +
  • clickup
  • +
  • clickleft
  • +
  • clickright
  • +
  • ok
  • +
  • info
  • +
  • plus
  • +
  • table
  • +
  • schedule
  • +
    +

    + DVR +

    + +
  • orange
  • +
  • green
  • +
  • yellow
  • +
  • blue
  • +
  • rewind
  • +
  • play
  • +
  • plus
  • +
  • forward
  • +
  • stop
  • +
  • pause
  • +
    + + +
    +
    + +`; +} \ No newline at end of file diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/return.png b/config/www/community/generic-remote-control-card/remotes/yesremote/return.png new file mode 100644 index 0000000..4d83d18 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/return.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/rewind.png b/config/www/community/generic-remote-control-card/remotes/yesremote/rewind.png new file mode 100644 index 0000000..b7fa111 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/rewind.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/right.png b/config/www/community/generic-remote-control-card/remotes/yesremote/right.png new file mode 100644 index 0000000..451ecf3 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/right.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/schedule.png b/config/www/community/generic-remote-control-card/remotes/yesremote/schedule.png new file mode 100644 index 0000000..09b19a9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/schedule.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/seven.png b/config/www/community/generic-remote-control-card/remotes/yesremote/seven.png new file mode 100644 index 0000000..06a3d7e Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/seven.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/seven_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/seven_over.png new file mode 100644 index 0000000..42953b1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/seven_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/six.png b/config/www/community/generic-remote-control-card/remotes/yesremote/six.png new file mode 100644 index 0000000..67d7ab9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/six.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/six_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/six_over.png new file mode 100644 index 0000000..63f5f72 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/six_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/stop.png b/config/www/community/generic-remote-control-card/remotes/yesremote/stop.png new file mode 100644 index 0000000..9d00b22 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/stop.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/table.png b/config/www/community/generic-remote-control-card/remotes/yesremote/table.png new file mode 100644 index 0000000..4c015e2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/table.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/three.png b/config/www/community/generic-remote-control-card/remotes/yesremote/three.png new file mode 100644 index 0000000..c0c07f9 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/three.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/three_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/three_over.png new file mode 100644 index 0000000..870a87a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/three_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/tv.png b/config/www/community/generic-remote-control-card/remotes/yesremote/tv.png new file mode 100644 index 0000000..fac608b Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/tv.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/tv_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/tv_over.png new file mode 100644 index 0000000..0ec0f33 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/tv_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/two.png b/config/www/community/generic-remote-control-card/remotes/yesremote/two.png new file mode 100644 index 0000000..9f1401f Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/two.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/two_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/two_over.png new file mode 100644 index 0000000..4bd24b7 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/two_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/up.png b/config/www/community/generic-remote-control-card/remotes/yesremote/up.png new file mode 100644 index 0000000..dfb2aef Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/up.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/vdown.png b/config/www/community/generic-remote-control-card/remotes/yesremote/vdown.png new file mode 100644 index 0000000..525f3ae Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/vdown.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/vdown_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/vdown_over.png new file mode 100644 index 0000000..069fd84 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/vdown_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/vod.png b/config/www/community/generic-remote-control-card/remotes/yesremote/vod.png new file mode 100644 index 0000000..6675f64 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/vod.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/vod_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/vod_over.png new file mode 100644 index 0000000..1a301d1 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/vod_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/vup.png b/config/www/community/generic-remote-control-card/remotes/yesremote/vup.png new file mode 100644 index 0000000..49d7bb2 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/vup.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/vup_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/vup_over.png new file mode 100644 index 0000000..2ddafa5 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/vup_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/yellow.png b/config/www/community/generic-remote-control-card/remotes/yesremote/yellow.png new file mode 100644 index 0000000..332d440 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/yellow.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/yes.png b/config/www/community/generic-remote-control-card/remotes/yesremote/yes.png new file mode 100644 index 0000000..54f4cbb Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/yes.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/yes_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/yes_over.png new file mode 100644 index 0000000..61e982d Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/yes_over.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/zero.png b/config/www/community/generic-remote-control-card/remotes/yesremote/zero.png new file mode 100644 index 0000000..cb59c93 Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/zero.png differ diff --git a/config/www/community/generic-remote-control-card/remotes/yesremote/zero_over.png b/config/www/community/generic-remote-control-card/remotes/yesremote/zero_over.png new file mode 100644 index 0000000..b1a148a Binary files /dev/null and b/config/www/community/generic-remote-control-card/remotes/yesremote/zero_over.png differ diff --git a/config/www/community/irrigation-unlimited-card/irrigation-unlimited-card.js b/config/www/community/irrigation-unlimited-card/irrigation-unlimited-card.js new file mode 100644 index 0000000..108f614 --- /dev/null +++ b/config/www/community/irrigation-unlimited-card/irrigation-unlimited-card.js @@ -0,0 +1,549 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +***************************************************************************** */ +function e(e,t,i,s){var n,o=arguments.length,r=o<3?t:null===s?s=Object.getOwnPropertyDescriptor(t,i):s;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,i,s);else for(var a=e.length-1;a>=0;a--)(n=e[a])&&(r=(o<3?n(r):o>3?n(t,i,r):n(t,i))||r);return o>3&&r&&Object.defineProperty(t,i,r),r +/** + * @license + * Copyright 2019 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */}const t=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol(),s=new Map;class n{constructor(e,t){if(this._$cssResult$=!0,t!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){let e=s.get(this.cssText);return t&&void 0===e&&(s.set(this.cssText,e=new CSSStyleSheet),e.replaceSync(this.cssText)),e}toString(){return this.cssText}}const o=(e,...t)=>{const s=1===e.length?e[0]:t.reduce(((t,i,s)=>t+(e=>{if(!0===e._$cssResult$)return e.cssText;if("number"==typeof e)return e;throw Error("Value passed to 'css' function must be a 'css' function result: "+e+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+e[s+1]),e[0]);return new n(s,i)},r=t?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const i of e.cssRules)t+=i.cssText;return(e=>new n("string"==typeof e?e:e+"",i))(t)})(e):e +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */;var a;const l=window.trustedTypes,d=l?l.emptyScript:"",u=window.reactiveElementPolyfillSupport,c={toAttribute(e,t){switch(t){case Boolean:e=e?d:null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let i=e;switch(t){case Boolean:i=null!==e;break;case Number:i=null===e?null:Number(e);break;case Object:case Array:try{i=JSON.parse(e)}catch(e){i=null}}return i}},h=(e,t)=>t!==e&&(t==t||e==e),v={attribute:!0,type:String,converter:c,reflect:!1,hasChanged:h};class p extends HTMLElement{constructor(){super(),this._$Et=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Ei=null,this.o()}static addInitializer(e){var t;null!==(t=this.l)&&void 0!==t||(this.l=[]),this.l.push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach(((t,i)=>{const s=this._$Eh(i,t);void 0!==s&&(this._$Eu.set(s,i),e.push(s))})),e}static createProperty(e,t=v){if(t.state&&(t.attribute=!1),this.finalize(),this.elementProperties.set(e,t),!t.noAccessor&&!this.prototype.hasOwnProperty(e)){const i="symbol"==typeof e?Symbol():"__"+e,s=this.getPropertyDescriptor(e,i,t);void 0!==s&&Object.defineProperty(this.prototype,e,s)}}static getPropertyDescriptor(e,t,i){return{get(){return this[t]},set(s){const n=this[e];this[t]=s,this.requestUpdate(e,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this.elementProperties.get(e)||v}static finalize(){if(this.hasOwnProperty("finalized"))return!1;this.finalized=!0;const e=Object.getPrototypeOf(this);if(e.finalize(),this.elementProperties=new Map(e.elementProperties),this._$Eu=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const i of t)this.createProperty(i,e[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const i=new Set(e.flat(1/0).reverse());for(const e of i)t.unshift(r(e))}else void 0!==e&&t.push(r(e));return t}static _$Eh(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}o(){var e;this._$Ep=new Promise((e=>this.enableUpdating=e)),this._$AL=new Map,this._$Em(),this.requestUpdate(),null===(e=this.constructor.l)||void 0===e||e.forEach((e=>e(this)))}addController(e){var t,i;(null!==(t=this._$Eg)&&void 0!==t?t:this._$Eg=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(i=e.hostConnected)||void 0===i||i.call(e))}removeController(e){var t;null===(t=this._$Eg)||void 0===t||t.splice(this._$Eg.indexOf(e)>>>0,1)}_$Em(){this.constructor.elementProperties.forEach(((e,t)=>{this.hasOwnProperty(t)&&(this._$Et.set(t,this[t]),delete this[t])}))}createRenderRoot(){var e;const i=null!==(e=this.shadowRoot)&&void 0!==e?e:this.attachShadow(this.constructor.shadowRootOptions);return((e,i)=>{t?e.adoptedStyleSheets=i.map((e=>e instanceof CSSStyleSheet?e:e.styleSheet)):i.forEach((t=>{const i=document.createElement("style"),s=window.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=t.cssText,e.appendChild(i)}))})(i,this.constructor.elementStyles),i}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}))}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}))}attributeChangedCallback(e,t,i){this._$AK(e,i)}_$ES(e,t,i=v){var s,n;const o=this.constructor._$Eh(e,i);if(void 0!==o&&!0===i.reflect){const r=(null!==(n=null===(s=i.converter)||void 0===s?void 0:s.toAttribute)&&void 0!==n?n:c.toAttribute)(t,i.type);this._$Ei=e,null==r?this.removeAttribute(o):this.setAttribute(o,r),this._$Ei=null}}_$AK(e,t){var i,s,n;const o=this.constructor,r=o._$Eu.get(e);if(void 0!==r&&this._$Ei!==r){const e=o.getPropertyOptions(r),a=e.converter,l=null!==(n=null!==(s=null===(i=a)||void 0===i?void 0:i.fromAttribute)&&void 0!==s?s:"function"==typeof a?a:null)&&void 0!==n?n:c.fromAttribute;this._$Ei=r,this[r]=l(t,e.type),this._$Ei=null}}requestUpdate(e,t,i){let s=!0;void 0!==e&&(((i=i||this.constructor.getPropertyOptions(e)).hasChanged||h)(this[e],t)?(this._$AL.has(e)||this._$AL.set(e,t),!0===i.reflect&&this._$Ei!==e&&(void 0===this._$E_&&(this._$E_=new Map),this._$E_.set(e,i))):s=!1),!this.isUpdatePending&&s&&(this._$Ep=this._$EC())}async _$EC(){this.isUpdatePending=!0;try{await this._$Ep}catch(e){Promise.reject(e)}const e=this.scheduleUpdate();return null!=e&&await e,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this._$Et&&(this._$Et.forEach(((e,t)=>this[t]=e)),this._$Et=void 0);let t=!1;const i=this._$AL;try{t=this.shouldUpdate(i),t?(this.willUpdate(i),null===(e=this._$Eg)||void 0===e||e.forEach((e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)})),this.update(i)):this._$EU()}catch(e){throw t=!1,this._$EU(),e}t&&this._$AE(i)}willUpdate(e){}_$AE(e){var t;null===(t=this._$Eg)||void 0===t||t.forEach((e=>{var t;return null===(t=e.hostUpdated)||void 0===t?void 0:t.call(e)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(e)),this.updated(e)}_$EU(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$Ep}shouldUpdate(e){return!0}update(e){void 0!==this._$E_&&(this._$E_.forEach(((e,t)=>this._$ES(t,this[t],e))),this._$E_=void 0),this._$EU()}updated(e){}firstUpdated(e){}} +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +var m;p.finalized=!0,p.elementProperties=new Map,p.elementStyles=[],p.shadowRootOptions={mode:"open"},null==u||u({ReactiveElement:p}),(null!==(a=globalThis.reactiveElementVersions)&&void 0!==a?a:globalThis.reactiveElementVersions=[]).push("1.2.1");const _=globalThis.trustedTypes,g=_?_.createPolicy("lit-html",{createHTML:e=>e}):void 0,$=`lit$${(Math.random()+"").slice(9)}$`,f="?"+$,y=`<${f}>`,b=document,w=(e="")=>b.createComment(e),A=e=>null===e||"object"!=typeof e&&"function"!=typeof e,S=Array.isArray,x=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,E=/-->/g,C=/>/g,k=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,z=/'/g,N=/"/g,T=/^(?:script|style|textarea)$/i,j=(e=>(t,...i)=>({_$litType$:e,strings:t,values:i}))(1),U=Symbol.for("lit-noChange"),P=Symbol.for("lit-nothing"),M=new WeakMap,D=b.createTreeWalker(b,129,null,!1),O=(e,t)=>{const i=e.length-1,s=[];let n,o=2===t?"":"",r=x;for(let t=0;t"===l[0]?(r=null!=n?n:x,d=-1):void 0===l[1]?d=-2:(d=r.lastIndex-l[2].length,a=l[1],r=void 0===l[3]?k:'"'===l[3]?N:z):r===N||r===z?r=k:r===E||r===C?r=x:(r=k,n=void 0);const c=r===k&&e[t+1].startsWith("/>")?" ":"";o+=r===x?i+y:d>=0?(s.push(a),i.slice(0,d)+"$lit$"+i.slice(d)+$+c):i+$+(-2===d?(s.push(void 0),t):c)}const a=o+(e[i]||"")+(2===t?"":"");if(!Array.isArray(e)||!e.hasOwnProperty("raw"))throw Error("invalid template strings array");return[void 0!==g?g.createHTML(a):a,s]};class q{constructor({strings:e,_$litType$:t},i){let s;this.parts=[];let n=0,o=0;const r=e.length-1,a=this.parts,[l,d]=O(e,t);if(this.el=q.createElement(l,i),D.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(s=D.nextNode())&&a.length0){s.textContent=_?_.emptyScript:"";for(let i=0;i{var t;return S(e)||"function"==typeof(null===(t=e)||void 0===t?void 0:t[Symbol.iterator])})(e)?this.A(e):this.$(e)}M(e,t=this._$AB){return this._$AA.parentNode.insertBefore(e,t)}S(e){this._$AH!==e&&(this._$AR(),this._$AH=this.M(e))}$(e){this._$AH!==P&&A(this._$AH)?this._$AA.nextSibling.data=e:this.S(b.createTextNode(e)),this._$AH=e}T(e){var t;const{values:i,_$litType$:s}=e,n="number"==typeof s?this._$AC(e):(void 0===s.el&&(s.el=q.createElement(s.h,this.options)),s);if((null===(t=this._$AH)||void 0===t?void 0:t._$AD)===n)this._$AH.m(i);else{const e=new H(n,this),t=e.p(this.options);e.m(i),this.S(t),this._$AH=e}}_$AC(e){let t=M.get(e.strings);return void 0===t&&M.set(e.strings,t=new q(e)),t}A(e){S(this._$AH)||(this._$AH=[],this._$AR());const t=this._$AH;let i,s=0;for(const n of e)s===t.length?t.push(i=new L(this.M(w()),this.M(w()),this,this.options)):i=t[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=P}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(e,t=this,i,s){const n=this.strings;let o=!1;if(void 0===n)e=R(this,e,t,0),o=!A(e)||e!==this._$AH&&e!==U,o&&(this._$AH=e);else{const s=e;let r,a;for(e=n[0],r=0;r{var s,n;const o=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:t;let r=o._$litPart$;if(void 0===r){const e=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;o._$litPart$=r=new L(t.insertBefore(w(),e),e,void 0,null!=i?i:{})}return r._$AI(e),r})(t,this.renderRoot,this.renderOptions)}connectedCallback(){var e;super.connectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!0)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this._$Dt)||void 0===e||e.setConnected(!1)}render(){return U}}Q.finalized=!0,Q._$litElement$=!0,null===(K=globalThis.litElementHydrateSupport)||void 0===K||K.call(globalThis,{LitElement:Q});const X=globalThis.litElementPolyfillSupport;null==X||X({LitElement:Q}),(null!==(G=globalThis.litElementVersions)&&void 0!==G?G:globalThis.litElementVersions=[]).push("3.1.2");var Y="[^\\s]+";function ee(e,t){for(var i=[],s=0,n=e.length;s-1?n:null}};function ie(e){for(var t=[],i=1;i3?0:(e-e%10!=10?1:0)*e%10]}},ae=(ie({},re),function(e){return+e-1}),le=[null,"[1-9]\\d?"],de=[null,Y],ue=["isPm",Y,function(e,t){var i=e.toLowerCase();return i===t.amPm[0]?0:i===t.amPm[1]?1:null}],ce=["timezoneOffset","[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z?",function(e){var t=(e+"").match(/([+-]|\d\d)/gi);if(t){var i=60*+t[1]+parseInt(t[2],10);return"+"===t[0]?i:-i}return 0}];te("monthNamesShort"),te("monthNames");var he,ve;!function(){try{(new Date).toLocaleDateString("i")}catch(e){return"RangeError"===e.name}}(),function(){try{(new Date).toLocaleString("i")}catch(e){return"RangeError"===e.name}}(),function(){try{(new Date).toLocaleTimeString("i")}catch(e){return"RangeError"===e.name}}(),function(e){e.language="language",e.system="system",e.comma_decimal="comma_decimal",e.decimal_comma="decimal_comma",e.space_comma="space_comma",e.none="none"}(he||(he={})),function(e){e.language="language",e.system="system",e.am_pm="12",e.twenty_four="24"}(ve||(ve={})); +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */ +const pe=e=>t=>"function"==typeof t?((e,t)=>(window.customElements.define(e,t),t))(e,t):((e,t)=>{const{kind:i,elements:s}=t;return{kind:i,elements:s,finisher(t){window.customElements.define(e,t)}}})(e,t) +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */,me=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?{...t,finisher(i){i.createProperty(t.key,e)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:t.key,initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(i){i.createProperty(t.key,e)}};function _e(e){return(t,i)=>void 0!==i?((e,t,i)=>{t.constructor.createProperty(i,e)})(e,t,i):me(e,t) +/** + * @license + * Copyright 2017 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */}function ge(e){return _e({...e,state:!0})} +/** + * @license + * Copyright 2021 Google LLC + * SPDX-License-Identifier: BSD-3-Clause + */var $e;null===($e=window.HTMLSlotElement)||void 0===$e||$e.prototype.assignedElements;let fe=class extends Q{setConfig(e){this._config=e}get _name(){var e;return(null===(e=this._config)||void 0===e?void 0:e.name)||""}get _show_controllers(){var e;return(null===(e=this._config)||void 0===e?void 0:e.show_controllers)||""}get _always_show_zones(){var e;return(null===(e=this._config)||void 0===e?void 0:e.always_show_zones)||!1}get _always_show_sequences(){var e;return(null===(e=this._config)||void 0===e?void 0:e.always_show_sequences)||!1}render(){return this.hass?j` + + + + + + + + + `:P}_valueChanged(e){if(!this._config||!this.hass)return;const t=e.target;if(this[`_${t.configValue}`]!==t.value){if(t.configValue)if(""===t.value){const e=Object.assign({},this._config);delete e[t.configValue],this._config=e}else this._config=Object.assign(Object.assign({},this._config),{[t.configValue]:void 0!==t.checked?t.checked:t.value});!function(e,t,i,s){s=s||{},i=null==i?{}:i;var n=new Event(t,{bubbles:void 0===s.bubbles||s.bubbles,cancelable:Boolean(s.cancelable),composed:void 0===s.composed||s.composed});n.detail=i,e.dispatchEvent(n)}(this,"config-changed",{config:this._config})}}};fe.styles=o` + `,e([_e({attribute:!1})],fe.prototype,"hass",void 0),e([ge()],fe.prototype,"_config",void 0),fe=e([pe("irrigation-unlimited-card-editor")],fe);var ye={version:"Version",invalid_configuration:"Invalid configuration",show_warning:"Show Warning",show_error:"Show Error"},be={common:ye},we={version:"Versjon",invalid_configuration:"Ikke gyldig konfiguration",show_warning:"Vis advarsel"},Ae={common:we};const Se={en:Object.freeze({__proto__:null,common:ye,default:be}),nb:Object.freeze({__proto__:null,common:we,default:Ae})};function xe(e,t="",i=""){const s=(localStorage.getItem("selectedLanguage")||"en").replace(/['"]+/g,"").replace("-","_");let n;try{n=e.split(".").reduce(((e,t)=>e[t]),Se[s])}catch(t){n=e.split(".").reduce(((e,t)=>e[t]),Se.en)}return void 0===n&&(n=e.split(".").reduce(((e,t)=>e[t]),Se.en)),""!==t&&""!==i&&(n=n.replace(t,i)),n}console.info(`%c IRRIGATION-UNLIMITED-CARD \n%c ${xe("common.version")} 2024.1.0 `,"color: orange; font-weight: bold; background: black","color: white; font-weight: bold; background: dimgray"),window.customCards=window.customCards||[],window.customCards.push({type:"irrigation-unlimited-card",name:"Irrigation Unlimited Card",description:"A companion card for the Irrigation Unlimited integration"});let Ee=class extends Q{constructor(){super(...arguments),this._iu_entities=void 0}static async getConfigElement(){return document.createElement("irrigation-unlimited-card-editor")}setConfig(e){if(!e)throw new Error(xe("common.invalid_configuration"));this.config=e}static getStubConfig(){return{}}getCardSize(){return 1}shouldUpdate(e){var t;if(!this.hass)return!1;if(e.has("config"))return!0;if(null==this._iu_entities){this._iu_entities=[];for(const e in this.hass.states)if(e.startsWith("binary_sensor.irrigation_unlimited_")){const i=this.hass.states[e],s=new Date(i.last_updated);this._iu_entities.push({entity_id:e,last_updated:s,name:i.attributes.friendly_name,zone_id:null===(t=i.attributes)||void 0===t?void 0:t.zone_id})}return!0}{let e=!1;for(const t of this._iu_entities){const i=new Date(this.hass.states[t.entity_id].last_updated);i>t.last_updated&&(t.last_updated=i,e=!0)}return e}}render(){return this.hass?j` + +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + ${Array.from(Array(this.hass.states["irrigation_unlimited.coordinator"].attributes.controller_count).keys()).map((e=>this._renderController(e)))} +
    +
    + `:P}_renderController(e){var t;const i=this.hass.states["binary_sensor.irrigation_unlimited_c"+(e+1)+"_m"],s="on"===i.state,n=i.attributes.enabled,o=i.attributes.suspended,r=!(!this.config.show_controllers||this.config.show_controllers&&(null===(t=this.config.show_controllers)||void 0===t?void 0:t.replace(/\s/g,"").split(",").includes(e+1+""))),a=!this.config.always_show_zones,l=!this.config.always_show_sequences;let d,u,c,h="";s?(d=new Date(i.attributes.current_start),u=i.attributes.time_remaining,c=i.attributes.current_name):o?(d=new Date(o),u="",c=""):(d=new Date(i.attributes.next_start),u=i.attributes.next_duration,c=i.attributes.next_name),isNaN(d.getTime())||(h=d.toLocaleTimeString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit",hour12:!1}));const v=["iu-controller iu-object"];r&&v.push("iu-hidden");const p=["iu-controller-row iu-td"];s&&p.push("iu-on"),n&&p.push("iu-enabled"),o&&p.push("iu-suspended");const m=["iu-zones iu-content"];a&&m.push("iu-hidden");const _=["iu-sequences iu-content"];return l&&_.push("iu-hidden"),j` +
    +
    +
    +
    +
    + +
    +
    + ${e+1} + ${i.attributes.friendly_name} +
    +
    +
    + ${c} +
    + ${h} +
    +
    +
    +
    + ${u} +
    +
    +
    +
    ${this._renderMenu(n,!1,!0,!0,null,o)}
    +
    +
    +
    + + + +
    +
    + + + +
    +
    +
    +
    + ${Array.from(Array(i.attributes.zone_count).keys()).map((t=>this._renderZone(e,t)))} +
    +
    +
    + ${i.attributes.sequence_status.map((t=>this._renderSequence(e,t)))} +
    +
    + `}_renderZone(e,t){const i=this.hass.states["binary_sensor.irrigation_unlimited_c"+(e+1)+"_z"+(t+1)],s="on"===i.state,n=i.attributes.enabled,o=i.attributes.suspended,r="blocked"===i.attributes.status;let a,l,d,u,c,h="";s?(a=new Date(i.attributes.current_start),l=i.attributes.time_remaining,d=i.attributes.current_schedule,u=i.attributes.current_name,c=i.attributes.current_adjustment):o?(a=new Date(o),l="",d=-1,u="",c=""):(a=new Date(i.attributes.next_start),l=i.attributes.next_duration,d=i.attributes.next_schedule,u=i.attributes.next_name,c=i.attributes.next_adjustment);const v=0===d;isNaN(a.getTime())||(h=a.toLocaleTimeString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit",hour12:!1}));const p=["iu-zone-row iu-td"];s&&p.push("iu-on"),n&&p.push("iu-enabled"),o&&p.push("iu-suspended"),v&&p.push("iu-manual"),r&&p.push("iu-blocked");let m=i.attributes.timeline;return void 0===m&&(m=[]),j` +
    +
    +
    +
    +
    + +
    +
    + ${t+1} + ${i.attributes.friendly_name} +
    +
    +
    + ${u} +
    + ${h} +
    +
    +
    +
    ${l}
    +
    +
    +
    ${c}
    +
    +
    ${this._renderMenu(n,r,!0,!0,c,o)}
    +
    +
    + ${m.filter((function(e){return"history"===e.status&&e.start!==e.end})).map((e=>this._renderZoneHistory(e)))} +
    +
    +
    + `}_renderZoneHistory(e){const t=new Date(e.start),i=new Date(new Date(e.end).getTime()-t.getTime()).toISOString().slice(12,19),s=t.toLocaleString(void 0,{weekday:"short",month:"numeric",day:"numeric",hour:"numeric",minute:"2-digit",hour12:!1});return j` +
    +
    +
    +
    + +
    +
    ${s}
    +
    ${e.schedule_name}
    +
    ${i}
    +
    ${e.adjustment}
    +
    +
    +
    + `}_renderSequence(e,t){const i="on"===t.status||"paused"===t.status,s=t.enabled,n=t.suspended,o="blocked"===t.status,r=s&&!n&&null===t.schedule.index,a=0!==t.duration;let l,d,u,c="";n?(l=new Date(n),d="",u=""):(l=null!==t.start?new Date(t.start):null,d=new Date(1e3*t.duration).toISOString().substring(12,19),u=t.schedule.name),null===l||isNaN(l.getTime())||(c=l.toLocaleTimeString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit",hour12:!1}));const h=["iu-sequence-row iu-td"];return i&&h.push("iu-on"),s&&h.push("iu-enabled"),n&&h.push("iu-suspended"),r&&h.push("iu-manual"),a&&h.push("iu-running"),o&&h.push("iu-blocked"),j` +
    +
    +
    +
    +
    + +
    +
    + ${t.index+1} + ${t.name} +
    +
    +
    + ${u} +
    + ${c} +
    +
    +
    +
    ${d}
    +
    +
    +
    ${t.adjustment}
    +
    +
    ${this._renderMenu(s,o,!0,!0,t.adjustment,n)}
    +
    +
    + ${t.zones.map((i=>this._renderSequenceZone(e,t.index,i,r)))} +
    +
    +
    + `}_renderSequenceZone(e,t,i,s){const n="on"===i.status,o=i.enabled,r=i.suspended,a="blocked"===i.status,l=0!==i.duration,d=new Date(1e3*i.duration).toISOString().substring(12,19);let u="";if(null!==r){const e=new Date(r);isNaN(e.getTime())||(u=e.toLocaleTimeString(void 0,{weekday:"short",hour:"numeric",minute:"2-digit",hour12:!1}))}const c=["iu-sequence-zone-row iu-td"];return n&&c.push("iu-on"),o&&c.push("iu-enabled"),r&&c.push("iu-suspended"),s&&c.push("iu-manual"),l&&c.push("iu-running"),a&&c.push("iu-blocked"),j` +
    +
    +
    +
    + +
    +
    + ${i.zone_ids.map(((t,i,s)=>this._renderSequenceZoneRef(e,t,i===s.length-1)))} +
    +
    +
    + ${u} +
    +
    +
    +
    ${d}
    +
    +
    +
    ${i.adjustment}
    +
    +
    ${this._renderMenu(o,a,!1,!1,i.adjustment,r)}
    +
    +
    + `}_renderSequenceZoneRef(e,t,i){const s=`binary_sensor.irrigation_unlimited_c${e+1}_`;let n;if(void 0!==this._iu_entities)for(const e of this._iu_entities)if(e.entity_id.startsWith(s)&&e.zone_id===t){e.name&&(n=e.name);break}return n?j`${n}${!1===i?", ":""}`:j` + ${t} + `}_renderMenu(e,t,i,s,n,o){return j` +
    + +
    +
    +
    Enable
    +
    +
    ${this._renderEnabled(e,t)}
    +
    +
    +
    Suspend
    +
    + +
    +
    + + + +
    +
    +
    +
    Manual
    +
    + +
    +
    + + + +
    +
    +
    +
    Cancel
    +
    +
    + + + +
    +
    +
    +
    Adjust
    +
    + +
    +
    + + + +
    +
    +
    +
    + `}_renderEnabled(e,t){return j` + + `}_selectColour(e){const t=["#3498db","#e74c3c","#9b59b6","#f1c40f","#2ecc71","#1abc9c","#34495e","#e67e22","#7f8c8d","#27ae60","#2980b9","#8e44ad"];return t[e%t.length]}_clickNet(e){var t;const i=e.target;if(i.closest(".iu-menu"))return;const s=null===(t=i.closest("#iu-card"))||void 0===t?void 0:t.querySelectorAll(".iu-menu-content:not(.iu-hidden)");null==s||s.forEach((e=>e.classList.add("iu-hidden")))}_toggleCollapse(e){const t=e.target.closest(".iu-collapsible");null==t||t.classList.toggle("iu-hidden"),this.requestUpdate()}_toggleZones(e){var t,i;null===(i=null===(t=e.target.closest(".iu-controller"))||void 0===t?void 0:t.querySelector(".iu-zones"))||void 0===i||i.classList.toggle("iu-hidden")}_toggleSequences(e){var t,i;null===(i=null===(t=e.target.closest(".iu-controller"))||void 0===t?void 0:t.querySelector(".iu-sequences"))||void 0===i||i.classList.toggle("iu-hidden")}_toggleMenu(e){var t,i;null===(i=null===(t=e.target.closest(".iu-menu"))||void 0===t?void 0:t.querySelector(".iu-menu-content"))||void 0===i||i.classList.toggle("iu-hidden")}_get_iu_key(e){var t,i;return null===(i=null===(t=e.target.closest(".iu-object"))||void 0===t?void 0:t.getAttribute("iu-key"))||void 0===i?void 0:i.split(".",4)}_build_entity_id(e){let t="binary_sensor.irrigation_unlimited_c"+e[0]+"_";return t+="0"===e[1]?"m":"z"+e[1],t}_build_data(e){const t=this._get_iu_key(e);if(!t)return;const i={entity_id:this._build_entity_id(t)};return"0"!==t[2]&&(i.sequence_id=Number(t[2])),"0"!==t[3]&&(i.zones=Number(t[3])),i}_serviceEnable(e){const t=this._build_data(e);t&&this.hass.callService("irrigation_unlimited","toggle",t)}_serviceSuspend(e){var t;const i=this._build_data(e);if(!i)return;const s=null===(t=e.target.closest(".iu-menu-item"))||void 0===t?void 0:t.querySelector(".iu-time-input");s.value?i.for=s.value:i.reset=null,this.hass.callService("irrigation_unlimited","suspend",i)}_serviceManualRun(e){var t;const i=this._build_data(e);if(!i)return;const s=null===(t=e.target.closest(".iu-menu-item"))||void 0===t?void 0:t.querySelector(".iu-time-input");s.value&&(i.time=s.value),this.hass.callService("irrigation_unlimited","manual_run",i),this._toggleMenu(e)}_serviceCancel(e){const t=this._build_data(e);t&&(this.hass.callService("irrigation_unlimited","cancel",t),this._toggleMenu(e))}_serviceAdjust(e){var t;const i=this._build_data(e);if(!i)return;const s=(null===(t=e.target.closest(".iu-menu-item"))||void 0===t?void 0:t.querySelector(".iu-adjust-input")).value;switch(s.slice(0,1)){case"%":i.percentage=s.slice(1);break;case"=":i.actual=s.slice(1);break;case"+":i.increase=s.slice(1);break;case"-":i.decrease=s.slice(1);break;case"":i.reset=null;break;default:return}console.log(i),this.hass.callService("irrigation_unlimited","adjust_time",i),this._toggleMenu(e)}static get styles(){return o` + .iu-controller.iu-hidden { + display: none; + } + + .iu-control-panel { + display: flex; + justify-content: flex-start; + align-items: center; + } + + .iu-control-panel-item { + padding: .5em 0 .5em 1em; + } + + .iu-zones.iu-hidden.iu-content { + display: none; + } + + .iu-sequences.iu-hidden.iu-content { + display: none; + } + + .iu-hidden .iu-content { + display: none; + } + + .iu-hidden .iu-expander::before { + content: '\u25B6'; + font-size: large; + } + + .iu-expander::before { + content: '\u25BC'; + font-size: large; + } + + .iu-controller-row.iu-td { + display: flex; + align-items: center; + min-height: 3em; + } + + .iu-zone-row.iu-td { + display: flex; + align-items: center; + min-height: 3em; + } + + .iu-sequence-row.iu-td { + display: flex; + align-items: center; + min-height: 3em; + } + + .iu-sequence-zone-row.iu-td { + display: flex; + align-items: center; + height: 2em; + } + + .iu-td { + display: flex; + align-items: center; + } + + .iu-td1 { + flex: 1.5em; + text-align: center; + cursor: pointer; + } + + .iu-td2 { + flex: 30px; + text-align: center; + } + + .iu-td3 { + flex: 40%; + text-align: left; + } + + .iu-td4 { + flex: 20%; + text-align: center; + } + + .iu-td5 { + flex: 15%; + text-align: center; + } + + .iu-td6 { + flex: 15%; + text-align: center; + } + + .iu-td7 { + flex: 10%; + text-align: center; + } + + .iu-on .iu-duration { + color: var(--state-on-color, #66a61e); + } + + .iu-schedule { + color: var(--secondary-text-color, #727272); + font-size: small; + } + + .iu-manual .iu-schedule { + color: var(--label-badge-red, #DF4C1E); + } + + .iu-suspended .iu-start { + color: var(--label-badge-yellow, #FFFF00); + font-style: italic; + } + + .iu-name { + color: var(--secondary-text-color, #727272); + font-weight: 500; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + ha-icon { + color: var(--state-icon-color, #44739e); + } + + .iu-on .iu-td2 ha-icon { + color: var(--state-icon-active-color, #FDD835); + } + + .iu-menu { + position: relative; + display: inline-block; + } + + .iu-menu-button { + background-color: transparent; + text-align: center; + display: block; + font-size: 16px; + border: none; + cursor: pointer; + } + + .iu-menu-content { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + width: 200px; + padding: 10px 0; + position: absolute; + background-color: var(--card-background-color, white); + right: 0; + box-shadow: 0px 0px 7px 0px rgba(50, 50, 50, 0.75); + border-radius: 5px; + z-index: 1; + } + + .iu-menu-content.iu-hidden { + display: none; + } + + .iu-menu-content .iu-menu-item { + display: flex; + padding: 0px 5px; + line-height: 48px; + } + + .iu-menu .iu-menu-item:hover { + color: var(--primary-color, #b3e5fc); + background-color: var(--secondary-background-color, #e5e5e5); + } + + .iu-menu-item.iu-hidden { + display: none; + } + + .iu-mc1 { + flex: 30%; + text-align: left; + } + + .iu-mc2 { + flex: 40%; + text-align: right; + } + + .iu-mc3 { + flex: 30%; + text-align: center; + } + + .iu-mc3 ha-icon { + display: flex; + } + + .iu-adjust-input:invalid, + .iu-time-input:invalid { + color: var(--label-badge-red, #DF4C1E); + } + `}};e([_e({attribute:!1})],Ee.prototype,"hass",void 0),e([ge()],Ee.prototype,"config",void 0),Ee=e([pe("irrigation-unlimited-card")],Ee);export{Ee as IrrigationUnlimitedCard}; diff --git a/config/www/community/lovelace-mushroom/mushroom.js b/config/www/community/lovelace-mushroom/mushroom.js index 87d0928..9db368e 100644 --- a/config/www/community/lovelace-mushroom/mushroom.js +++ b/config/www/community/lovelace-mushroom/mushroom.js @@ -1,20 +1,20 @@ -var t="https://github.com/piitaya/lovelace-mushroom",e=function(t,i){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i])},e(t,i)};function i(t,i){if("function"!=typeof i&&null!==i)throw new TypeError("Class extends value "+String(i)+" is not a constructor or null");function o(){this.constructor=t}e(t,i),t.prototype=null===i?Object.create(i):(o.prototype=i.prototype,new o)}var o=function(){return o=Object.assign||function(t){for(var e,i=1,o=arguments.length;i=0;l--)(n=t[l])&&(a=(r<3?n(a):r>3?n(e,i,a):n(e,i))||a);return r>3&&a&&Object.defineProperty(e,i,a),a}function r(t){var e="function"==typeof Symbol&&Symbol.iterator,i=e&&t[e],o=0;if(i)return i.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&o>=t.length&&(t=void 0),{value:t&&t[o++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")} +var t="https://github.com/piitaya/lovelace-mushroom",e=function(t,i){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i])},e(t,i)};function i(t,i){if("function"!=typeof i&&null!==i)throw new TypeError("Class extends value "+String(i)+" is not a constructor or null");function o(){this.constructor=t}e(t,i),t.prototype=null===i?Object.create(i):(o.prototype=i.prototype,new o)}var o=function(){return o=Object.assign||function(t){for(var e,i=1,o=arguments.length;i=0;s--)(n=t[s])&&(a=(r<3?n(a):r>3?n(e,i,a):n(e,i))||a);return r>3&&a&&Object.defineProperty(e,i,a),a}function r(t){var e="function"==typeof Symbol&&Symbol.iterator,i=e&&t[e],o=0;if(i)return i.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&o>=t.length&&(t=void 0),{value:t&&t[o++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")} /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */const a=window,l=a.ShadowRoot&&(void 0===a.ShadyCSS||a.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s=Symbol(),c=new WeakMap;let d=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==s)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(l&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=c.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&c.set(e,t))}return t}toString(){return this.cssText}};const u=t=>new d("string"==typeof t?t:t+"",void 0,s),h=(t,...e)=>{const i=1===t.length?t[0]:e.reduce(((e,i,o)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[o+1]),t[0]);return new d(i,t,s)},m=l?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return u(e)})(t):t + */const a=window,s=a.ShadowRoot&&(void 0===a.ShadyCSS||a.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,l=Symbol(),c=new WeakMap;let d=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==l)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(s&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=c.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&c.set(e,t))}return t}toString(){return this.cssText}};const u=t=>new d("string"==typeof t?t:t+"",void 0,l),h=(t,...e)=>{const i=1===t.length?t[0]:e.reduce(((e,i,o)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[o+1]),t[0]);return new d(i,t,l)},m=s?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return u(e)})(t):t /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */;var p;const f=window,g=f.trustedTypes,_=g?g.emptyScript:"",v=f.reactiveElementPolyfillSupport,b={toAttribute(t,e){switch(e){case Boolean:t=t?_:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>e!==t&&(e==e||t==t),x={attribute:!0,type:String,converter:b,reflect:!1,hasChanged:y},w="finalized";let k=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const o=this._$Ep(i,e);void 0!==o&&(this._$Ev.set(o,i),t.push(o))})),t}static createProperty(t,e=x){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,o=this.getPropertyDescriptor(t,i,e);void 0!==o&&Object.defineProperty(this.prototype,t,o)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(o){const n=this[t];this[e]=o,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||x}static finalize(){if(this.hasOwnProperty(w))return!1;this[w]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(m(t))}else void 0!==t&&e.push(m(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{l?t.adoptedStyleSheets=e.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):e.forEach((e=>{const i=document.createElement("style"),o=a.litNonce;void 0!==o&&i.setAttribute("nonce",o),i.textContent=e.cssText,t.appendChild(i)}))})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=x){var o;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(o=i.converter)||void 0===o?void 0:o.toAttribute)?i.converter:b).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const o=this.constructor,n=o._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=o.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:b;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let o=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||y)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):o=!1),!this.isUpdatePending&&o&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; + */;var p;const f=window,g=f.trustedTypes,_=g?g.emptyScript:"",v=f.reactiveElementPolyfillSupport,b={toAttribute(t,e){switch(e){case Boolean:t=t?_:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>e!==t&&(e==e||t==t),x={attribute:!0,type:String,converter:b,reflect:!1,hasChanged:y},w="finalized";let k=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const o=this._$Ep(i,e);void 0!==o&&(this._$Ev.set(o,i),t.push(o))})),t}static createProperty(t,e=x){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,o=this.getPropertyDescriptor(t,i,e);void 0!==o&&Object.defineProperty(this.prototype,t,o)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(o){const n=this[t];this[e]=o,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||x}static finalize(){if(this.hasOwnProperty(w))return!1;this[w]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(m(t))}else void 0!==t&&e.push(m(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{s?t.adoptedStyleSheets=e.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):e.forEach((e=>{const i=document.createElement("style"),o=a.litNonce;void 0!==o&&i.setAttribute("nonce",o),i.textContent=e.cssText,t.appendChild(i)}))})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=x){var o;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(o=i.converter)||void 0===o?void 0:o.toAttribute)?i.converter:b).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const o=this.constructor,n=o._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=o.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:b;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let o=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||y)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):o=!1),!this.isUpdatePending&&o&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -var C;k[w]=!0,k.elementProperties=new Map,k.elementStyles=[],k.shadowRootOptions={mode:"open"},null==v||v({ReactiveElement:k}),(null!==(p=f.reactiveElementVersions)&&void 0!==p?p:f.reactiveElementVersions=[]).push("1.6.2");const $=window,E=$.trustedTypes,A=E?E.createPolicy("lit-html",{createHTML:t=>t}):void 0,S="$lit$",I=`lit$${(Math.random()+"").slice(9)}$`,T="?"+I,O=`<${T}>`,z=document,M=()=>z.createComment(""),j=t=>null===t||"object"!=typeof t&&"function"!=typeof t,D=Array.isArray,L="[ \t\n\f\r]",P=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,N=/-->/g,R=/>/g,F=RegExp(`>|${L}(?:([^\\s"'>=/]+)(${L}*=${L}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),V=/'/g,B=/"/g,U=/^(?:script|style|textarea|title)$/i,H=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),Y=H(1),W=H(2),X=Symbol.for("lit-noChange"),K=Symbol.for("lit-nothing"),G=new WeakMap,q=z.createTreeWalker(z,129,null,!1);function Z(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==A?A.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,o=[];let n,r=2===e?"":"",a=P;for(let e=0;e"===s[0]?(a=null!=n?n:P,c=-1):void 0===s[1]?c=-2:(c=a.lastIndex-s[2].length,l=s[1],a=void 0===s[3]?F:'"'===s[3]?B:V):a===B||a===V?a=F:a===N||a===R?a=P:(a=F,n=void 0);const u=a===F&&t[e+1].startsWith("/>")?" ":"";r+=a===P?i+O:c>=0?(o.push(l),i.slice(0,c)+S+i.slice(c)+I+u):i+I+(-2===c?(o.push(void 0),e):u)}return[Z(t,r+(t[i]||"")+(2===e?"":"")),o]};class Q{constructor({strings:t,_$litType$:e},i){let o;this.parts=[];let n=0,r=0;const a=t.length-1,l=this.parts,[s,c]=J(t,e);if(this.el=Q.createElement(s,i),q.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(o=q.nextNode())&&l.length0){o.textContent=E?E.emptyScript:"";for(let i=0;iD(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==K&&j(this._$AH)?this._$AA.nextSibling.data=t:this.$(z.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:o}=t,n="number"==typeof o?this._$AC(t):(void 0===o.el&&(o.el=Q.createElement(Z(o.h,o.h[0]),this.options)),o);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new et(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Q(t)),e}T(t){D(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,o=0;for(const n of t)o===e.length?e.push(i=new it(this.k(M()),this.k(M()),this,this.options)):i=e[o],i._$AI(n),o++;o2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=K}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,o){const n=this.strings;let r=!1;if(void 0===n)t=tt(this,t,e,0),r=!j(t)||t!==this._$AH&&t!==X,r&&(this._$AH=t);else{const o=t;let a,l;for(t=n[0],a=0;at}):void 0,S="$lit$",I=`lit$${(Math.random()+"").slice(9)}$`,T="?"+I,z=`<${T}>`,O=document,M=()=>O.createComment(""),j=t=>null===t||"object"!=typeof t&&"function"!=typeof t,D=Array.isArray,L="[ \t\n\f\r]",P=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,N=/-->/g,R=/>/g,F=RegExp(`>|${L}(?:([^\\s"'>=/]+)(${L}*=${L}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),V=/'/g,B=/"/g,U=/^(?:script|style|textarea|title)$/i,H=t=>(e,...i)=>({_$litType$:t,strings:e,values:i}),Y=H(1),W=H(2),X=Symbol.for("lit-noChange"),K=Symbol.for("lit-nothing"),G=new WeakMap,q=O.createTreeWalker(O,129,null,!1);function Z(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==A?A.createHTML(e):e}const J=(t,e)=>{const i=t.length-1,o=[];let n,r=2===e?"":"",a=P;for(let e=0;e"===l[0]?(a=null!=n?n:P,c=-1):void 0===l[1]?c=-2:(c=a.lastIndex-l[2].length,s=l[1],a=void 0===l[3]?F:'"'===l[3]?B:V):a===B||a===V?a=F:a===N||a===R?a=P:(a=F,n=void 0);const u=a===F&&t[e+1].startsWith("/>")?" ":"";r+=a===P?i+z:c>=0?(o.push(s),i.slice(0,c)+S+i.slice(c)+I+u):i+I+(-2===c?(o.push(void 0),e):u)}return[Z(t,r+(t[i]||"")+(2===e?"":"")),o]};class Q{constructor({strings:t,_$litType$:e},i){let o;this.parts=[];let n=0,r=0;const a=t.length-1,s=this.parts,[l,c]=J(t,e);if(this.el=Q.createElement(l,i),q.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(o=q.nextNode())&&s.length0){o.textContent=E?E.emptyScript:"";for(let i=0;iD(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==K&&j(this._$AH)?this._$AA.nextSibling.data=t:this.$(O.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:o}=t,n="number"==typeof o?this._$AC(t):(void 0===o.el&&(o.el=Q.createElement(Z(o.h,o.h[0]),this.options)),o);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new et(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=G.get(t.strings);return void 0===e&&G.set(t.strings,e=new Q(t)),e}T(t){D(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,o=0;for(const n of t)o===e.length?e.push(i=new it(this.k(M()),this.k(M()),this,this.options)):i=e[o],i._$AI(n),o++;o2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=K}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,o){const n=this.strings;let r=!1;if(void 0===n)t=tt(this,t,e,0),r=!j(t)||t!==this._$AH&&t!==X,r&&(this._$AH=t);else{const o=t;let a,s;for(t=n[0],a=0;ae=>"function"==typeof e?((t,e)=>(customElements.define(t,e),e))(t,e) * @license * Copyright 2021 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */var wt;null===(wt=window.HTMLSlotElement)||void 0===wt||wt.prototype.assignedElements;var kt,Ct,$t,Et,At,St=Number.isNaN||function(t){return"number"==typeof t&&t!=t};function It(t,e){if(t.length!==e.length)return!1;for(var i=0;inew Intl.DateTimeFormat(t.language,{weekday:"long",month:"long",day:"numeric",timeZone:"server"===t.time_zone?e:void 0})));const Ot=Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{year:"numeric",month:"long",day:"numeric",timeZone:"server"===t.time_zone?e:void 0})));Tt(((t,e)=>{const i=t.date_format===Et.system?void 0:t.language;return t.date_format===Et.language||(t.date_format,Et.system),new Intl.DateTimeFormat(i,{year:"numeric",month:"numeric",day:"numeric",timeZone:"server"===t.time_zone?e:void 0})})),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{day:"numeric",month:"short",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{month:"long",year:"numeric",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{month:"long",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{year:"numeric",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{weekday:"long",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{weekday:"short",timeZone:"server"===t.time_zone?e:void 0})));const zt=Tt((t=>{if(t.time_format===Ct.language||t.time_format===Ct.system){const e=t.time_format===Ct.language?t.language:void 0,i=(new Date).toLocaleString(e);return i.includes("AM")||i.includes("PM")}return t.time_format===Ct.am_pm})),Mt=Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||zt(t)?t.language:"en-u-hc-h23",{hour:"numeric",minute:"2-digit",hour12:zt(t),timeZone:"server"===t.time_zone?e:void 0})));Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||zt(t)?t.language:"en-u-hc-h23",{hour:zt(t)?"numeric":"2-digit",minute:"2-digit",second:"2-digit",hour12:zt(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||zt(t)?t.language:"en-u-hc-h23",{weekday:"long",hour:zt(t)?"numeric":"2-digit",minute:"2-digit",hour12:zt(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en-GB",{hour:"numeric",minute:"2-digit",hour12:!1,timeZone:"server"===t.time_zone?e:void 0})));const jt=(t,e,i)=>Dt(e,i.time_zone).format(t),Dt=Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||zt(t)?t.language:"en-u-hc-h23",{year:"numeric",month:"long",day:"numeric",hour:zt(t)?"numeric":"2-digit",minute:"2-digit",hour12:zt(t),timeZone:"server"===t.time_zone?e:void 0})));Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||zt(t)?t.language:"en-u-hc-h23",{year:"numeric",month:"short",day:"numeric",hour:zt(t)?"numeric":"2-digit",minute:"2-digit",hour12:zt(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||zt(t)?t.language:"en-u-hc-h23",{month:"short",day:"numeric",hour:zt(t)?"numeric":"2-digit",minute:"2-digit",hour12:zt(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||zt(t)?t.language:"en-u-hc-h23",{year:"numeric",month:"long",day:"numeric",hour:zt(t)?"numeric":"2-digit",minute:"2-digit",second:"2-digit",hour12:zt(t),timeZone:"server"===t.time_zone?e:void 0})));const Lt=(t,e,i,o)=>{o=o||{},i=null==i?{}:i;const n=new Event(e,{bubbles:void 0===o.bubbles||o.bubbles,cancelable:Boolean(o.cancelable),composed:void 0===o.composed||o.composed});return n.detail=i,t.dispatchEvent(n),n},Pt=t=>t.substr(0,t.indexOf(".")),Nt="unavailable",Rt="unknown",Ft="off",Vt=[Nt,Rt,Ft];function Bt(t){const e=Pt(t.entity_id),i=t.state;if(["button","input_button","scene"].includes(e))return i!==Nt;if(Vt.includes(i))return!1;switch(e){case"cover":case"valve":return!["closed","closing"].includes(i);case"device_tracker":case"person":return"not_home"!==i;case"media_player":return"standby"!==i;case"vacuum":return!["idle","docked","paused"].includes(i);case"plant":return"problem"===i;default:return!0}}function Ut(t){return t.state!==Nt}function Ht(t){return t.state===Ft}function Yt(t){return t.attributes.entity_picture_local||t.attributes.entity_picture}const Wt=(t,e)=>Xt(t.attributes,e),Xt=(t,e)=>0!=(t.supported_features&e);Tt((t=>new Intl.Collator(t))),Tt((t=>new Intl.Collator(t,{sensitivity:"accent"})));const Kt=t=>Xt(t,4)&&"number"==typeof t.in_progress,Gt=t=>(t=>Kt(t.attributes))(t)||!!t.attributes.in_progress,qt=(t,e=2)=>{let i=""+t;for(let t=1;tfunction(t){const e=Math.floor(t/1e3/3600),i=Math.floor(t/1e3%3600/60),o=Math.floor(t/1e3%3600%60),n=Math.floor(t%1e3);return e>0?`${e}:${qt(i)}:${qt(o)}`:i>0?`${i}:${qt(o)}`:o>0||n>0?`${o}${n>0?`.${qt(n,3)}`:""}`:null}(parseFloat(t)*Zt[e])||"0",Qt=(t,e=2)=>Math.round(t*10**e)/10**e,te=(t,e,i)=>{const o=e?(t=>{switch(t.number_format){case kt.comma_decimal:return["en-US","en"];case kt.decimal_comma:return["de","es","it"];case kt.space_comma:return["fr","sv","cs"];case kt.system:return;default:return t.language}})(e):void 0;if(Number.isNaN=Number.isNaN||function t(e){return"number"==typeof e&&t(e)},(null==e?void 0:e.number_format)!==kt.none&&!Number.isNaN(Number(t))&&Intl)try{return new Intl.NumberFormat(o,ie(t,i)).format(Number(t))}catch(e){return console.error(e),new Intl.NumberFormat(void 0,ie(t,i)).format(Number(t))}return"string"==typeof t?t:`${Qt(t,null==i?void 0:i.maximumFractionDigits).toString()}${"currency"===(null==i?void 0:i.style)?` ${i.currency}`:""}`},ee=(t,e)=>{var i;const o=null==e?void 0:e.display_precision;return null!=o?{maximumFractionDigits:o,minimumFractionDigits:o}:Number.isInteger(Number(null===(i=t.attributes)||void 0===i?void 0:i.step))&&Number.isInteger(Number(t.state))?{maximumFractionDigits:0}:null!=t.attributes.step?{maximumFractionDigits:Math.ceil(Math.log10(1/t.attributes.step))}:void 0},ie=(t,e)=>{const i=Object.assign({maximumFractionDigits:2},e);if("string"!=typeof t)return i;if(!e||void 0===e.minimumFractionDigits&&void 0===e.maximumFractionDigits){const e=t.indexOf(".")>-1?t.split(".")[1].length:0;i.minimumFractionDigits=e,i.maximumFractionDigits=e}return i},oe=t=>{switch(t.language){case"cz":case"de":case"fi":case"fr":case"sk":case"sv":return" ";default:return""}},ne=(t,e,i,o,n,r)=>{const a=n[e.entity_id];return re(t,i,o,a,e.entity_id,e.attributes,void 0!==r?r:e.state)},re=(t,e,i,o,n,r,a)=>{var l;if(a===Rt||a===Nt)return t(`state.default.${a}`);if((t=>!!t.unit_of_measurement||!!t.state_class)(r)){if("duration"===r.device_class&&r.unit_of_measurement&&Zt[r.unit_of_measurement])try{return Jt(a,r.unit_of_measurement)}catch(t){}if("monetary"===r.device_class)try{return te(a,e,Object.assign({style:"currency",currency:r.unit_of_measurement,minimumFractionDigits:2},ee({state:a,attributes:r},o)))}catch(t){}const t=r.unit_of_measurement?"%"===r.unit_of_measurement?oe(e)+"%":` ${r.unit_of_measurement}`:"";return`${te(a,e,ee({state:a,attributes:r},o))}${t}`}const s=Pt(n);if("datetime"===s){const t=new Date(a);return jt(t,e,i)}if(["date","input_datetime","time"].includes(s))try{const t=a.split(" ");if(2===t.length)return jt(new Date(t.join("T")),Object.assign(Object.assign({},e),{time_zone:$t.local}),i);if(1===t.length){if(a.includes("-"))return((t,e,i)=>Ot(e,i.time_zone).format(t))(new Date(`${a}T00:00`),Object.assign(Object.assign({},e),{time_zone:$t.local}),i);if(a.includes(":")){const t=new Date;return((t,e,i)=>Mt(e,i.time_zone).format(t))(new Date(`${t.toISOString().split("T")[0]}T${a}`),Object.assign(Object.assign({},e),{time_zone:$t.local}),i)}}return a}catch(t){return a}if("counter"===s||"number"===s||"input_number"===s)return te(a,e,ee({state:a,attributes:r},o));if(["button","event","input_button","scene","stt","tts"].includes(s)||"sensor"===s&&"timestamp"===r.device_class)try{return jt(new Date(a),e,i)}catch(t){return a}return"update"===s?"on"===a?(t=>Kt(t)||!!t.in_progress)(r)?Xt(r,4)&&"number"==typeof r.in_progress?t("ui.card.update.installing_with_progress",{progress:r.in_progress}):t("ui.card.update.installing"):r.latest_version:r.skipped_version===r.latest_version?null!==(l=r.latest_version)&&void 0!==l?l:t("state.default.unavailable"):t("ui.card.update.up_to_date"):(null==o?void 0:o.translation_key)&&t(`component.${o.platform}.entity.${s}.${o.translation_key}.state.${a}`)||r.device_class&&t(`component.${s}.entity_component.${r.device_class}.state.${a}`)||t(`component.${s}.entity_component._.state.${a}`)||a};class ae extends TypeError{constructor(t,e){let i;const{message:o,...n}=t,{path:r}=t;super(0===r.length?o:"At path: "+r.join(".")+" -- "+o),this.value=void 0,this.key=void 0,this.type=void 0,this.refinement=void 0,this.path=void 0,this.branch=void 0,this.failures=void 0,Object.assign(this,n),this.name=this.constructor.name,this.failures=()=>{var o;return null!=(o=i)?o:i=[t,...e()]}}}function le(t){return"object"==typeof t&&null!=t}function se(t){return"string"==typeof t?JSON.stringify(t):""+t}function ce(t,e,i,o){if(!0===t)return;!1===t?t={}:"string"==typeof t&&(t={message:t});const{path:n,branch:r}=e,{type:a}=i,{refinement:l,message:s="Expected a value of type `"+a+"`"+(l?" with refinement `"+l+"`":"")+", but received: `"+se(o)+"`"}=t;return{value:o,type:a,refinement:l,key:n[n.length-1],path:n,branch:r,...t,message:s}}function*de(t,e,i,o){(function(t){return le(t)&&"function"==typeof t[Symbol.iterator]})(t)||(t=[t]);for(const n of t){const t=ce(n,e,i,o);t&&(yield t)}}function*ue(t,e,i){void 0===i&&(i={});const{path:o=[],branch:n=[t],coerce:r=!1,mask:a=!1}=i,l={path:o,branch:n};if(r&&(t=e.coercer(t,l),a&&"type"!==e.type&&le(e.schema)&&le(t)&&!Array.isArray(t)))for(const i in t)void 0===e.schema[i]&&delete t[i];let s=!0;for(const i of e.validator(t,l))s=!1,yield[i,void 0];for(let[i,c,d]of e.entries(t,l)){const e=ue(c,d,{path:void 0===i?o:[...o,i],branch:void 0===i?n:[...n,c],coerce:r,mask:a});for(const o of e)o[0]?(s=!1,yield[o[0],void 0]):r&&(c=o[1],void 0===i?t=c:t instanceof Map?t.set(i,c):t instanceof Set?t.add(c):le(t)&&(t[i]=c))}if(s)for(const i of e.refiner(t,l))s=!1,yield[i,void 0];s&&(yield[void 0,t])}class he{constructor(t){this.TYPE=void 0,this.type=void 0,this.schema=void 0,this.coercer=void 0,this.validator=void 0,this.refiner=void 0,this.entries=void 0;const{type:e,schema:i,validator:o,refiner:n,coercer:r=(t=>t),entries:a=function*(){}}=t;this.type=e,this.schema=i,this.entries=a,this.coercer=r,this.validator=o?(t,e)=>de(o(t,e),e,this,t):()=>[],this.refiner=n?(t,e)=>de(n(t,e),e,this,t):()=>[]}assert(t){return me(t,this)}create(t){return function(t,e){const i=pe(t,e,{coerce:!0});if(i[0])throw i[0];return i[1]}(t,this)}is(t){return function(t,e){const i=pe(t,e);return!i[0]}(t,this)}mask(t){return function(t,e){const i=pe(t,e,{coerce:!0,mask:!0});if(i[0])throw i[0];return i[1]}(t,this)}validate(t,e){return void 0===e&&(e={}),pe(t,this,e)}}function me(t,e){const i=pe(t,e);if(i[0])throw i[0]}function pe(t,e,i){void 0===i&&(i={});const o=ue(t,e,i),n=function(t){const{done:e,value:i}=t.next();return e?void 0:i}(o);if(n[0]){const t=new ae(n[0],(function*(){for(const t of o)t[0]&&(yield t[0])}));return[t,void 0]}return[void 0,n[1]]}function fe(){for(var t=arguments.length,e=new Array(t),i=0;it.schema)),r=Object.assign({},...n);return o?Ae(r):Ce(r)}function ge(t,e){return new he({type:t,schema:null,validator:e})}function _e(t){return new he({type:"dynamic",schema:null,*entries(e,i){const o=t(e,i);yield*o.entries(e,i)},validator:(e,i)=>t(e,i).validator(e,i),coercer:(e,i)=>t(e,i).coercer(e,i),refiner:(e,i)=>t(e,i).refiner(e,i)})}function ve(){return ge("any",(()=>!0))}function be(t){return new he({type:"array",schema:t,*entries(e){if(t&&Array.isArray(e))for(const[i,o]of e.entries())yield[i,o,t]},coercer:t=>Array.isArray(t)?t.slice():t,validator:t=>Array.isArray(t)||"Expected an array value, but received: "+se(t)})}function ye(){return ge("boolean",(t=>"boolean"==typeof t))}function xe(t){const e={},i=t.map((t=>se(t))).join();for(const i of t)e[i]=i;return new he({type:"enums",schema:e,validator:e=>t.includes(e)||"Expected one of `"+i+"`, but received: "+se(e)})}function we(t){const e=se(t),i=typeof t;return new he({type:"literal",schema:"string"===i||"number"===i||"boolean"===i?t:null,validator:i=>i===t||"Expected the literal `"+e+"`, but received: "+se(i)})}function ke(){return ge("number",(t=>"number"==typeof t&&!isNaN(t)||"Expected a number, but received: "+se(t)))}function Ce(t){const e=t?Object.keys(t):[],i=ge("never",(()=>!1));return new he({type:"object",schema:t||null,*entries(o){if(t&&le(o)){const n=new Set(Object.keys(o));for(const i of e)n.delete(i),yield[i,o[i],t[i]];for(const t of n)yield[t,o[t],i]}},validator:t=>le(t)||"Expected an object, but received: "+se(t),coercer:t=>le(t)?{...t}:t})}function $e(t){return new he({...t,validator:(e,i)=>void 0===e||t.validator(e,i),refiner:(e,i)=>void 0===e||t.refiner(e,i)})}function Ee(){return ge("string",(t=>"string"==typeof t||"Expected a string, but received: "+se(t)))}function Ae(t){const e=Object.keys(t);return new he({type:"type",schema:t,*entries(i){if(le(i))for(const o of e)yield[o,i[o],t[o]]},validator:t=>le(t)||"Expected an object, but received: "+se(t)})}function Se(t){const e=t.map((t=>t.type)).join(" | ");return new he({type:"union",schema:null,coercer(e,i){const o=t.find((t=>{const[i]=t.validate(e,{coerce:!0});return!i}))||ge("unknown",(()=>!0));return o.coercer(e,i)},validator(i,o){const n=[];for(const e of t){const[...t]=ue(i,e,o),[r]=t;if(!r[0])return[];for(const[e]of t)e&&n.push(e)}return["Expected the value to satisfy a union of `"+e+"`, but received: "+se(i),...n]}})}function Ie(t){const e=t.language||"en";return t.translationMetadata.translations[e]&&t.translationMetadata.translations[e].isRTL||!1}const Te=(t,e,i=!1)=>{let o;const n=(...n)=>{const r=i&&!o;clearTimeout(o),o=window.setTimeout((()=>{o=void 0,i||t(...n)}),e),r&&t(...n)};return n.cancel=()=>{clearTimeout(o)},n},Oe=(t,e)=>{if(t===e)return!0;if(t&&e&&"object"==typeof t&&"object"==typeof e){if(t.constructor!==e.constructor)return!1;let i,o;if(Array.isArray(t)){if(o=t.length,o!==e.length)return!1;for(i=o;0!=i--;)if(!Oe(t[i],e[i]))return!1;return!0}if(t instanceof Map&&e instanceof Map){if(t.size!==e.size)return!1;for(i of t.entries())if(!e.has(i[0]))return!1;for(i of t.entries())if(!Oe(i[1],e.get(i[0])))return!1;return!0}if(t instanceof Set&&e instanceof Set){if(t.size!==e.size)return!1;for(i of t.entries())if(!e.has(i[0]))return!1;return!0}if(ArrayBuffer.isView(t)&&ArrayBuffer.isView(e)){if(o=t.length,o!==e.length)return!1;for(i=o;0!=i--;)if(t[i]!==e[i])return!1;return!0}if(t.constructor===RegExp)return t.source===e.source&&t.flags===e.flags;if(t.valueOf!==Object.prototype.valueOf)return t.valueOf()===e.valueOf();if(t.toString!==Object.prototype.toString)return t.toString()===e.toString();const n=Object.keys(t);if(o=n.length,o!==Object.keys(e).length)return!1;for(i=o;0!=i--;)if(!Object.prototype.hasOwnProperty.call(e,n[i]))return!1;for(i=o;0!=i--;){const o=n[i];if(!Oe(t[o],e[o]))return!1}return!0}return t!=t&&e!=e},ze=()=>new Promise((t=>{var e;e=t,requestAnimationFrame((()=>setTimeout(e,0)))})),Me={auto:1,heat_cool:2,heat:3,cool:4,dry:5,fan_only:6,off:7},je=(t,e)=>Me[t]-Me[e];const De=["hs","xy","rgb","rgbw","rgbww"],Le=[...De,"color_temp","brightness","white"],Pe=16384,Ne="returning",Re=8192,Fe=(t,e,i)=>t.subscribeMessage((t=>e(t)),Object.assign({type:"render_template"},i)) + */var wt;null===(wt=window.HTMLSlotElement)||void 0===wt||wt.prototype.assignedElements;var kt,Ct,$t,Et,At,St=Number.isNaN||function(t){return"number"==typeof t&&t!=t};function It(t,e){if(t.length!==e.length)return!1;for(var i=0;inew Intl.DateTimeFormat(t.language,{weekday:"long",month:"long",day:"numeric",timeZone:"server"===t.time_zone?e:void 0})));const zt=Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{year:"numeric",month:"long",day:"numeric",timeZone:"server"===t.time_zone?e:void 0})));Tt(((t,e)=>{const i=t.date_format===Et.system?void 0:t.language;return t.date_format===Et.language||(t.date_format,Et.system),new Intl.DateTimeFormat(i,{year:"numeric",month:"numeric",day:"numeric",timeZone:"server"===t.time_zone?e:void 0})})),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{day:"numeric",month:"short",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{month:"long",year:"numeric",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{month:"long",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{year:"numeric",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{weekday:"long",timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat(t.language,{weekday:"short",timeZone:"server"===t.time_zone?e:void 0})));const Ot=Tt((t=>{if(t.time_format===Ct.language||t.time_format===Ct.system){const e=t.time_format===Ct.language?t.language:void 0,i=(new Date).toLocaleString(e);return i.includes("AM")||i.includes("PM")}return t.time_format===Ct.am_pm})),Mt=Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||Ot(t)?t.language:"en-u-hc-h23",{hour:"numeric",minute:"2-digit",hour12:Ot(t),timeZone:"server"===t.time_zone?e:void 0})));Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||Ot(t)?t.language:"en-u-hc-h23",{hour:Ot(t)?"numeric":"2-digit",minute:"2-digit",second:"2-digit",hour12:Ot(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||Ot(t)?t.language:"en-u-hc-h23",{weekday:"long",hour:Ot(t)?"numeric":"2-digit",minute:"2-digit",hour12:Ot(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en-GB",{hour:"numeric",minute:"2-digit",hour12:!1,timeZone:"server"===t.time_zone?e:void 0})));const jt=(t,e,i)=>Dt(e,i.time_zone).format(t),Dt=Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||Ot(t)?t.language:"en-u-hc-h23",{year:"numeric",month:"long",day:"numeric",hour:Ot(t)?"numeric":"2-digit",minute:"2-digit",hour12:Ot(t),timeZone:"server"===t.time_zone?e:void 0})));Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||Ot(t)?t.language:"en-u-hc-h23",{year:"numeric",month:"short",day:"numeric",hour:Ot(t)?"numeric":"2-digit",minute:"2-digit",hour12:Ot(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||Ot(t)?t.language:"en-u-hc-h23",{month:"short",day:"numeric",hour:Ot(t)?"numeric":"2-digit",minute:"2-digit",hour12:Ot(t),timeZone:"server"===t.time_zone?e:void 0}))),Tt(((t,e)=>new Intl.DateTimeFormat("en"!==t.language||Ot(t)?t.language:"en-u-hc-h23",{year:"numeric",month:"long",day:"numeric",hour:Ot(t)?"numeric":"2-digit",minute:"2-digit",second:"2-digit",hour12:Ot(t),timeZone:"server"===t.time_zone?e:void 0})));const Lt=(t,e,i,o)=>{o=o||{},i=null==i?{}:i;const n=new Event(e,{bubbles:void 0===o.bubbles||o.bubbles,cancelable:Boolean(o.cancelable),composed:void 0===o.composed||o.composed});return n.detail=i,t.dispatchEvent(n),n},Pt=t=>t.substr(0,t.indexOf(".")),Nt="unavailable",Rt="unknown",Ft="off",Vt=[Nt,Rt,Ft];function Bt(t){const e=Pt(t.entity_id),i=t.state;if(["button","input_button","scene"].includes(e))return i!==Nt;if(Vt.includes(i))return!1;switch(e){case"cover":case"valve":return!["closed","closing"].includes(i);case"device_tracker":case"person":return"not_home"!==i;case"media_player":return"standby"!==i;case"vacuum":return!["idle","docked","paused"].includes(i);case"plant":return"problem"===i;default:return!0}}function Ut(t){return t.state!==Nt}function Ht(t){return t.state===Ft}function Yt(t){return t.attributes.entity_picture_local||t.attributes.entity_picture}const Wt=(t,e)=>Xt(t.attributes,e),Xt=(t,e)=>0!=(t.supported_features&e);Tt((t=>new Intl.Collator(t))),Tt((t=>new Intl.Collator(t,{sensitivity:"accent"})));const Kt=t=>Xt(t,4)&&"number"==typeof t.in_progress,Gt=t=>(t=>Kt(t.attributes))(t)||!!t.attributes.in_progress,qt=(t,e=2)=>{let i=""+t;for(let t=1;tfunction(t){const e=Math.floor(t/1e3/3600),i=Math.floor(t/1e3%3600/60),o=Math.floor(t/1e3%3600%60),n=Math.floor(t%1e3);return e>0?`${e}:${qt(i)}:${qt(o)}`:i>0?`${i}:${qt(o)}`:o>0||n>0?`${o}${n>0?`.${qt(n,3)}`:""}`:null}(parseFloat(t)*Zt[e])||"0",Qt=(t,e=2)=>Math.round(t*10**e)/10**e,te=(t,e,i)=>{const o=e?(t=>{switch(t.number_format){case kt.comma_decimal:return["en-US","en"];case kt.decimal_comma:return["de","es","it"];case kt.space_comma:return["fr","sv","cs"];case kt.system:return;default:return t.language}})(e):void 0;if(Number.isNaN=Number.isNaN||function t(e){return"number"==typeof e&&t(e)},(null==e?void 0:e.number_format)!==kt.none&&!Number.isNaN(Number(t))&&Intl)try{return new Intl.NumberFormat(o,ie(t,i)).format(Number(t))}catch(e){return console.error(e),new Intl.NumberFormat(void 0,ie(t,i)).format(Number(t))}return"string"==typeof t?t:`${Qt(t,null==i?void 0:i.maximumFractionDigits).toString()}${"currency"===(null==i?void 0:i.style)?` ${i.currency}`:""}`},ee=(t,e)=>{var i;const o=null==e?void 0:e.display_precision;return null!=o?{maximumFractionDigits:o,minimumFractionDigits:o}:Number.isInteger(Number(null===(i=t.attributes)||void 0===i?void 0:i.step))&&Number.isInteger(Number(t.state))?{maximumFractionDigits:0}:null!=t.attributes.step?{maximumFractionDigits:Math.ceil(Math.log10(1/t.attributes.step))}:void 0},ie=(t,e)=>{const i=Object.assign({maximumFractionDigits:2},e);if("string"!=typeof t)return i;if(!e||void 0===e.minimumFractionDigits&&void 0===e.maximumFractionDigits){const e=t.indexOf(".")>-1?t.split(".")[1].length:0;i.minimumFractionDigits=e,i.maximumFractionDigits=e}return i},oe=t=>{switch(t.language){case"cz":case"de":case"fi":case"fr":case"sk":case"sv":return" ";default:return""}},ne=(t,e,i,o,n,r)=>{const a=n[e.entity_id];return re(t,i,o,a,e.entity_id,e.attributes,void 0!==r?r:e.state)},re=(t,e,i,o,n,r,a)=>{var s;if(a===Rt||a===Nt)return t(`state.default.${a}`);if((t=>!!t.unit_of_measurement||!!t.state_class)(r)){if("duration"===r.device_class&&r.unit_of_measurement&&Zt[r.unit_of_measurement])try{return Jt(a,r.unit_of_measurement)}catch(t){}if("monetary"===r.device_class)try{return te(a,e,Object.assign({style:"currency",currency:r.unit_of_measurement,minimumFractionDigits:2},ee({state:a,attributes:r},o)))}catch(t){}const t=r.unit_of_measurement?"%"===r.unit_of_measurement?oe(e)+"%":` ${r.unit_of_measurement}`:"";return`${te(a,e,ee({state:a,attributes:r},o))}${t}`}const l=Pt(n);if("datetime"===l){const t=new Date(a);return jt(t,e,i)}if(["date","input_datetime","time"].includes(l))try{const t=a.split(" ");if(2===t.length)return jt(new Date(t.join("T")),Object.assign(Object.assign({},e),{time_zone:$t.local}),i);if(1===t.length){if(a.includes("-"))return((t,e,i)=>zt(e,i.time_zone).format(t))(new Date(`${a}T00:00`),Object.assign(Object.assign({},e),{time_zone:$t.local}),i);if(a.includes(":")){const t=new Date;return((t,e,i)=>Mt(e,i.time_zone).format(t))(new Date(`${t.toISOString().split("T")[0]}T${a}`),Object.assign(Object.assign({},e),{time_zone:$t.local}),i)}}return a}catch(t){return a}if("counter"===l||"number"===l||"input_number"===l)return te(a,e,ee({state:a,attributes:r},o));if(["button","event","input_button","scene","stt","tts"].includes(l)||"sensor"===l&&"timestamp"===r.device_class)try{return jt(new Date(a),e,i)}catch(t){return a}return"update"===l?"on"===a?(t=>Kt(t)||!!t.in_progress)(r)?Xt(r,4)&&"number"==typeof r.in_progress?t("ui.card.update.installing_with_progress",{progress:r.in_progress}):t("ui.card.update.installing"):r.latest_version:r.skipped_version===r.latest_version?null!==(s=r.latest_version)&&void 0!==s?s:t("state.default.unavailable"):t("ui.card.update.up_to_date"):(null==o?void 0:o.translation_key)&&t(`component.${o.platform}.entity.${l}.${o.translation_key}.state.${a}`)||r.device_class&&t(`component.${l}.entity_component.${r.device_class}.state.${a}`)||t(`component.${l}.entity_component._.state.${a}`)||a};class ae extends TypeError{constructor(t,e){let i;const{message:o,...n}=t,{path:r}=t;super(0===r.length?o:"At path: "+r.join(".")+" -- "+o),this.value=void 0,this.key=void 0,this.type=void 0,this.refinement=void 0,this.path=void 0,this.branch=void 0,this.failures=void 0,Object.assign(this,n),this.name=this.constructor.name,this.failures=()=>{var o;return null!=(o=i)?o:i=[t,...e()]}}}function se(t){return"object"==typeof t&&null!=t}function le(t){return"string"==typeof t?JSON.stringify(t):""+t}function ce(t,e,i,o){if(!0===t)return;!1===t?t={}:"string"==typeof t&&(t={message:t});const{path:n,branch:r}=e,{type:a}=i,{refinement:s,message:l="Expected a value of type `"+a+"`"+(s?" with refinement `"+s+"`":"")+", but received: `"+le(o)+"`"}=t;return{value:o,type:a,refinement:s,key:n[n.length-1],path:n,branch:r,...t,message:l}}function*de(t,e,i,o){(function(t){return se(t)&&"function"==typeof t[Symbol.iterator]})(t)||(t=[t]);for(const n of t){const t=ce(n,e,i,o);t&&(yield t)}}function*ue(t,e,i){void 0===i&&(i={});const{path:o=[],branch:n=[t],coerce:r=!1,mask:a=!1}=i,s={path:o,branch:n};if(r&&(t=e.coercer(t,s),a&&"type"!==e.type&&se(e.schema)&&se(t)&&!Array.isArray(t)))for(const i in t)void 0===e.schema[i]&&delete t[i];let l=!0;for(const i of e.validator(t,s))l=!1,yield[i,void 0];for(let[i,c,d]of e.entries(t,s)){const e=ue(c,d,{path:void 0===i?o:[...o,i],branch:void 0===i?n:[...n,c],coerce:r,mask:a});for(const o of e)o[0]?(l=!1,yield[o[0],void 0]):r&&(c=o[1],void 0===i?t=c:t instanceof Map?t.set(i,c):t instanceof Set?t.add(c):se(t)&&(t[i]=c))}if(l)for(const i of e.refiner(t,s))l=!1,yield[i,void 0];l&&(yield[void 0,t])}class he{constructor(t){this.TYPE=void 0,this.type=void 0,this.schema=void 0,this.coercer=void 0,this.validator=void 0,this.refiner=void 0,this.entries=void 0;const{type:e,schema:i,validator:o,refiner:n,coercer:r=(t=>t),entries:a=function*(){}}=t;this.type=e,this.schema=i,this.entries=a,this.coercer=r,this.validator=o?(t,e)=>de(o(t,e),e,this,t):()=>[],this.refiner=n?(t,e)=>de(n(t,e),e,this,t):()=>[]}assert(t){return me(t,this)}create(t){return function(t,e){const i=pe(t,e,{coerce:!0});if(i[0])throw i[0];return i[1]}(t,this)}is(t){return function(t,e){const i=pe(t,e);return!i[0]}(t,this)}mask(t){return function(t,e){const i=pe(t,e,{coerce:!0,mask:!0});if(i[0])throw i[0];return i[1]}(t,this)}validate(t,e){return void 0===e&&(e={}),pe(t,this,e)}}function me(t,e){const i=pe(t,e);if(i[0])throw i[0]}function pe(t,e,i){void 0===i&&(i={});const o=ue(t,e,i),n=function(t){const{done:e,value:i}=t.next();return e?void 0:i}(o);if(n[0]){const t=new ae(n[0],(function*(){for(const t of o)t[0]&&(yield t[0])}));return[t,void 0]}return[void 0,n[1]]}function fe(){for(var t=arguments.length,e=new Array(t),i=0;it.schema)),r=Object.assign({},...n);return o?Ae(r):Ce(r)}function ge(t,e){return new he({type:t,schema:null,validator:e})}function _e(t){return new he({type:"dynamic",schema:null,*entries(e,i){const o=t(e,i);yield*o.entries(e,i)},validator:(e,i)=>t(e,i).validator(e,i),coercer:(e,i)=>t(e,i).coercer(e,i),refiner:(e,i)=>t(e,i).refiner(e,i)})}function ve(){return ge("any",(()=>!0))}function be(t){return new he({type:"array",schema:t,*entries(e){if(t&&Array.isArray(e))for(const[i,o]of e.entries())yield[i,o,t]},coercer:t=>Array.isArray(t)?t.slice():t,validator:t=>Array.isArray(t)||"Expected an array value, but received: "+le(t)})}function ye(){return ge("boolean",(t=>"boolean"==typeof t))}function xe(t){const e={},i=t.map((t=>le(t))).join();for(const i of t)e[i]=i;return new he({type:"enums",schema:e,validator:e=>t.includes(e)||"Expected one of `"+i+"`, but received: "+le(e)})}function we(t){const e=le(t),i=typeof t;return new he({type:"literal",schema:"string"===i||"number"===i||"boolean"===i?t:null,validator:i=>i===t||"Expected the literal `"+e+"`, but received: "+le(i)})}function ke(){return ge("number",(t=>"number"==typeof t&&!isNaN(t)||"Expected a number, but received: "+le(t)))}function Ce(t){const e=t?Object.keys(t):[],i=ge("never",(()=>!1));return new he({type:"object",schema:t||null,*entries(o){if(t&&se(o)){const n=new Set(Object.keys(o));for(const i of e)n.delete(i),yield[i,o[i],t[i]];for(const t of n)yield[t,o[t],i]}},validator:t=>se(t)||"Expected an object, but received: "+le(t),coercer:t=>se(t)?{...t}:t})}function $e(t){return new he({...t,validator:(e,i)=>void 0===e||t.validator(e,i),refiner:(e,i)=>void 0===e||t.refiner(e,i)})}function Ee(){return ge("string",(t=>"string"==typeof t||"Expected a string, but received: "+le(t)))}function Ae(t){const e=Object.keys(t);return new he({type:"type",schema:t,*entries(i){if(se(i))for(const o of e)yield[o,i[o],t[o]]},validator:t=>se(t)||"Expected an object, but received: "+le(t)})}function Se(t){const e=t.map((t=>t.type)).join(" | ");return new he({type:"union",schema:null,coercer(e,i){const o=t.find((t=>{const[i]=t.validate(e,{coerce:!0});return!i}))||ge("unknown",(()=>!0));return o.coercer(e,i)},validator(i,o){const n=[];for(const e of t){const[...t]=ue(i,e,o),[r]=t;if(!r[0])return[];for(const[e]of t)e&&n.push(e)}return["Expected the value to satisfy a union of `"+e+"`, but received: "+le(i),...n]}})}function Ie(t){const e=t.language||"en";return t.translationMetadata.translations[e]&&t.translationMetadata.translations[e].isRTL||!1}const Te=(t,e,i=!1)=>{let o;const n=(...n)=>{const r=i&&!o;clearTimeout(o),o=window.setTimeout((()=>{o=void 0,i||t(...n)}),e),r&&t(...n)};return n.cancel=()=>{clearTimeout(o)},n},ze=(t,e)=>{if(t===e)return!0;if(t&&e&&"object"==typeof t&&"object"==typeof e){if(t.constructor!==e.constructor)return!1;let i,o;if(Array.isArray(t)){if(o=t.length,o!==e.length)return!1;for(i=o;0!=i--;)if(!ze(t[i],e[i]))return!1;return!0}if(t instanceof Map&&e instanceof Map){if(t.size!==e.size)return!1;for(i of t.entries())if(!e.has(i[0]))return!1;for(i of t.entries())if(!ze(i[1],e.get(i[0])))return!1;return!0}if(t instanceof Set&&e instanceof Set){if(t.size!==e.size)return!1;for(i of t.entries())if(!e.has(i[0]))return!1;return!0}if(ArrayBuffer.isView(t)&&ArrayBuffer.isView(e)){if(o=t.length,o!==e.length)return!1;for(i=o;0!=i--;)if(t[i]!==e[i])return!1;return!0}if(t.constructor===RegExp)return t.source===e.source&&t.flags===e.flags;if(t.valueOf!==Object.prototype.valueOf)return t.valueOf()===e.valueOf();if(t.toString!==Object.prototype.toString)return t.toString()===e.toString();const n=Object.keys(t);if(o=n.length,o!==Object.keys(e).length)return!1;for(i=o;0!=i--;)if(!Object.prototype.hasOwnProperty.call(e,n[i]))return!1;for(i=o;0!=i--;){const o=n[i];if(!ze(t[o],e[o]))return!1}return!0}return t!=t&&e!=e},Oe=()=>new Promise((t=>{var e;e=t,requestAnimationFrame((()=>setTimeout(e,0)))})),Me={auto:1,heat_cool:2,heat:3,cool:4,dry:5,fan_only:6,off:7},je=(t,e)=>Me[t]-Me[e];const De=["hs","xy","rgb","rgbw","rgbww"],Le=[...De,"color_temp","brightness","white"],Pe=16384,Ne="returning",Re=8192,Fe=(t,e,i)=>t.subscribeMessage((t=>e(t)),Object.assign({type:"render_template"},i)) /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */,Ve=1,Be=3,Ue=4,He=t=>(...e)=>({_$litDirective$:t,values:e});let Ye=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const We=(t,e)=>{const i=(()=>{const t=document.body;if(t.querySelector("action-handler"))return t.querySelector("action-handler");const e=document.createElement("action-handler");return t.appendChild(e),e})();i&&i.bind(t,e)},Xe=He(class extends Ye{update(t,[e]){return We(t.element,e),X}render(t){}}),Ke=async(t,e,i,o)=>{Lt(t,"hass-action",{config:i,action:o})};function Ge(t){return void 0!==t&&"none"!==t.action}const qe=Ce({user:Ee()}),Ze=Se([ye(),Ce({text:$e(Ee()),excemptions:$e(be(qe))})]),Je=Ce({action:we("url"),url_path:Ee(),confirmation:$e(Ze)}),Qe=Ce({action:we("call-service"),service:Ee(),service_data:$e(Ce()),data:$e(Ce()),target:$e(Ce({entity_id:$e(Se([Ee(),be(Ee())])),device_id:$e(Se([Ee(),be(Ee())])),area_id:$e(Se([Ee(),be(Ee())]))})),confirmation:$e(Ze)}),ti=Ce({action:we("navigate"),navigation_path:Ee(),confirmation:$e(Ze)}),ei=Ae({action:we("assist"),pipeline_id:$e(Ee()),start_listening:$e(ye())}),ii=Ae({action:we("fire-dom-event")}),oi=Ce({action:xe(["none","toggle","more-info","call-service","url","navigate","assist"]),confirmation:$e(Ze)}),ni=_e((t=>{if(t&&"object"==typeof t&&"action"in t)switch(t.action){case"call-service":return Qe;case"fire-dom-event":return ii;case"navigate":return ti;case"url":return Je;case"assist":return ei}return oi})),ri=h` - #sortable a:nth-of-type(2n) paper-icon-item { - animation-name: keyframes1; - animation-iteration-count: infinite; - transform-origin: 50% 10%; - animation-delay: -0.75s; - animation-duration: 0.25s; + */,Ve=1,Be=3,Ue=4,He=t=>(...e)=>({_$litDirective$:t,values:e});let Ye=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}};const We=(t,e)=>{const i=(()=>{const t=document.body;if(t.querySelector("action-handler"))return t.querySelector("action-handler");const e=document.createElement("action-handler");return t.appendChild(e),e})();i&&i.bind(t,e)},Xe=He(class extends Ye{update(t,[e]){return We(t.element,e),X}render(t){}}),Ke=async(t,e,i,o)=>{Lt(t,"hass-action",{config:i,action:o})};function Ge(t){return void 0!==t&&"none"!==t.action}const qe=Ce({user:Ee()}),Ze=Se([ye(),Ce({text:$e(Ee()),excemptions:$e(be(qe))})]),Je=Ce({action:we("url"),url_path:Ee(),confirmation:$e(Ze)}),Qe=Ce({action:xe(["call-service","perform-action"]),service:$e(Ee()),perform_action:$e(Ee()),service_data:$e(Ce()),data:$e(Ce()),target:$e(Ce({entity_id:$e(Se([Ee(),be(Ee())])),device_id:$e(Se([Ee(),be(Ee())])),area_id:$e(Se([Ee(),be(Ee())]))})),confirmation:$e(Ze)}),ti=Ce({action:we("navigate"),navigation_path:Ee(),confirmation:$e(Ze)}),ei=Ae({action:we("assist"),pipeline_id:$e(Ee()),start_listening:$e(ye())}),ii=Ae({action:we("fire-dom-event")}),oi=Ce({action:xe(["none","toggle","more-info","call-service","perform-action","url","navigate","assist"]),confirmation:$e(Ze)}),ni=_e((t=>{if(t&&"object"==typeof t&&"action"in t)switch(t.action){case"call-service":case"perform-action":return Qe;case"fire-dom-event":return ii;case"navigate":return ti;case"url":return Je;case"assist":return ei}return oi})),ri=h` + #sortable a:nth-of-type(2n) paper-icon-item { + animation-name: keyframes1; + animation-iteration-count: infinite; + transform-origin: 50% 10%; + animation-delay: -0.75s; + animation-duration: 0.25s; + } + + #sortable a:nth-of-type(2n-1) paper-icon-item { + animation-name: keyframes2; + animation-iteration-count: infinite; + animation-direction: alternate; + transform-origin: 30% 5%; + animation-delay: -0.5s; + animation-duration: 0.33s; + } + + #sortable a { + height: 48px; + display: flex; + } + + #sortable { + outline: none; + display: block !important; + } + + .hidden-panel { + display: flex !important; + } + + .sortable-fallback { + display: none; + } + + .sortable-ghost { + opacity: 0.4; + } + + .sortable-fallback { + opacity: 0; + } + + @keyframes keyframes1 { + 0% { + transform: rotate(-1deg); + animation-timing-function: ease-in; } - #sortable a:nth-of-type(2n-1) paper-icon-item { - animation-name: keyframes2; - animation-iteration-count: infinite; - animation-direction: alternate; - transform-origin: 30% 5%; - animation-delay: -0.5s; - animation-duration: 0.33s; + 50% { + transform: rotate(1.5deg); + animation-timing-function: ease-out; + } + } + + @keyframes keyframes2 { + 0% { + transform: rotate(1deg); + animation-timing-function: ease-in; } - #sortable a { - height: 48px; - display: flex; + 50% { + transform: rotate(-1.5deg); + animation-timing-function: ease-out; } + } - #sortable { - outline: none; - display: block !important; - } + .show-panel, + .hide-panel { + display: none; + position: absolute; + top: 0; + right: 4px; + --mdc-icon-button-size: 40px; + } - .hidden-panel { - display: flex !important; - } + :host([rtl]) .show-panel { + right: initial; + left: 4px; + } - .sortable-fallback { - display: none; - } + .hide-panel { + top: 4px; + right: 8px; + } - .sortable-ghost { - opacity: 0.4; - } + :host([rtl]) .hide-panel { + right: initial; + left: 8px; + } - .sortable-fallback { - opacity: 0; - } + :host([expanded]) .hide-panel { + display: block; + } - @keyframes keyframes1 { - 0% { - transform: rotate(-1deg); - animation-timing-function: ease-in; - } + :host([expanded]) .show-panel { + display: inline-flex; + } - 50% { - transform: rotate(1.5deg); - animation-timing-function: ease-out; - } - } - - @keyframes keyframes2 { - 0% { - transform: rotate(1deg); - animation-timing-function: ease-in; - } - - 50% { - transform: rotate(-1.5deg); - animation-timing-function: ease-out; - } - } - - .show-panel, - .hide-panel { - display: none; - position: absolute; - top: 0; - right: 4px; - --mdc-icon-button-size: 40px; - } - - :host([rtl]) .show-panel { - right: initial; - left: 4px; - } - - .hide-panel { - top: 4px; - right: 8px; - } - - :host([rtl]) .hide-panel { - right: initial; - left: 8px; - } - - :host([expanded]) .hide-panel { - display: block; - } - - :host([expanded]) .show-panel { - display: inline-flex; - } - - paper-icon-item.hidden-panel, - paper-icon-item.hidden-panel span, - paper-icon-item.hidden-panel ha-icon[slot="item-icon"] { - color: var(--secondary-text-color); - cursor: pointer; - } -`;var ai={form:{color_picker:{values:{default:"اللون الإفتراضي"}},info_picker:{values:{default:"المعلومات الافتراضية",name:"الإسم",state:"الحالة","last-changed":"آخر تغيير","last-updated":"آخر تحديث",none:"لا شئ"}},icon_type_picker:{values:{default:"النوع افتراضي",icon:"أيقونة","entity-picture":"صورة الكيان",none:"لا شئ"}},layout_picker:{values:{default:"تخطيط افتراضي",vertical:"تخطيط رأسي",horizontal:"تخطيط أفقي"}},alignment_picker:{values:{default:"المحاذاة الافتراضية",start:"بداية",end:"نهاية",center:"توسيط",justify:"مساواة"}}},card:{generic:{icon_color:"لون الأيقونة",layout:"التخطيط",fill_container:"ملئ الحاوية",primary_info:"المعلومات الأساسية",secondary_info:"المعلومات الفرعية",icon_type:"نوع الأيقونة",content_info:"المحتوى",use_entity_picture:"استخدم صورة الكيان؟",collapsible_controls:"تصغير عناصر التحكم عند الإيقاف",icon_animation:"تحريك الرمز عندما يكون نشطًا؟"},light:{show_brightness_control:"التحكم في السطوع؟",use_light_color:"استخدم لون فاتح",show_color_temp_control:"التحكم في حرارة اللون؟",show_color_control:"التحكم في اللون؟",incompatible_controls:"قد لا يتم عرض بعض عناصر التحكم إذا كان الضوء الخاص بك لا يدعم الميزة."},fan:{show_percentage_control:"التحكم في النسبة المئوية؟",show_oscillate_control:"التحكم في التذبذب؟"},cover:{show_buttons_control:"أزرار التحكم؟",show_position_control:"التحكم في الموقع؟"},template:{primary:"المعلومات الأساسية",secondary:"المعلومات الثانوية",multiline_secondary:"متعدد الأسطر الثانوية؟",entity_extra:"تستخدم في القوالب والإجراءات",content:"المحتوى",badge_icon:"أيقونة الشارة",badge_color:"لون الشارة",picture:"صورة (ستحل محل الأيقونة)"},title:{title:"العنوان",subtitle:"العنوان الفرعي"},chips:{alignment:"محاذاة"},weather:{show_conditions:"الأحوال الجوية؟",show_temperature:"الطقس؟"},update:{show_buttons_control:"أزرار التحكم؟"},vacuum:{commands:"الاوامر"},"media-player":{use_media_info:"استخدم معلومات الوسائط",use_media_artwork:"استخدم صورة الوسائط",show_volume_level:"إظهار مستوى الصوت",media_controls:"التحكم في الوسائط",media_controls_list:{on_off:"تشغيل/إيقاف",shuffle:"خلط",previous:"السابق",play_pause_stop:"تشغيل/إيقاف مؤقت/إيقاف",next:"التالي",repeat:"وضع التكرار"},volume_controls:"التحكم في الصوت",volume_controls_list:{volume_buttons:"أزرار الصوت",volume_set:"مستوى الصوت",volume_mute:"كتم"}},lock:{lock:"مقفل",unlock:"إلغاء قفل",open:"مفتوح"},humidifier:{show_target_humidity_control:"التحكم في الرطوبة؟?"},climate:{show_temperature_control:"التحكم في درجة الحرارة؟",hvac_modes:"أوضاع HVAC"}},chip:{sub_element_editor:{title:"محرر الرقاقة"},conditional:{chip:"رقاقة"},"chip-picker":{chips:"رقاقات",add:"أضف رقاقة",edit:"تعديل",clear:"مسح",select:"اختر الرقاقة",types:{action:"إجراء","alarm-control-panel":"تنبيه",back:"رجوع",conditional:"مشروط",entity:"الكيان",light:"Light",menu:"القائمة",template:"قالب",weather:"الطقس"}}}},li={editor:ai},si={form:{color_picker:{values:{default:"Основен цвят"}},info_picker:{values:{default:"Основна информация",name:"Име",state:"Състояние","last-changed":"Последно Променен","last-updated":"Последно Актуализиран",none:"Липсва"}},icon_type_picker:{values:{default:"Основен тип",icon:"Икона","entity-picture":"Картина на обекта",none:"Липсва"}},layout_picker:{values:{default:"Основно оформление",vertical:"Вертикално оформление",horizontal:"Хоризонтално оформление"}},alignment_picker:{values:{default:"Основно подравняване",start:"Старт",end:"Край",center:"Център",justify:"Подравнен"}}},card:{generic:{icon_color:"Цвят на икона",layout:"Оформление",fill_container:"Изпълване на контейнера",primary_info:"Първостепенна информация",secondary_info:"Второстепенна информация",icon_type:"Тип на икона",content_info:"Съдържание",use_entity_picture:"Използвай снимката на обекта?",collapsible_controls:"Свий контролите при изключен",icon_animation:"Анимирай иконата при активен?"},light:{show_brightness_control:"Контрол на яркостта?",use_light_color:"Използвай цвета на светлината",show_color_temp_control:"Контрол на температурата?",show_color_control:"Контрол на цвета?",incompatible_controls:"Някои опции могат да бъдат скрити при условие че осветителното тяло не поддържа фунцията."},fan:{show_percentage_control:"Процентов контрол?",show_oscillate_control:"Контрол на трептенето?"},cover:{show_buttons_control:"Контролни бутони?",show_position_control:"Контрол на позицията?",show_tilt_position_control:"Контрол на наклона?"},template:{primary:"Първостепенна информация",secondary:"Второстепенна информация",multiline_secondary:"Много-редова второстепенна информация?",entity_extra:"Използван в шаблони и действия",content:"Съдържание",badge_icon:"Икона на значка",badge_color:"Цвят на значка",picture:"Картина (ще замени иконата)"},title:{title:"Заглавие",subtitle:"Подзаглавие"},chips:{alignment:"Подравняване"},weather:{show_conditions:"Условия?",show_temperature:"Температура?"},update:{show_buttons_control:"Контролни бутони?"},vacuum:{commands:"Конади",commands_list:{on_off:"Вкл./Изкл."}},"media-player":{use_media_info:"Използвай информация от медията",use_media_artwork:"Използвай визуалните детайли от медията",show_volume_level:"Покажи контрола за звук",media_controls:"Контрол на Медиата",media_controls_list:{on_off:"Вкл./Изкл.",shuffle:"Разбъркано",previous:"Предишен",play_pause_stop:"Пусни/пауза/стоп",next:"Следващ",repeat:"Повтаряне"},volume_controls:"Контрол на звука",volume_controls_list:{volume_buttons:"Бутони за звук",volume_set:"Ниво на звука",volume_mute:"Заглуши"}},lock:{lock:"Заключен",unlock:"Отключен",open:"Отворен"},humidifier:{show_target_humidity_control:"Контрол на влажността?"},climate:{show_temperature_control:"Контрол на температурата?",hvac_modes:"HVAC Режими"}},chip:{sub_element_editor:{title:"Чип редактор"},conditional:{chip:"Чип"},"chip-picker":{chips:"Чипове",add:"Добави чип",edit:"Редактирай",clear:"Изчисти",select:"Избери чип",types:{action:"Действия","alarm-control-panel":"Аларма",back:"Назад",conditional:"Условни",entity:"Обект",light:"Осветление",menu:"Меню",template:"Шаблон",weather:"Време"}}}},ci={editor:si},di={form:{color_picker:{values:{default:"Color per defecte"}},info_picker:{values:{default:"Informació per defecte",name:"Nom",state:"Estat","last-changed":"Últim Canvi","last-updated":"Última Actualització",none:"Cap"}},icon_type_picker:{values:{default:"Tipus per defecte",icon:"Icona","entity-picture":"Entitat d'imatge",none:"Cap"}},layout_picker:{values:{default:"Distribució per defecte",vertical:"Distribució vertical",horizontal:"Distribució horitzontal"}},alignment_picker:{values:{default:"Alineació per defecte",start:"Inici",end:"Final",center:"Centre",justify:"Justifica"}}},card:{generic:{icon_color:"Color d'icona",layout:"Distribució",fill_container:"Emplena el contenidor",primary_info:"Informació primaria",secondary_info:"Informació secundaria",icon_type:"Tipus d'icona",content_info:"Contingut",use_entity_picture:"Fer servir la imatge de l'entitat?",collapsible_controls:"Amaga els controls en desactivar",icon_animation:"Animar icona en activar?"},light:{show_brightness_control:"Control de brillantor?",use_light_color:"Fes servir el color del llum",show_color_temp_control:"Control de la temperatura del color?",show_color_control:"Control de color?",incompatible_controls:"Alguns controls no es mostraran si l'entitat no suporta eixa funció."},fan:{show_percentage_control:"Control de percentatge?",show_oscillate_control:"Control d'oscil·lació?"},cover:{show_buttons_control:"Botons de control?",show_position_control:"Control de posició?",show_tilt_position_control:"Control d'inclinació?"},template:{primary:"Informació primaria",secondary:"Informació secundaria",multiline_secondary:"Secundaria en varies línies?",entity_extra:"Utilitzats en plantilles i accions",content:"Contingut",badge_icon:"Icona de la insígnia",badge_color:"Color de la insígnia",picture:"Imatge (reemplaçarà la icona)"},title:{title:"Títol",subtitle:"Subtítol",title_tap_action:"Acció en tocar el títol",subtitle_tap_action:"Acció en tocar el subtítol"},chips:{alignment:"Alineació"},weather:{show_conditions:"Condicions?",show_temperature:"Temperatura?"},update:{show_buttons_control:"Botons de control?"},vacuum:{commands:"Comandaments",commands_list:{on_off:"Engegar/Apagar"}},"media-player":{use_media_info:"Empra la informació multimèdia",use_media_artwork:"Fes servir l'art multimèdia",show_volume_level:"Mostra el nivell de volum",media_controls:"Controls multimèdia",media_controls_list:{on_off:"Engegar/Apagar",shuffle:"Mesclar",previous:"Pista anterior",play_pause_stop:"Reproduïr/Pausar/Detindre",next:"Pista següent",repeat:"Mode de repetició"},volume_controls:"Controls de volum",volume_controls_list:{volume_buttons:"Botons de volum",volume_set:"Nivell de volum",volume_mute:"Silenci"}},lock:{lock:"Bloqueja",unlock:"Desbloqueja",open:"Obri"},humidifier:{show_target_humidity_control:"Control d'humitat?"},climate:{show_temperature_control:"Control de temperatura?",hvac_modes:"Modes HVAC"},number:{display_mode:"Mode de visualització",display_mode_list:{default:"Per defecte (lliscant)",slider:"Lliscant",buttons:"Botons"}}},chip:{sub_element_editor:{title:"Editor de xips"},conditional:{chip:"Xip"},"chip-picker":{chips:"Xips",add:"Afegir xip",edit:"Editar",clear:"Buidar",select:"Seleccionar chip",types:{action:"Acció","alarm-control-panel":"Alarma",back:"Tornar",conditional:"Condicional",entity:"Entitat",light:"Llum",menu:"Menú",spacer:"Espai",template:"Plantilla",weather:"Oratge"}}}},ui={not_found:"No s'ha trobat l'entitat"},hi={editor:di,card:ui},mi={form:{color_picker:{values:{default:"Výchozí barva"}},info_picker:{values:{default:"Základní informace",name:"Název",state:"Stav","last-changed":"Poslední změna","last-updated":"Poslední update",none:"Nic"}},icon_type_picker:{values:{default:"Výchozí typ",icon:"Ikona","entity-picture":"Ikona entity",none:"Nic"}},layout_picker:{values:{default:"Výchozí rozložení",vertical:"Svislé rozložení",horizontal:"Vodorovné rozložení"}},alignment_picker:{values:{default:"Výchozí zarovnání",start:"Začátek",end:"Konec",center:"Na střed",justify:"Do bloku"}}},card:{generic:{icon_color:"Barva ikony",layout:"Rozložení",fill_container:"Vyplnit prostor",primary_info:"Základní informace",secondary_info:"Sekundární informace",icon_type:"Typ ikony",content_info:"Obsah",use_entity_picture:"Použít ikonu entity?",collapsible_controls:"Skrýt ovládací prvky pokud je VYP",icon_animation:"Animovaná ikona, pokud je aktivní?"},light:{show_brightness_control:"Ovládání jasu?",use_light_color:"Ikona podle barvy světla?",show_color_temp_control:"Ovládání teploty světla?",show_color_control:"Ovládání barvy světla?",incompatible_controls:"Některé ovládací prvky se nemusí zobrazit, pokud vaše světlo tuto funkci nepodporuje."},fan:{show_percentage_control:"Ovládání v procentech?",show_oscillate_control:"Oscillate control?"},cover:{show_buttons_control:"Zobrazit ovládací tlačítka?",show_position_control:"Zobrazit ovládání polohy?",show_tilt_position_control:"Zobrazit ovládání náklonu?"},template:{primary:"Základní informace",secondary:"Sekundární informace",multiline_secondary:"Víceřádková sekundární informace?",entity_extra:"Použito v šablonách a akcích",content:"Obsah",badge_icon:"Ikona odznaku",badge_color:"Barva odznaku",picture:"Obrázek (nahradí ikonu)"},title:{title:"Titulek",subtitle:"Popis",title_tap_action:"Titulek akce klepnutím",subtitle_tap_action:"Popis akce klepnutím"},chips:{alignment:"Zarovnání"},weather:{show_conditions:"Zobrazit podmínky?",show_temperature:"Zobrazit teplotu?"},update:{show_buttons_control:"Zobrazit ovládací tlačítka?"},vacuum:{commands:"Příkazy",commands_list:{on_off:"Vyp / Zap"}},"media-player":{use_media_info:"Použít informace o médiích",use_media_artwork:"Použít ilustrace médií",show_volume_level:"Zobrazit úroveň hlasitosti",media_controls:"Ovládání médií",media_controls_list:{on_off:"Vyp / Zap",shuffle:"Zamíchat",previous:"Předchozí skladba",play_pause_stop:"hrát/pauza/zastavit",next:"Další skladba",repeat:"Opakovat"},volume_controls:"Ovládání hlasitosti",volume_controls_list:{volume_buttons:"Tlačítka hlasitosti",volume_set:"Úroveň hlasitosti",volume_mute:"Ztlumit"}},lock:{lock:"Zamčeno",unlock:"Odemčeno",open:"Otevřeno"},humidifier:{show_target_humidity_control:"Ovládání vlhkosti?"},climate:{show_temperature_control:"Ovládání teploty?",hvac_modes:"HVAC Mód"},number:{display_mode:"Režim zobrazení",display_mode_list:{default:"Výchozí (posuvník)",slider:"Posuvník",buttons:"Tlačítka"}}},chip:{sub_element_editor:{title:"Editor tlačítek"},conditional:{chip:"Tlačítko"},"chip-picker":{chips:"Tlačítka",add:"Přidat tlačítko",edit:"Editovat",clear:"Vymazat",select:"Vybrat tlačítko",types:{action:"Akce","alarm-control-panel":"Alarm",back:"Zpět",conditional:"Podmínky",entity:"Entita",light:"Světlo",menu:"Menu",spacer:"Mezera",template:"Šablona",weather:"Počasí"}}}},pi={not_found:"Entita nebyla nalezena"},fi={editor:mi,card:pi},gi={form:{color_picker:{values:{default:"Standard farve"}},info_picker:{values:{default:"Standard information",name:"Navn",state:"Status","last-changed":"Sidst ændret","last-updated":"Sidst opdateret",none:"Ingen"}},icon_type_picker:{values:{default:"Standard type",icon:"Ikon","entity-picture":"Enheds billede",none:"Ingen"}},layout_picker:{values:{default:"Standard layout",vertical:"Vertikal layout",horizontal:"Horisontal layout"}},alignment_picker:{values:{default:"Standard justering",start:"Start",end:"Slut",center:"Centrer",justify:"Lige margener"}}},card:{generic:{icon_color:"Ikon farve",layout:"Layout",fill_container:"Fyld container",primary_info:"Primær information",secondary_info:"Sekundær information",icon_type:"Ikon type",content_info:"Indhold",use_entity_picture:"Brug enheds billede?",collapsible_controls:"Skjul kontroller når slukket",icon_animation:"Animér ikon når aktiv?"},light:{show_brightness_control:"Lysstyrkekontrol?",use_light_color:"Brug lysfarve",show_color_temp_control:"Temperatur farvekontrol?",show_color_control:"Farvekontrol?",incompatible_controls:"Nogle kontroller vises muligvis ikke, hvis dit lys ikke understøtter funktionen."},fan:{show_percentage_control:"Procentvis kontrol?",show_oscillate_control:"Oscillerende kontrol?"},cover:{show_buttons_control:"Betjeningsknapper?",show_position_control:"Positionskontrol?"},template:{primary:"Primær information",secondary:"Sekundær information",multiline_secondary:"Multi-linje skundær?",entity_extra:"Anvendes i skabelober og handlinger",content:"Indhold",badge_icon:"Badge ikon",badge_color:"Badge farve",picture:"Billede (erstatter ikonen)"},title:{title:"Titel",subtitle:"Undertitel"},chips:{alignment:"Justering"},weather:{show_conditions:"Forhold?",show_temperature:"Temperatur?"},update:{show_buttons_control:"Betjeningsknapper?"},vacuum:{commands:"Kommandoer"},"media-player":{use_media_info:"Brug medie info",use_media_artwork:"Brug mediebilleder",show_volume_level:"Vis volumen niveau",media_controls:"Medie kontrol",media_controls_list:{on_off:"Tænd/Sluk",shuffle:"Bland",previous:"Forrige nummer",play_pause_stop:"Afspil/Pause/Stop",next:"Næste nummer",repeat:"Gentagelsestilstand"},volume_controls:"Volumen kontrol",volume_controls_list:{volume_buttons:"Volumen knapper",volume_set:"Volumenniveau",volume_mute:"Lydløs"}},lock:{lock:"Lås",unlock:"Lås op",open:"Åben"},humidifier:{show_target_humidity_control:"Luftfugtigheds kontrol?"},climate:{show_temperature_control:"Temperatur kontrol?",hvac_modes:"HVAC-tilstande"}},chip:{sub_element_editor:{title:"Chip-editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Tilføj chip",edit:"Rediger",clear:"Nulstil",select:"Vælg chip",types:{action:"Handling","alarm-control-panel":"Alarm",back:"Tilbage",conditional:"Betinget",entity:"Enhed",light:"Lys",menu:"Menu",template:"Skabelon",weather:"Vejr"}}}},_i={editor:gi},vi={form:{color_picker:{values:{default:"Standardfarbe"}},info_picker:{values:{default:"Standard-Information",name:"Name",state:"Zustand","last-changed":"Letzte Änderung","last-updated":"Letzte Aktualisierung",none:"Keine"}},icon_type_picker:{values:{default:"Standard-Typ",icon:"Icon","entity-picture":"Entitätsbild",none:"Keines"}},layout_picker:{values:{default:"Standard-Layout",vertical:"Vertikales Layout",horizontal:"Horizontales Layout"}},alignment_picker:{values:{default:"Standard",start:"Anfang",end:"Ende",center:"Mitte",justify:"Ausrichten"}}},card:{generic:{icon_color:"Icon-Farbe",layout:"Layout",fill_container:"Container ausfüllen",primary_info:"Primäre Information",secondary_info:"Sekundäre Information",icon_type:"Icon-Typ",content_info:"Inhalt",use_entity_picture:"Entitätsbild verwenden?",collapsible_controls:"Schieberegler einklappen, wenn aus",icon_animation:"Icon animieren, wenn aktiv?"},light:{show_brightness_control:"Helligkeitsregelung?",use_light_color:"Farbsteuerung verwenden",show_color_temp_control:"Farbtemperatursteuerung?",show_color_control:"Farbsteuerung?",incompatible_controls:"Einige Steuerelemente werden möglicherweise nicht angezeigt, wenn Ihr Licht diese Funktion nicht unterstützt."},fan:{show_percentage_control:"Prozentuale Kontrolle?",show_oscillate_control:"Oszillationssteuerung?"},cover:{show_buttons_control:"Schaltflächensteuerung?",show_position_control:"Positionssteuerung?",show_tilt_position_control:"Winkelsteuerung?"},template:{primary:"Primäre Information",secondary:"Sekundäre Information",multiline_secondary:"Mehrzeilig sekundär?",entity_extra:"Wird in Vorlagen und Aktionen verwendet",content:"Inhalt",badge_icon:"Badge-Icon",badge_color:"Badge-Farbe",picture:"Bild (ersetzt das Icon)"},title:{title:"Titel",subtitle:"Untertitel",title_tap_action:"Titel Tipp-Aktion",subtitle_tap_action:"Untertitel Tipp-Aktion"},chips:{alignment:"Ausrichtung"},weather:{show_conditions:"Bedingungen?",show_temperature:"Temperatur?"},update:{show_buttons_control:"Schaltflächensteuerung?"},vacuum:{commands:"Befehle",commands_list:{on_off:"An/Ausschalten"}},"media-player":{use_media_info:"Medieninfos verwenden",use_media_artwork:"Mediengrafik verwenden",show_volume_level:"Lautstärke-Level anzeigen",media_controls:"Mediensteuerung",media_controls_list:{on_off:"Ein/Aus",shuffle:"Zufällige Wiedergabe",previous:"Vorheriger Titel",play_pause_stop:"Play/Pause/Stop",next:"Nächster Titel",repeat:"Wiederholen"},volume_controls:"Lautstärkesteuerung",volume_controls_list:{volume_buttons:"Lautstärke-Buttons",volume_set:"Lautstärke-Level",volume_mute:"Stumm"}},lock:{lock:"Verriegeln",unlock:"Entriegeln",open:"Öffnen"},humidifier:{show_target_humidity_control:"Luftfeuchtigkeitssteuerung?"},climate:{show_temperature_control:"Temperatursteuerung?",hvac_modes:"HVAC-Modi"},number:{display_mode:"Anzeigemodus",display_mode_list:{default:"Standard (Schieberegler)",slider:"Schieberegler",buttons:"Buttons"}}},chip:{sub_element_editor:{title:"Chip Editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Chip hinzufügen",edit:"Editieren",clear:"Löschen",select:"Chip auswählen",types:{action:"Aktion","alarm-control-panel":"Alarm",back:"Zurück",conditional:"Bedingung",entity:"Entität",light:"Licht",menu:"Menü",spacer:"Abstand",template:"Vorlage",weather:"Wetter"}}}},bi={not_found:"Entität nicht gefunden"},yi={editor:vi,card:bi},xi={form:{color_picker:{values:{default:"Προεπιλεγμένο χρώμα"}},info_picker:{values:{default:"Προεπιλεγμένες πληροφορίες",name:"Όνομα",state:"Κατάσταση","last-changed":"Τελευταία αλλαγή","last-updated":"Τελευταία ενημέρωση",none:"Τίποτα"}},layout_picker:{values:{default:"Προεπιλεγμένη διάταξη",vertical:"Κάθετη διάταξη",horizontal:"Οριζόντια διάταξη"}},alignment_picker:{values:{default:"Προεπιλεγμένη στοίχιση",start:"Στοίχιση αριστερά",end:"Στοίχιση δεξιά",center:"Στοίχιση στο κέντρο",justify:"Πλήρης στοίχιση"}}},card:{generic:{icon_color:"Χρώμα εικονιδίου",layout:"Διάταξη",primary_info:"Πρωτεύουσες πληροφορίες",secondary_info:"Δευτερεύουσες πληροφορίες",content_info:"Περιεχόμενο",use_entity_picture:"Χρήση εικόνας οντότητας;",icon_animation:"Κίνηση εικονιδίου όταν είναι ενεργό;"},light:{show_brightness_control:"Έλεγχος φωτεινότητας;",use_light_color:"Χρήση χρώματος φωτος",show_color_temp_control:"Έλεγχος χρώματος θερμοκρασίας;",show_color_control:"Έλεγχος χρώματος;",incompatible_controls:"Ορισμένα στοιχεία ελέγχου ενδέχεται να μην εμφανίζονται εάν το φωτιστικό σας δεν υποστηρίζει τη λειτουργία."},fan:{show_percentage_control:"Έλεγχος ποσοστού;",show_oscillate_control:"Έλεγχος ταλάντωσης;"},cover:{show_buttons_control:"Έλεγχος κουμπιών;",show_position_control:"Έλεγχος θέσης;"},template:{primary:"Πρωτεύουσες πληροφορίες",secondary:"Δευτερεύουσες πληροφορίες",multiline_secondary:"Δευτερεύουσες πολλαπλών γραμμών;",entity_extra:"Χρησιμοποιείται σε πρότυπα και ενέργειες",content:"Περιεχόμενο"},title:{title:"Τίτλος",subtitle:"Υπότιτλος"},chips:{alignment:"Ευθυγράμμιση"},weather:{show_conditions:"Συνθήκες;",show_temperature:"Θερμοκρασία;"},update:{show_buttons_control:"Έλεγχος κουμπιών;"},vacuum:{commands:"Εντολές"},"media-player":{use_media_info:"Χρήση πληροφοριών πολυμέσων",use_media_artwork:"Χρήση έργων τέχνης πολυμέσων",media_controls:"Έλεγχος πολυμέσων",media_controls_list:{on_off:"Ενεργοποίηση/απενεργοποίηση",shuffle:"Τυχαία σειρά",previous:"Προηγούμενο κομμάτι",play_pause_stop:"Αναπαραγωγή/παύση/διακοπή",next:"Επόμενο κομμάτι",repeat:"Λειτουργία επανάληψης"},volume_controls:"Χειριστήρια έντασης ήχου",volume_controls_list:{volume_buttons:"Κουμπιά έντασης ήχου",volume_set:"Επίπεδο έντασης ήχου",volume_mute:"Σίγαση"}}},chip:{sub_element_editor:{title:"Επεξεργαστής Chip"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Προσθήκη chip",edit:"Επεξεργασία",clear:"Καθαρισμός",select:"Επιλογή chip",types:{action:"Ενέργεια","alarm-control-panel":"Συναγερμός",back:"Πίσω",conditional:"Υπό προϋποθέσεις",entity:"Οντότητα",light:"Φως",menu:"Μενού",template:"Πρότυπο",weather:"Καιρός"}}}},wi={editor:xi},ki={form:{color_picker:{values:{default:"Default color"}},info_picker:{values:{default:"Default information",name:"Name",state:"State","last-changed":"Last Changed","last-updated":"Last Updated",none:"None"}},icon_type_picker:{values:{default:"Default type",icon:"Icon","entity-picture":"Entity picture",none:"None"}},layout_picker:{values:{default:"Default layout",vertical:"Vertical layout",horizontal:"Horizontal layout"}},alignment_picker:{values:{default:"Default alignment",start:"Start",end:"End",center:"Center",justify:"Justify"}}},card:{generic:{icon_color:"Icon color",layout:"Layout",fill_container:"Fill container",primary_info:"Primary information",secondary_info:"Secondary information",icon_type:"Icon type",content_info:"Content",use_entity_picture:"Use entity picture?",collapsible_controls:"Collapse controls when off",icon_animation:"Animate icon when active?"},light:{show_brightness_control:"Brightness control?",use_light_color:"Use light color",show_color_temp_control:"Temperature color control?",show_color_control:"Color control?",incompatible_controls:"Some controls may not be displayed if your light does not support the feature."},fan:{show_percentage_control:"Percentage control?",show_oscillate_control:"Oscillate control?"},cover:{show_buttons_control:"Control buttons?",show_position_control:"Position control?",show_tilt_position_control:"Tilt control?"},template:{primary:"Primary information",secondary:"Secondary information",multiline_secondary:"Multiline secondary?",entity_extra:"Used in templates and actions",content:"Content",badge_icon:"Badge icon",badge_color:"Badge color",picture:"Picture (will replace the icon)"},title:{title:"Title",subtitle:"Subtitle",title_tap_action:"Title tap action",subtitle_tap_action:"Subtitle tap action"},chips:{alignment:"Alignment"},weather:{show_conditions:"Conditions?",show_temperature:"Temperature?"},update:{show_buttons_control:"Control buttons?"},vacuum:{commands:"Commands",commands_list:{on_off:"Turn on/off"}},"media-player":{use_media_info:"Use media info",use_media_artwork:"Use media artwork",show_volume_level:"Show volume level",media_controls:"Media controls",media_controls_list:{on_off:"Turn on/off",shuffle:"Shuffle",previous:"Previous track",play_pause_stop:"Play/pause/stop",next:"Next track",repeat:"Repeat mode"},volume_controls:"Volume controls",volume_controls_list:{volume_buttons:"Volume buttons",volume_set:"Volume level",volume_mute:"Mute"}},lock:{lock:"Lock",unlock:"Unlock",open:"Open"},humidifier:{show_target_humidity_control:"Humidity control?"},climate:{show_temperature_control:"Temperature control?",hvac_modes:"HVAC Modes"},number:{display_mode:"Display Mode",display_mode_list:{default:"Default (slider)",slider:"Slider",buttons:"Buttons"}}},chip:{sub_element_editor:{title:"Chip editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Add chip",edit:"Edit",clear:"Clear",select:"Select chip",types:{action:"Action","alarm-control-panel":"Alarm",back:"Back",conditional:"Conditional",entity:"Entity",light:"Light",menu:"Menu",spacer:"Spacer",template:"Template",weather:"Weather"}}}},Ci={not_found:"Entity not found"},$i={editor:ki,card:Ci},Ei={form:{color_picker:{values:{default:"Color predeterminado"}},info_picker:{values:{default:"Información predeterminada",name:"Nombre",state:"Estado","last-changed":"Último cambio","last-updated":"Última actualización",none:"Ninguno"}},icon_type_picker:{values:{default:"Por defecto",icon:"Icono","entity-picture":"Imagen de entidad",none:"Ninguno"}},layout_picker:{values:{default:"Diseño predeterminado",vertical:"Diseño vertical",horizontal:"Diseño horizontal"}},alignment_picker:{values:{default:"Alineación predeterminada",start:"Inicio",end:"Final",center:"Centrado",justify:"Justificado"}}},card:{generic:{icon_color:"Color de icono",layout:"Diseño",fill_container:"Rellenar",primary_info:"Información primaria",secondary_info:"Información secundaria",icon_type:"Icono",content_info:"Contenido",use_entity_picture:"¿Usar imagen de entidad?",collapsible_controls:"Contraer controles cuando está apagado",icon_animation:"¿Icono animado cuando está activo?"},light:{show_brightness_control:"¿Controlar brillo?",use_light_color:"Usar color de la luz",show_color_temp_control:"¿Controlar temperatura del color?",show_color_control:"¿Controlar color?",incompatible_controls:"Es posible que algunos controles no se muestren si la luz no es compatible con esta función."},fan:{show_percentage_control:"¿Controlar porcentaje?",show_oscillate_control:"¿Controlar oscilación?"},cover:{show_buttons_control:"¿Botones de control?",show_position_control:"¿Control de posición?",show_tilt_position_control:"¿Control de inclinación?"},template:{primary:"Información primaria",secondary:"Información secundaria",multiline_secondary:"¿Secundaria multilínea?",entity_extra:"Utilizado en plantillas y acciones.",content:"Contenido",badge_icon:"Icono del distintivo",badge_color:"Color del distintivo",picture:"Imagen (sustituirá al icono)"},title:{title:"Título",subtitle:"Subtítulo",title_tap_action:"Acción al tocar el título",subtitle_tap_action:"Acción al tocar el subtítulo"},chips:{alignment:"Alineación"},weather:{show_conditions:"¿Condiciones?",show_temperature:"¿Temperatura?"},update:{show_buttons_control:"¿Botones de control?"},vacuum:{commands:"Comandos",commands_list:{on_off:"Activar/desactivar"}},"media-player":{use_media_info:"Usar información multimedia",use_media_artwork:"Usar ilustraciones multimedia",show_volume_level:"Mostrar nivel de volumen",media_controls:"Controles multimedia",media_controls_list:{on_off:"Activar/desactivar",shuffle:"Aleatoria",previous:"Pista anterior",play_pause_stop:"Reproducir/pausa/parar",next:"Pista siguiente",repeat:"Modo de repetición"},volume_controls:"Controles de volumen",volume_controls_list:{volume_buttons:"Botones de volumen",volume_set:"Nivel de volumen",volume_mute:"Silenciar"}},lock:{lock:"Bloquear",unlock:"Desbloquear",open:"Abrir"},humidifier:{show_target_humidity_control:"¿Controlar humedad?"},climate:{show_temperature_control:"¿Control de temperatura?",hvac_modes:"Modos de climatización"}},chip:{sub_element_editor:{title:"Editor de chip"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Añadir chip",edit:"Editar",clear:"Limpiar",select:"Seleccionar chip",types:{action:"Acción","alarm-control-panel":"Alarma",back:"Volver",conditional:"Condicional",entity:"Entidad",light:"Luz",menu:"Menú",template:"Plantilla",weather:"Clima"}}}},Ai={editor:Ei},Si={form:{color_picker:{values:{default:"Oletusväri"}},info_picker:{values:{default:"Oletustiedot",name:"Nimi",state:"Tila","last-changed":"Viimeksi muuttunut","last-updated":"Viimeksi päivittynyt",none:"Ei mitään"}},icon_type_picker:{values:{default:"Oletustyyppi",icon:"Kuvake","entity-picture":"Kohteen kuva",none:"Ei mitään"}},layout_picker:{values:{default:"Oletusasettelu",vertical:"Pystysuuntainen",horizontal:"Vaakasuuntainen"}},alignment_picker:{values:{default:"Keskitys",start:"Alku",end:"Loppu",center:"Keskitä",justify:"Sovita"}}},card:{generic:{icon_color:"Ikonin väri",layout:"Asettelu",fill_container:"Täytä alue",primary_info:"Ensisijaiset tiedot",secondary_info:"Toissijaiset tiedot",icon_type:"Kuvakkeen tyyppi",content_info:"Sisältö",use_entity_picture:"Käytä kohteen kuvaa?",collapsible_controls:"Piilota toiminnot off-tilassa",icon_animation:"Animoi kuvake, kun aktiivinen?"},light:{show_brightness_control:"Kirkkauden säätö?",use_light_color:"Käytä valaisimen väriä",show_color_temp_control:"Värilämpötilan säätö?",show_color_control:"Värin säätö?",incompatible_controls:"Jotkin toiminnot eivät näy, jos valaisimesi ei tue niitä."},fan:{show_percentage_control:"Prosentuaalinen säätö?",show_oscillate_control:"Oskillaation säätö?"},cover:{show_buttons_control:"Toimintopainikkeet?",show_position_control:"Sijainnin hallinta?"},template:{primary:"Ensisijaiset tiedot",secondary:"Toissijaiset tiedot",multiline_secondary:"Monirivinen toissijainen tieto?",entity_extra:"Käytetään malleissa ja toiminnoissa",content:"Sisältö",badge_icon:"Merkin kuvake",badge_color:"Merkin väri",picture:"Kuva (korvaa kuvakkeen)"},title:{title:"Otsikko",subtitle:"Tekstitys"},chips:{alignment:"Asettelu"},weather:{show_conditions:"Ehdot?",show_temperature:"Lämpötila?"},update:{show_buttons_control:"Toimintopainikkeet?"},vacuum:{commands:"Komennot"},"media-player":{use_media_info:"Käytä median tietoja",use_media_artwork:"Käytä median kuvituksia",show_volume_level:"Näytä äänenvoimakkuuden hallinta",media_controls:"Toiminnot",media_controls_list:{on_off:"Päälle/pois",shuffle:"Sekoita",previous:"Edellinen kappale",play_pause_stop:"Toista/keskeytä/pysäytä",next:"Seuraava kappale",repeat:"Jatkuva toisto"},volume_controls:"Äänenvoimakkuuden hallinta",volume_controls_list:{volume_buttons:"Äänenvoimakkuuspainikkeet",volume_set:"Äänenvoimakkuus",volume_mute:"Mykistä"}},lock:{lock:"Lukitse",unlock:"Poista lukitus",open:"Avaa"},humidifier:{show_target_humidity_control:"Kosteudenhallinta?"}},chip:{sub_element_editor:{title:"Merkkieditori"},conditional:{chip:"Merkki"},"chip-picker":{chips:"Merkit",add:"Lisää merkki",edit:"Muokkaa",clear:"Tyhjennä",select:"Valitse merkki",types:{action:"Toiminto","alarm-control-panel":"Hälytys",back:"Takaisin",conditional:"Ehdollinen",entity:"Kohde",light:"Valaisin",menu:"Valikko",template:"Malli",weather:"Sää"}}}},Ii={editor:Si},Ti={form:{color_picker:{values:{default:"Couleur par défaut"}},info_picker:{values:{default:"Information par défaut",name:"Nom",state:"État","last-changed":"Dernière modification","last-updated":"Dernière mise à jour",none:"Aucune"}},icon_type_picker:{values:{default:"Type par défaut",icon:"Icône","entity-picture":"Image de l'entité",none:"Aucune"}},layout_picker:{values:{default:"Disposition par défault",vertical:"Disposition verticale",horizontal:"Disposition horizontale"}},alignment_picker:{values:{default:"Alignement par défaut",start:"Début",end:"Fin",center:"Centré",justify:"Justifié"}}},card:{generic:{icon_color:"Couleur de l'icône",layout:"Disposition",fill_container:"Remplir le conteneur",primary_info:"Information principale",secondary_info:"Information secondaire",icon_type:"Type d'icône",content_info:"Contenu",use_entity_picture:"Utiliser l'image de l'entité ?",collapsible_controls:"Reduire les contrôles quand éteint",icon_animation:"Animation de l'icône ?"},light:{show_brightness_control:"Contrôle de luminosité ?",use_light_color:"Utiliser la couleur de la lumière",show_color_temp_control:"Contrôle de la température ?",show_color_control:"Contrôle de la couleur ?",incompatible_controls:"Certains contrôles peuvent ne pas être affichés si votre lumière ne supporte pas la fonctionnalité."},fan:{show_percentage_control:"Contrôle de la vitesse ?",show_oscillate_control:"Contrôle de l'oscillation ?"},cover:{show_buttons_control:"Contrôle avec boutons ?",show_position_control:"Contrôle de la position ?"},template:{primary:"Information principale",secondary:"Information secondaire",multiline_secondary:"Information secondaire sur plusieurs lignes ?",entity_extra:"Utilisée pour les templates et les actions",content:"Contenu",badge_icon:"Icône du badge",badge_color:"Couleur du badge",picture:"Picture (remplacera l'icône)"},title:{title:"Titre",subtitle:"Sous-titre",title_tap_action:"Appui sur le titre",subtitle_tap_action:"Appui sur le sous-titre"},chips:{alignment:"Alignement"},weather:{show_conditons:"Conditions ?",show_temperature:"Température ?"},update:{show_buttons_control:"Contrôle avec boutons ?"},vacuum:{commands:"Commandes",commands_list:{on_off:"Allumer/Éteindre"}},"media-player":{use_media_info:"Utiliser les informations du media",use_media_artwork:"Utiliser l'illustration du media",show_volume_level:"Afficher le niveau de volume",media_controls:"Contrôles du media",media_controls_list:{on_off:"Allumer/Éteindre",shuffle:"Lecture aléatoire",previous:"Précédent",play_pause_stop:"Lecture/pause/stop",next:"Suivant",repeat:"Mode de répétition"},volume_controls:"Contrôles du volume",volume_controls_list:{volume_buttons:"Bouton de volume",volume_set:"Niveau de volume",volume_mute:"Muet"}},lock:{lock:"Verrouiller",unlock:"Déverrouiller",open:"Ouvrir"},humidifier:{show_target_humidity_control:"Contrôle d'humidité ?"},climate:{show_temperature_control:"Contrôle de la température?",hvac_modes:"Modes du thermostat"},number:{display_mode:"Mode d'affichage",display_mode_list:{default:"Par défaut (Curseur)",slider:"Curseur",buttons:"Boutons"}}},chip:{sub_element_editor:{title:'Éditeur de "chip"'},conditional:{chip:"Chip"},"chip-picker":{chips:'"Chips"',add:'Ajouter une "chip"',edit:"Modifier",clear:"Effacer",select:'Sélectionner une "chip"',types:{action:"Action","alarm-control-panel":"Alarme",back:"Retour",conditional:"Conditionnel",entity:"Entité",light:"Lumière",menu:"Menu",spacer:"Espacement",template:"Template",weather:"Météo"}}}},Oi={editor:Ti},zi={form:{color_picker:{values:{default:"צבע ברירת מחדל"}},info_picker:{values:{default:"מידע ברירת מחדל",name:"שם",state:"מצב","last-changed":"שונה לאחרונה","last-updated":"עודכן לאחרונה",none:"ריק"}},layout_picker:{values:{default:"סידור ברירת מחדל",vertical:"סידור מאונך",horizontal:"סידור מאוזן"}},alignment_picker:{values:{default:"יישור ברירת מחדל",start:"התחלה",end:"סוף",center:"אמצע",justify:"מוצדק"}}},card:{generic:{icon_color:"צבע אייקון",layout:"סידור",fill_container:"מלא גבולות",primary_info:"מידע ראשי",secondary_info:"מידע מישני",content_info:"תוכן",use_entity_picture:"השתמש בתמונת ישות",collapsible_controls:"הסתר שליטה כשאר מכובה",icon_animation:"הנפש אייקון"},light:{show_brightness_control:"שליטה בבהירות?",use_light_color:"השתמש בצבע האור",show_color_temp_control:"הצג פקד גוון תאורה?",show_color_control:"הצג פקד צבע",incompatible_controls:"יתכן וחלק מהכפתורים לא יופיעו אם התאורה אינה תומכת בתכונה."},fan:{show_percentage_control:"שליטה באחוז?",show_oscillate_control:"שליטה בהתנדנדות?"},cover:{show_buttons_control:"הצג כפתורי שליטה",show_position_control:"הצג פקדי מיקום"},template:{primary:"מידע ראשי",secondary:"מידע מישני",multiline_secondary:"מידע מישני רב קווי",entity_extra:"משמש בתבניות ופעולות",content:"תוכן"},title:{title:"כותרת",subtitle:"כתובית"},chips:{alignment:"יישור"},weather:{show_conditions:"הצג תנאים?",show_temperature:"הצג טמפרטורה?"},update:{show_buttons_control:"הצג כפתורי שליטה?"},vacuum:{commands:"פקודות",icon_animation:"הנפשת אייקון"},"media-player":{use_media_info:"השתמש במידע מדיה",use_media_artwork:"השתמש באומנות מדיה",show_volume_level:"הצג שליטת ווליום",media_controls:"שליטה במדיה",media_controls_list:{on_off:"הדלק/כבה",shuffle:"ערבב",previous:"רצועה קודמת",play_pause_stop:"נגן/השהה/הפסק",next:"רצועה הבאה",repeat:"חזרה"},volume_controls:"שליטה בווליום",volume_controls_list:{volume_buttons:"כפתורי ווליום",volume_set:"רמת ווליום",volume_mute:"השתק"}},lock:{lock:"נעל",unlock:"בטל נעילה",open:"פתח"},humidifier:{show_target_humidity_control:"הצג פקדי לחות"}},chip:{sub_element_editor:{title:"עורך שבב"},conditional:{chip:"שבב"},"chip-picker":{chips:"שבבים",add:"הוסף שבב",edit:"ערוך",clear:"נקה",select:"בחר שבב",types:{action:"פעולה","alarm-control-panel":"אזעקה",back:"חזור",conditional:"מותנה",entity:"ישות",light:"אור",menu:"תפריט",template:"תבנית",weather:"מזג אוויר"}}}},Mi={editor:zi},ji={form:{color_picker:{values:{default:"Alapértelmezett szín"}},info_picker:{values:{default:"Alepértelmezett információ",name:"Név",state:"Állapot","last-changed":"Utoljára módosítva","last-updated":"Utoljára frissítve",none:"Egyik sem"}},icon_type_picker:{values:{default:"Alapértelmezett típus",icon:"Ikon","entity-picture":"Entitás kép",none:"Egyik sem"}},layout_picker:{values:{default:"Alapértelmezet elrendezés",vertical:"Függőleges elrendezés",horizontal:"Vízszintes elrendezés"}},alignment_picker:{values:{default:"Alapértelmezett rendezés",start:"Kezdete",end:"Vége",center:"Közepe",justify:"Sorkizárt"}}},card:{generic:{icon_color:"Ikon szín",layout:"Elrendezés",fill_container:"Tároló kitöltése",primary_info:"Elsődleges információ",secondary_info:"Másodlagos információ",icon_type:"Ikon típus",content_info:"Tartalom",use_entity_picture:"Entitás kép használata",collapsible_controls:"Vezérlők összezárása kikapcsolt állapotban",icon_animation:"Ikon animálása aktív állapotban"},light:{show_brightness_control:"Fényerő vezérlő",use_light_color:"Fény szín használata",show_color_temp_control:"Színhőmérséklet vezérlő",show_color_control:"Szín vezérlő",incompatible_controls:"Azok a vezérlők nem lesznek megjelenítve, amelyeket a fényforrás nem támogat."},fan:{show_percentage_control:"Százalékos vezérlő",show_oscillate_control:"Oszcilláció vezérlő"},cover:{show_buttons_control:"Vezérlő gombok",show_position_control:"Pozíció vezérlő",show_tilt_position_control:"Dőlésszög szabályzó"},template:{primary:"Elsődleges információ",secondary:"Másodlagos információ",multiline_secondary:"Másodlagost több sorba?",entity_extra:"Műveletek és sablonok használatakor",content:"Tartalom",badge_icon:"Jelvény ikon",badge_color:"Jelvény szín",picture:"Kép (lecseréli az ikont)"},title:{title:"Fejléc",subtitle:"Alcím",title_tap_action:"Fejlécre koppintáskor",subtitle_tap_action:"Alcímre koppintáskor"},chips:{alignment:"Rendezés"},weather:{show_conditions:"Állapotok",show_temperature:"Hőmérséklet"},update:{show_buttons_control:"Vezérlő gombok"},vacuum:{commands:"Utasítások",commands_list:{on_off:"Ki/Bekapcsolás"}},"media-player":{use_media_info:"Média infó használata",use_media_artwork:"Média borító használata",show_volume_level:"Hangerő mutatása",media_controls:"Média vezérlők",media_controls_list:{on_off:"Ki/bekapcsolás",shuffle:"Véletlen lejátszás",previous:"Előző szám",play_pause_stop:"Lejátszás/szünet/állj",next:"Következő szám",repeat:"Ismétlés módja"},volume_controls:"Hangerő vezérlők",volume_controls_list:{volume_buttons:"Hangerő gombok",volume_set:"Hangerő szint",volume_mute:"Némítás"}},lock:{lock:"Zár",unlock:"Nyit",open:"Nyitva"},humidifier:{show_target_humidity_control:"Páratartalom vezérlő"},climate:{show_temperature_control:"Hőmérséklet vezérlő",hvac_modes:"HVAC mód"},number:{display_mode:"Megjelenítési mód",display_mode_list:{default:"Alepértelmezett (csúszka)",slider:"Csúszka",buttons:"Gombok"}}},chip:{sub_element_editor:{title:"Chip szerkesztő"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chip-ek",add:"Chip hozzáadása",edit:"Szerkesztés",clear:"Ürítés",select:"Chip kiválasztása",types:{action:"Művelet","alarm-control-panel":"Riasztó",back:"Vissza",conditional:"Feltételes",entity:"Entitás",light:"Fényforrás",menu:"Menü",spacer:"Térköz",template:"Sablon",weather:"Időjárás"}}}},Di={not_found:"Entitás nem található"},Li={editor:ji,card:Di},Pi={form:{color_picker:{values:{default:"Warna bawaan"}},info_picker:{values:{default:"Informasi bawaan",name:"Nama",state:"Status","last-changed":"Terakhir Diubah","last-updated":"Terakhir Diperbarui",none:"Tidak ada"}},icon_type_picker:{values:{default:"Tipe bawaan",icon:"Ikon","entity-picture":"Gambar entitas",none:"Tidak ada"}},layout_picker:{values:{default:"Tata letak bawaan",vertical:"Tata letak vertikal",horizontal:"Tata letak horizontal"}},alignment_picker:{values:{default:"Perataan bawaan",start:"Awal",end:"Akhir",center:"Tengah",justify:"Rata kanan-kiri"}}},card:{generic:{icon_color:"Warna ikon",layout:"Tata letak",fill_container:"Isi kontainer",primary_info:"Informasi primer",secondary_info:"Informasi sekunder",icon_type:"Tipe ikon",content_info:"Konten",use_entity_picture:"Gunakan gambar entitas?",collapsible_controls:"Sembunyikan kontrol saat mati",icon_animation:"Animasikan ikon saat aktif?"},light:{show_brightness_control:"Kontrol kecerahan?",use_light_color:"Gunakan warna lampu",show_color_temp_control:"Kontrol suhu warna?",show_color_control:"Kontrol warna?",incompatible_controls:"Beberapa kontrol mungkin tidak ditampilkan jika lampu Anda tidak mendukung fitur tersebut."},fan:{show_percentage_control:"Kontrol persentase?",show_oscillate_control:"Kontrol osilasi?"},cover:{show_buttons_control:"Tombol kontrol?",show_position_control:"Kontrol posisi?",show_tilt_position_control:"Kontrol kemiringan?"},template:{primary:"Informasi primer",secondary:"Informasi sekunder",multiline_secondary:"Info sekunder multibaris?",entity_extra:"Digunakan dalam templat dan tindakan",content:"Konten",badge_icon:"Ikon lencana",badge_color:"Warna lencana",picture:"Gambar (akan menggantikan ikon)"},title:{title:"Judul",subtitle:"Subjudul",title_tap_action:"Tindakan ketuk judul",subtitle_tap_action:"Tindakan ketuk subjudul"},chips:{alignment:"Perataan"},weather:{show_conditions:"Kondisi?",show_temperature:"Suhu?"},update:{show_buttons_control:"Tombol kontrol?"},vacuum:{commands:"Perintah",commands_list:{on_off:"Nyalakan/Matikan"}},"media-player":{use_media_info:"Gunakan info media",use_media_artwork:"Gunakan gambar seni media",show_volume_level:"Tampilkan level volume",media_controls:"Kontrol media",media_controls_list:{on_off:"Nyalakan/Matikan",shuffle:"Acak",previous:"Lagu sebelumnya",play_pause_stop:"Putar/jeda/stop",next:"Lagu berikutnya",repeat:"Mode pengulangan"},volume_controls:"Kontrol volume",volume_controls_list:{volume_buttons:"Tombol volume",volume_set:"Level volume",volume_mute:"Bisukan"}},lock:{lock:"Kunci",unlock:"Buka kunci",open:"Buka"},humidifier:{show_target_humidity_control:"Kontrol kelembapan?"},climate:{show_temperature_control:"Kontrol suhu?",hvac_modes:"Mode HVAC"},number:{display_mode:"Mode Tampilan",display_mode_list:{default:"Bawaan (geser)",slider:"Geser",buttons:"Tombol"}}},chip:{sub_element_editor:{title:"Editor cip"},conditional:{chip:"Cip"},"chip-picker":{chips:"Cip",add:"Tambah cip",edit:"Edit",clear:"Hapus",select:"Pilih cip",types:{action:"Tindakan","alarm-control-panel":"Alarm",back:"Kembali",conditional:"Kondisional",entity:"Entitas",light:"Lampu",menu:"Menu",spacer:"Pemisah",template:"Templat",weather:"Cuaca"}}}},Ni={not_found:"Entitas tidak ditemukan"},Ri={editor:Pi,card:Ni},Fi={form:{color_picker:{values:{default:"Colore predefinito"}},info_picker:{values:{default:"Informazione predefinita",name:"Nome",state:"Stato","last-changed":"Ultimo cambiamento","last-updated":"Ultimo aggiornamento",none:"Nessuno"}},icon_type_picker:{values:{default:"Tipo predefinito",icon:"Icona","entity-picture":"Immagine dell'entità",none:"Nessuna"}},layout_picker:{values:{default:"Disposizione predefinita",vertical:"Disposizione verticale",horizontal:"Disposizione orizzontale"}},alignment_picker:{values:{default:"Allineamento predefinito",start:"Inizio",end:"Fine",center:"Centro",justify:"Giustificato"}}},card:{generic:{icon_color:"Colore dell'icona",layout:"Disposizione",fill_container:"Riempi il contenitore",primary_info:"Informazione primaria",secondary_info:"Informazione secondaria",icon_type:"Tipo icona",content_info:"Contenuto",use_entity_picture:"Usa l'immagine dell'entità",collapsible_controls:"Nascondi i controlli quando spento",icon_animation:"Anima l'icona quando attiva"},light:{use_light_color:"Usa il colore della luce",show_brightness_control:"Controllo luminosità",show_color_temp_control:"Controllo temperatura",show_color_control:"Controllo colore",incompatible_controls:"Alcuni controlli potrebbero non essere mostrati se la tua luce non li supporta."},fan:{show_percentage_control:"Controllo potenza",show_oscillate_control:"Controllo oscillazione"},cover:{show_buttons_control:"Pulsanti di controllo",show_position_control:"Controllo percentuale apertura",show_tilt_position_control:"Controllo percentuale inclinazione"},template:{primary:"Informazione primaria",secondary:"Informazione secondaria",multiline_secondary:"Abilita frasi multilinea",entity_extra:"Usato in templates ed azioni",content:"Contenuto",badge_icon:"Icona del badge",badge_color:"Colore del badge",picture:"Immagine (sostituirà l'icona)"},title:{title:"Titolo",subtitle:"Sottotitolo",title_tap_action:"Azione di tap sul titolo",subtitle_tap_action:"Azione di tap sul sottotitolo"},chips:{alignment:"Allineamento"},weather:{show_conditions:"Condizioni",show_temperature:"Temperatura"},update:{show_buttons_control:"Pulsanti di controllo"},vacuum:{commands:"Comandi",commands_list:{on_off:"Accendi/Spegni"}},"media-player":{use_media_info:"Mostra le informazioni della sorgente",use_media_artwork:"Usa la copertina della sorgente",show_volume_level:"Mostra volume",media_controls:"Controlli media",media_controls_list:{on_off:"Accendi/Spegni",shuffle:"Riproduzione casuale",previous:"Traccia precedente",play_pause_stop:"Play/Pausa/Stop",next:"Traccia successiva",repeat:"Ciclo continuo"},volume_controls:"Controlli del Volume",volume_controls_list:{volume_buttons:"Bottoni del volume",volume_set:"Livello del volume",volume_mute:"Silenzia"}},lock:{lock:"Blocca",unlock:"Sblocca",open:"Aperto"},humidifier:{show_target_humidity_control:"Controllo umidità"},climate:{show_temperature_control:"Controllo della temperatura?",hvac_modes:"Modalità del termostato"},number:{display_mode:"Modalità di visualizzazione",display_mode_list:{default:"Predefinito (cursore)",slider:"Cursore",buttons:"Pulsanti"}}},chip:{sub_element_editor:{title:"Editor di chip"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Aggiungi chip",edit:"Modifica",clear:"Rimuovi",select:"Seleziona chip",types:{action:"Azione","alarm-control-panel":"Allarme",back:"Pulsante indietro",conditional:"Condizione",entity:"Entità",light:"Luce",menu:"Menù",spacer:"Distanziere",template:"Modello",weather:"Meteo"}}}},Vi={not_found:"Entità non trovata"},Bi={editor:Fi,card:Vi},Ui={form:{color_picker:{values:{default:"기본 색"}},info_picker:{values:{default:"기본 정보",name:"이름",state:"상태","last-changed":"마지막 변경","last-updated":"마지막 업데이트",none:"없음"}},icon_type_picker:{values:{default:"기본 타입",icon:"아이콘","entity-picture":"엔티티 사진",none:"없음"}},layout_picker:{values:{default:"기본 레이아웃",vertical:"수직 레이아웃",horizontal:"수평 레이아웃"}},alignment_picker:{values:{default:"기본 정렬",start:"시작",end:"끝",center:"중앙",justify:"행 정렬"}}},card:{generic:{icon_color:"아이콘 색",layout:"레이아웃",fill_container:"콘테이너 채우기",primary_info:"기본 정보",secondary_info:"보조 정보",icon_type:"아이콘 타입",content_info:"내용 정보",use_entity_picture:"엔티티 사진 사용",collapsible_controls:"꺼져있을 때 컨트롤 접기",icon_animation:"활성화 시 아이콘 애니메이션 사용"},light:{show_brightness_control:"밝기 컨트롤 표시",use_light_color:"조명 색 사용",show_color_temp_control:"색 온도 컨트롤 표시",show_color_control:"색 컨트롤 표시",incompatible_controls:"조명이 기능을 지원하지 않는 경우 일부 컨트롤이 표시되지 않을 수 있습니다."},fan:{show_percentage_control:"퍼센트 컨트롤",show_oscillate_control:"오실레이트 컨트롤"},cover:{show_buttons_control:"컨트롤 버튼 표시",show_position_control:"위치 컨트롤 표시",show_tilt_position_control:"기울기 컨트롤 표시"},template:{primary:"기본 정보",secondary:"보조 정보",multiline_secondary:"Multiline secondary?",entity_extra:"템플릿 및 작업에 사용",content:"내용",badge_icon:"뱃지 아이콘",badge_color:"뱃지 색",picture:"그림 (아이콘 대체)"},title:{title:"제목",subtitle:"부제목",title_tap_action:"제목 탭 액션",subtitle_tap_action:"부제목 탭 액션"},chips:{alignment:"정렬"},weather:{show_conditions:"조건 표시",show_temperature:"온도 표시"},update:{show_buttons_control:"컨트롤 버튼 표시"},vacuum:{commands:"명령어",commands_list:{on_off:"켜기/끄기"}},"media-player":{use_media_info:"미디어 정보 사용",use_media_artwork:"미디어 아트워크 사용",show_volume_level:"볼륨 레벨 표시",media_controls:"미디어 컨트롤",media_controls_list:{on_off:"켜기/끄기",shuffle:"섞기",previous:"이전 트랙",play_pause_stop:"재생/일시 정지/정지",next:"다음 트랙",repeat:"반복 모드"},volume_controls:"볼륨 컨트롤",volume_controls_list:{volume_buttons:"볼륨 버튼",volume_set:"볼륨 레벨",volume_mute:"음소거"}},lock:{lock:"잠금",unlock:"잠금 해제",open:"열기"},humidifier:{show_target_humidity_control:"습도 조절 표시"},climate:{show_temperature_control:"온도 조절 표시",hvac_modes:"HVAC 모드"}},chip:{sub_element_editor:{title:"칩 에디터"},conditional:{chip:"칩"},"chip-picker":{chips:"칩",add:"칩 추가",edit:"수정",clear:"클리어",select:"칩 선택",types:{action:"액션","alarm-control-panel":"알람",back:"이전",conditional:"Conditional",entity:"엔티티",light:"조명",menu:"메뉴",template:"템플릿",weather:"날씨"}}}},Hi={editor:Ui},Yi={form:{color_picker:{values:{default:"Standard farge"}},info_picker:{values:{default:"Standard informasjon",name:"Navn",state:"Tilstand","last-changed":"Sist endret","last-updated":"Sist oppdatert",none:"Ingen"}},layout_picker:{values:{default:"Standardoppsett",vertical:"Vertikalt oppsett",horizontal:"Horisontalt oppsett"}},alignment_picker:{values:{default:"Standard justering",start:"Start",end:"Slutt",center:"Senter",justify:"Bekreft"}}},card:{generic:{icon_color:"Ikon farge",layout:"Oppsett",primary_info:"Primærinformasjon",secondary_info:"Sekundærinformasjon",content_info:"Innhold",use_entity_picture:"Bruk enhetsbilde?",icon_animation:"Animer ikon når aktivt?"},light:{show_brightness_control:"Lysstyrkekontroll?",use_light_color:"Bruk lys farge",show_color_temp_control:"Temperatur fargekontroll?",show_color_control:"Fargekontroll?",incompatible_controls:"Noen kontroller vises kanskje ikke hvis lyset ditt ikke støtter denne funksjonen."},fan:{show_percentage_control:"Prosentvis kontroll?",show_oscillate_control:"Oscillerende kontroll?"},cover:{show_buttons_control:"Kontollere med knapper?",show_position_control:"Posisjonskontroll?"},template:{primary:"Primærinformasjon",secondary:"Sekundærinformasjon",multiline_secondary:"Multiline sekundær?",entity_extra:"Brukes i maler og handlinger",content:"Inhold"},title:{title:"Tittel",subtitle:"Undertekst"},chips:{alignment:"Justering"},weather:{show_conditions:"Forhold?",show_temperature:"Temperatur?"},vacuum:{commands:"Kommandoer"}},chip:{sub_element_editor:{title:"Chip redaktør"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Legg til chip",edit:"Endre",clear:"Klare",select:"Velg chip",types:{action:"Handling","alarm-control-panel":"Alarm",back:"Tilbake",conditional:"Betinget",entity:"Entitet",light:"Lys",menu:"Meny",template:"Mal",weather:"Vær"}}}},Wi={editor:Yi},Xi={form:{color_picker:{values:{default:"Standaard kleur"}},info_picker:{values:{default:"Standaard informatie",name:"Naam",state:"Staat","last-changed":"Laatst gewijzigd","last-updated":"Laatst bijgewerkt",none:"Geen"}},icon_type_picker:{values:{default:"Standaard icoon type",icon:"Icoon","entity-picture":"Entiteit afbeelding",none:"Geen"}},layout_picker:{values:{default:"Standaard lay-out",vertical:"Verticale lay-out",horizontal:"Horizontale lay-out"}},alignment_picker:{values:{default:"Standaard uitlijning",start:"Begin",end:"Einde",center:"Midden",justify:"Uitlijnen "}}},card:{generic:{icon_color:"Icoon kleur",layout:"Lay-out",fill_container:"Vul container",primary_info:"Primaire informatie",secondary_info:"Secundaire informatie",icon_type:"Icoon type",content_info:"Inhoud",use_entity_picture:"Gebruik entiteit afbeelding",collapsible_controls:"Bedieningselementen verbergen wanneer uitgeschakeld",icon_animation:"Pictogram animeren indien actief"},light:{show_brightness_control:"Bediening helderheid",use_light_color:"Gebruik licht kleur",show_color_temp_control:"Bediening kleurtemperatuur",show_color_control:"Bediening kleur",incompatible_controls:"Sommige bedieningselementen worden mogelijk niet weergegeven als uw lamp deze functie niet ondersteunt."},fan:{show_percentage_control:"Bediening middels percentage",show_oscillate_control:"Bediening oscillatie"},cover:{show_buttons_control:"Toon knoppen",show_position_control:"Toon positie bediening",show_tilt_position_control:"Toon tilt control"},template:{primary:"Primaire informatie",secondary:"Secundaire informatie",multiline_secondary:"Secundaire informatie op meerdere lijnen weergeven",entity_extra:"Gebruikt in sjablonen en acties",content:"Inhoud",badge_icon:"Badge icoon",badge_color:"Badge kleur",picture:"Afbeelding (zal het icoon vervangen)"},title:{title:"Titel",subtitle:"Ondertitel",title_tap_action:"Titel tik actie",subtitle_tap_action:"Ondertitel tik actie"},chips:{alignment:"Uitlijning"},weather:{show_conditions:"Weerbeeld",show_temperature:"Temperatuur"},update:{show_buttons_control:"Bedieningsknoppen"},vacuum:{commands:"Commando's",commands_list:{on_off:"Zet aan/uit"}},"media-player":{use_media_info:"Gebruik media informatie",use_media_artwork:"Gebruik media omslag",show_volume_level:"Toon volumeniveau",media_controls:"Mediabediening",media_controls_list:{on_off:"zet aan/uit",shuffle:"Shuffle",previous:"Vorige nummer",play_pause_stop:"Speel/pauze/stop",next:"Volgende nummer",repeat:"Herhalen"},volume_controls:"Volumeregeling",volume_controls_list:{volume_buttons:"Volume knoppen",volume_set:"Volumeniveau",volume_mute:"Dempen"}},lock:{lock:"Vergrendel",unlock:"Ontgrendel",open:"Open"},humidifier:{show_target_humidity_control:"Vochtigheid controle?"},climate:{show_temperature_control:"Temperatuur controle",hvac_modes:"HVAC Modes"},number:{display_mode:"Weergave Modus",display_mode_list:{default:"Standaard (schuifbalk)",slider:"Schuifbalk",buttons:"Knoppen"}}},chip:{sub_element_editor:{title:"Chip editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Toevoegen chip",edit:"Bewerk",clear:"Maak leeg",select:"Selecteer chip",types:{action:"Actie","alarm-control-panel":"Alarm",back:"Terug",conditional:"Voorwaardelijk",entity:"Entiteit",light:"Licht",menu:"Menu",spacer:"Afstandhouder",template:"Sjabloon",weather:"Weer"}}}},Ki={not_found:"Entiteit niet gevonden"},Gi={editor:Xi,card:Ki},qi={form:{color_picker:{values:{default:"Domyślny kolor"}},info_picker:{values:{default:"Domyślne informacje",name:"Nazwa",state:"Stan","last-changed":"Ostatnia zmiana","last-updated":"Ostatnia aktualizacja",none:"Brak"}},icon_type_picker:{values:{default:"Domyślny typ",icon:"Ikona","entity-picture":"Obraz encji",none:"Brak"}},layout_picker:{values:{default:"Układ domyślny",vertical:"Układ pionowy",horizontal:"Układ poziomy"}},alignment_picker:{values:{default:"Wyrównanie domyślne",start:"Wyrównanie do lewej",end:"Wyrównanie do prawej",center:"Wyśrodkowanie",justify:"Justowanie"}}},card:{generic:{icon_color:"Kolor ikony",layout:"Układ",fill_container:"Wypełnij zawartością",primary_info:"Informacje główne",secondary_info:"Informacje drugorzędne",icon_type:"Typ ikony",content_info:"Zawartość",use_entity_picture:"Użyć obrazu encji?",collapsible_controls:"Zwiń sterowanie, jeśli wyłączone",icon_animation:"Animować, gdy aktywny?"},light:{show_brightness_control:"Sterowanie jasnością?",use_light_color:"Użyj koloru światła",show_color_temp_control:"Sterowanie temperaturą światła?",show_color_control:"Sterowanie kolorami?",incompatible_controls:"Niektóre funkcje są niewidoczne, jeśli światło ich nie obsługuje."},fan:{show_percentage_control:"Sterowanie procentowe?",show_oscillate_control:"Sterowanie oscylacją?"},cover:{show_buttons_control:"Przyciski sterujące?",show_position_control:"Sterowanie położeniem?",show_tilt_position_control:"Sterowanie poziomem otwarcia?"},template:{primary:"Informacje główne",secondary:"Informacje drugorzędne",multiline_secondary:"Drugorzędne wielowierszowe?",entity_extra:"Używane w szablonach i akcjach",content:"Zawartość",badge_icon:"Ikona odznaki",badge_color:"Kolor odznaki",picture:"Obraz (zamiast ikony)"},title:{title:"Tytuł",subtitle:"Podtytuł"},chips:{alignment:"Wyrównanie"},weather:{show_conditions:"Warunki?",show_temperature:"Temperatura?"},update:{show_buttons_control:"Przyciski sterujące?"},vacuum:{commands:"Polecenia"},"media-player":{use_media_info:"Użyj informacji o multimediach",use_media_artwork:"Użyj okładek multimediów",show_volume_level:"Wyświetl poziom głośności",media_controls:"Sterowanie multimediami",media_controls_list:{on_off:"Włącz/wyłącz",shuffle:"Losowo",previous:"Poprzednie nagranie",play_pause_stop:"Odtwórz/Pauza/Zatrzymaj",next:"Następne nagranie",repeat:"Powtarzanie"},volume_controls:"Sterowanie głośnością",volume_controls_list:{volume_buttons:"Przyciski głośności",volume_set:"Poziom głośności",volume_mute:"Wycisz"}},lock:{lock:"Zablokuj",unlock:"Odblokuj",open:"Otwórz"},humidifier:{show_target_humidity_control:"Sterowanie wilgotnością?"},climate:{show_temperature_control:"Sterowanie temperaturą?",hvac_modes:"Tryby urządzenia"}},chip:{sub_element_editor:{title:"Edytor czipów"},conditional:{chip:"Czip"},"chip-picker":{chips:"Czipy",add:"Dodaj czip",edit:"Edytuj",clear:"Wyczyść",select:"Wybierz czip",types:{action:"Akcja","alarm-control-panel":"Alarm",back:"Wstecz",conditional:"Warunkowy",entity:"Encja",light:"Światło",menu:"Menu",spacer:"Odstęp",template:"Szablon",weather:"Pogoda"}}}},Zi={editor:qi},Ji={form:{color_picker:{values:{default:"Cor padrão"}},info_picker:{values:{default:"Informações padrão",name:"Nome",state:"Estado","last-changed":"Última alteração","last-updated":"Última atualização",none:"Nenhum"}},layout_picker:{values:{default:"Layout padrão",vertical:"Layout vertical",horizontal:"Layout horizontal"}},alignment_picker:{values:{default:"Padrão (inicio)",end:"Final",center:"Centro",justify:"Justificado"}}},card:{generic:{icon_color:"Cor do ícone?",layout:"Layout",primary_info:"Informações primárias",secondary_info:"Informações secundárias",use_entity_picture:"Usar imagem da entidade?",icon_animation:"Animar ícone quando ativo?"},light:{show_brightness_control:"Mostrar controle de brilho?",use_light_color:"Usar cor da luz?",show_color_temp_control:"Mostrar controle de temperatura?",show_color_control:"Mostrar controle de cor?",incompatible_controls:"Alguns controles podem não ser exibidos se sua luz não suportar o recurso."},fan:{show_percentage_control:"Mostrar controle de porcentagem?",show_oscillate_control:"Mostrar controle de oscilação?"},cover:{show_buttons_control:"Mostrar botões?",show_position_control:"Mostrar controle de posição?"},template:{primary:"Informações primárias",secondary:"Informações secundárias",multiline_secondary:"Multilinha secundária?",content:"Conteúdo"},title:{title:"Título",subtitle:"Subtítulo"},chips:{alignment:"Alinhamento"},weather:{show_conditions:"Condições?",show_temperature:"Temperatura?"}},chip:{sub_element_editor:{title:"Editor de fichas"},conditional:{chip:"Ficha"},"chip-picker":{chips:"Fichas",add:"Adicionar ficha",edit:"Editar",clear:"Limpar",select:"Selecionar ficha",types:{action:"Ação","alarm-control-panel":"Alarme",back:"Voltar",conditional:"Condicional",entity:"Entidade",light:"Iluminação",menu:"Menu",template:"Modelo",weather:"Clima"}}}},Qi={editor:Ji},to={form:{color_picker:{values:{default:"Cor padrão"}},info_picker:{values:{default:"Informações padrão",name:"Nome",state:"Estado","last-changed":"Última alteração","last-updated":"Última atualização",none:"Nenhum"}},layout_picker:{values:{default:"Layout padrão",vertical:"Layout vertical",horizontal:"Layout horizontal"}},alignment_picker:{values:{default:"Padrão (inicio)",end:"Fim",center:"Centrado",justify:"Justificado"}}},card:{generic:{icon_color:"Cor do ícone?",layout:"Layout",primary_info:"Informações primárias",secondary_info:"Informações secundárias",use_entity_picture:"Usar imagem da entidade?",icon_animation:"Animar ícone quando ativo?"},light:{show_brightness_control:"Mostrar controle de brilho?",use_light_color:"Usar cor da luz?",show_color_temp_control:"Mostrar controle de temperatura?",show_color_control:"Mostrar controle de cor?",incompatible_controls:"Alguns controles podem não ser exibidos se a luz não suportar o recurso."},fan:{show_percentage_control:"Mostrar controle de porcentagem?",show_oscillate_control:"Mostrar controle de oscilação?"},cover:{show_buttons_control:"Mostrar botões?",show_position_control:"Mostrar controle de posição?"},template:{primary:"Informações primárias",secondary:"Informações secundárias",multiline_secondary:"Multilinha secundária?",content:"Conteúdo"},title:{title:"Título",subtitle:"Subtítulo"},chips:{alignment:"Alinhamento"},weather:{show_conditions:"Condições?",show_temperature:"Temperatura?"}},chip:{sub_element_editor:{title:"Editor de fichas"},conditional:{chip:"Ficha"},"chip-picker":{chips:"Fichas",add:"Adicionar ficha",edit:"Editar",clear:"Limpar",select:"Selecionar ficha",types:{action:"Ação","alarm-control-panel":"Alarme",back:"Voltar",conditional:"Condicional",entity:"Entidade",light:"Iluminação",menu:"Menu",template:"Modelo",weather:"Clima"}}}},eo={editor:to},io={form:{color_picker:{values:{default:"Culoare implicită"}},info_picker:{values:{default:"Informație implicită",name:"Nume",state:"Stare","last-changed":"Ultima modificare","last-updated":"Ultima actulizare",none:"Niciuna"}},icon_type_picker:{values:{default:"Tip implicit",icon:"Pictogramă","entity-picture":"Imagine",none:"Niciuna"}},layout_picker:{values:{default:"Aranjare implicită",vertical:"Verticală",horizontal:"Orizontală"}},alignment_picker:{values:{default:"Aliniere implicită",start:"Stânga",end:"Dreapta",center:"Centrat",justify:"Umplere"}}},card:{generic:{icon_color:"Culoare pictogramă",layout:"Aranjare",fill_container:"Umplere container",primary_info:"Informație principală",secondary_info:"Informație secundară",icon_type:"Tip pictogramă",content_info:"Conținut",use_entity_picture:"Imagine?",collapsible_controls:"Restrângere la dezactivare"},light:{show_brightness_control:"Comandă pentru strălucire?",use_light_color:"Folosește culoarea luminii",show_color_temp_control:"Comandă pentru temperatură de culoare?",show_color_control:"Comandă pentru culoare?",incompatible_controls:"Unele comenzi ar putea să nu fie afișate dacă lumina nu suportă această caracteristică."},fan:{icon_animation:"Animare pictograma la activare?",show_percentage_control:"Comandă procent?",show_oscillate_control:"Comandă oscilație?"},cover:{show_buttons_control:"Comenzi pentru control?",show_position_control:"Comandă pentru poziție?",show_tilt_position_control:"Comandă pentru înclinare?"},template:{primary:"Informație principală",secondary:"Informație secundară",multiline_secondary:"Informație secundară pe mai multe linii?",entity_extra:"Folosită în șabloane și acțiuni",content:"Conținut",badge_icon:"Pictogramă insignă",badge_color:"Culoare insignă",picture:"Imagine (inlocuiește pictograma)"},title:{title:"Titlu",subtitle:"Subtitlu"},chips:{alignment:"Aliniere"},weather:{show_conditions:"Condiții?",show_temperature:"Temperatură?"},update:{show_buttons_control:"Comenzi control?"},vacuum:{commands:"Comenzi"},"media-player":{use_media_info:"Informații media",use_media_artwork:"Grafică media",show_volume_level:"Nivel volum",media_controls:"Comenzi media",media_controls_list:{on_off:"Pornit/Oprit",shuffle:"Amestecare",previous:"Pista anterioară",play_pause_stop:"Redare/Pauză/Stop",next:"Pista următoare",repeat:"Mod repetare"},volume_controls:"Comenzi volum",volume_controls_list:{volume_buttons:"Comenzi volum",volume_set:"Nivel volum",volume_mute:"Dezactivare sunet"}},lock:{lock:"Încuie",unlock:"Descuie",open:"Deschide"},humidifier:{show_target_humidity_control:"Comenzi umiditate?"},climate:{show_temperature_control:"Comenzi temperatură?",hvac_modes:"Moduri HVAC"}},chip:{sub_element_editor:{title:"Editor jeton"},conditional:{chip:"Jeton"},"chip-picker":{chips:"Jetoane",add:"Adaugă jeton",edit:"Modifică",clear:"Șterge",select:"Alege jeton",types:{action:"Acțiune","alarm-control-panel":"Alarmă",back:"Înapoi",conditional:"Condițional",entity:"Entitate",light:"Lumină",menu:"Meniu",template:"Șablon",weather:"Vreme"}}}},oo={editor:io},no={form:{color_picker:{values:{default:"Цвет по умолчанию"}},info_picker:{values:{default:"По умолчанию",name:"Имя",state:"Статус","last-changed":"Последнее изменение","last-updated":"Последнее обновление",none:"Нет"}},icon_type_picker:{values:{default:"По умолчанию",icon:"Иконка","entity-picture":"Изображение",none:"Нет"}},layout_picker:{values:{default:"Расположение по умолчанию",vertical:"Вертикальное расположение",horizontal:"Горизонтальное расположение"}},alignment_picker:{values:{default:"Выравнивание по умолчанию",start:"К началу",end:"К концу",center:"По центру",justify:"На всю ширину"}}},card:{generic:{icon_color:"Цвет иконки",layout:"Расположение",fill_container:"Заполнение",primary_info:"Основная информация",secondary_info:"Второстепенная информация",icon_type:"Тип иконки",content_info:"Содержимое",use_entity_picture:"Использовать изображение объекта?",collapsible_controls:"Сворачивать элементы управления при выключении"},light:{show_brightness_control:"Управлять яркостью?",use_light_color:"Использовать текущий цвет света",show_color_temp_control:"Управлять цветовой температурой?",show_color_control:"Управлять цветом?",incompatible_controls:"Некоторые элементы управления могут не отображаться, если ваш светильник не поддерживает эти функции."},fan:{icon_animation:"Анимировать иконку когда включено?",show_percentage_control:"Управлять процентами?",show_oscillate_control:"Oscillate control?"},cover:{show_buttons_control:"Добавить кнопки управления?",show_position_control:"Управлять позицией?",show_tilt_position_control:"Управлять наклоном?"},template:{primary:"Основная информация",secondary:"Второстепенная информация",multiline_secondary:"Многострочная Второстепенная информация?",entity_extra:"Используется в шаблонах и действиях",content:"Содержимое",badge_icon:"Иконка значка",badge_color:"Цвет значка",picture:"Изображение (заменить иконку)"},title:{title:"Заголовок",subtitle:"Подзаголовок"},chips:{alignment:"Выравнивание"},weather:{show_conditions:"Условия?",show_temperature:"Температура?"},update:{show_buttons_control:"Кнопки управления?"},vacuum:{commands:"Команды"},"media-player":{use_media_info:"Использовать информацию с медиа-устройства",use_media_artwork:"Использовать обложку с медиа-устройства",show_volume_level:"Показать уровень громкости",media_controls:"Управление медиа-устройством",media_controls_list:{on_off:"Включение/выключение",shuffle:"Перемешивание",previous:"Предыдущий трек",play_pause_stop:"Воспроизведение/пауза/остановка",next:"Следующий трек",repeat:"Режим повтора"},volume_controls:"Регулятор громкости",volume_controls_list:{volume_buttons:"Кнопки громкости",volume_set:"Уровень громкости",volume_mute:"Без звука"}},lock:{lock:"Закрыто",unlock:"Разблокировано",open:"Открыто"},humidifier:{show_target_humidity_control:"Управлять целевым уровенем влажности?"},climate:{show_temperature_control:"Управлять целевой температурой?",hvac_modes:"Режимы работы"}},chip:{sub_element_editor:{title:"Редактор мини-карточек"},conditional:{chip:"Мини-карточка"},"chip-picker":{chips:"Мини-карточки",add:"Добавить мини-карточку",edit:"Изменить",clear:"Очистить",select:"Выбрать мини-карточку",types:{action:"Действие","alarm-control-panel":"Тревога",back:"Назад",conditional:"Условия",entity:"Объект",light:"Освещение",menu:"Меню",template:"Шаблон",weather:"Погода"}}}},ro={editor:no},ao={form:{color_picker:{values:{default:"Privzeta barva"}},info_picker:{values:{default:"Privzete informacije",name:"Naziv",state:"Stanje","last-changed":"Zadnja sprememba","last-updated":"Zadnja posodobitev",none:"Brez"}},icon_type_picker:{values:{default:"Privzeta vrsta",icon:"Ikona","entity-picture":"Slika entitete",none:"Brez"}},layout_picker:{values:{default:"Privzeta postavitev",vertical:"Vertikalna postavitev",horizontal:"Horizontalna postavitev"}},alignment_picker:{values:{default:"Privzeta poravnava",start:"Pričetek",end:"Konec",center:"Center",justify:"Poravnava"}}},card:{generic:{icon_color:"Barva ikone",layout:"Postavitev",fill_container:"Zapolnitev prostora",primary_info:"Primarna informacija",secondary_info:"Sekundarna informacija",icon_type:"Vrsta ikone",content_info:"Vsebina",use_entity_picture:"Uporabi sliko entitete?",collapsible_controls:"Strni kontrolnike, ko so izklopljeni",icon_animation:"Animacija ikone, ko je aktivna?"},light:{show_brightness_control:"Nadzor svetlosti?",use_light_color:"Uporabi svetlo barvo",show_color_temp_control:"Nadzor temperature barve?",show_color_control:"Nadzor barv?",incompatible_controls:"Nekateri kontrolniki morda ne bodo prikazani, če vaša luč ne podpira te funkcije."},fan:{show_percentage_control:"Kontrola v odstotkih?",show_oscillate_control:"Kontrola nihanja?"},cover:{show_buttons_control:"Gumbi za upravljanje?",show_position_control:"Nadzor položaja?",show_tilt_position_control:"Nadzor nagiba?"},template:{primary:"Primarna informacija",secondary:"Sekundarna informacija",multiline_secondary:"Večvrstični sekundarni?",entity_extra:"Uporablja se v predlogah in dejanjih",content:"Vsebina",badge_icon:"Ikona značke",badge_color:"Barva značke",picture:"Slika (nadomestila bo ikono)"},title:{title:"Naziv",subtitle:"Podnaslov",title_tap_action:"Dejanje dotika naslova",subtitle_tap_action:"Dejanje dotika podnapisov"},chips:{alignment:"Poravnava"},weather:{show_conditions:"Pogoji?",show_temperature:"Temperatura?"},update:{show_buttons_control:"Gumbi za upravljanje?"},vacuum:{commands:"Ukazi",commands_list:{on_off:"Vklop/izklop"}},"media-player":{use_media_info:"Uporabite informacije o medijih",use_media_artwork:"Uporabite medijsko umetniško delo",show_volume_level:"Pokaži raven glasnosti",media_controls:"Nadzor medijev",media_controls_list:{on_off:"Vklop/izklop",shuffle:"Naključno",previous:"Prejšnja skladba",play_pause_stop:"Predvajaj/pavza/ustavi",next:"Naslednja skladba",repeat:"Ponavljajoči način"},volume_controls:"Kontrole glasnosti",volume_controls_list:{volume_buttons:"Gumbi za glasnost",volume_set:"Raven glasnosti",volume_mute:"Tiho"}},lock:{lock:"Zaklepanje",unlock:"Odkleni",open:"Odprto"},humidifier:{show_target_humidity_control:"Nadzor vlažnosti?"},climate:{show_temperature_control:"Nadzor temperature?",hvac_modes:"HVAC načini"},number:{display_mode:"Način prikaza",display_mode_list:{default:"Privzeto (drsnik)",slider:"Drsnik",buttons:"Gumbi"}}},chip:{sub_element_editor:{title:"Urejevalnik čipov"},conditional:{chip:"Ćiš"},"chip-picker":{chips:"Čipi",add:"Dodaj čip",edit:"Uredi",clear:"Pobriši",select:"Izbira čipa",types:{action:"Dejanje","alarm-control-panel":"Alarm",back:"Nazaj",conditional:"Pogojno",entity:"Entiteta",light:"Svetloba",menu:"Meni",spacer:"Distančnik",template:"Predloga",weather:"Vreme"}}}},lo={not_found:"Entiteta ni najdena"},so={editor:ao,card:lo},co={form:{color_picker:{values:{default:"Predvolená farba"}},info_picker:{values:{default:"Predvolené informácie",name:"Názov",state:"Stav","last-changed":"Posledná zmena","last-updated":"Posledná aktualizácia",none:"Žiadna"}},icon_type_picker:{values:{default:"Predvolený typ",icon:"Ikona","entity-picture":"Obrázok entity",none:"Žiadny"}},layout_picker:{values:{default:"Predvolené rozloženie",vertical:"Zvislé rozloženie",horizontal:"Vodorovné rozloženie"}},alignment_picker:{values:{default:"Predvolené zarovnanie",start:"Začiatok",end:"Koniec",center:"Stred",justify:"Vyplniť"}}},card:{generic:{icon_color:"Farba ikony",layout:"Rozloženie",fill_container:"Vyplniť priestor",primary_info:"Základné info",secondary_info:"Doplnkové info",icon_type:"Typ ikony",content_info:"Obsah",use_entity_picture:"Použiť obrázok entity?",collapsible_controls:"Skryť ovládanie v stave VYP.",icon_animation:"Animovaná ikona v stave ZAP?"},light:{show_brightness_control:"Ovládanie jasu?",use_light_color:"Použiť farbu svetla",show_color_temp_control:"Ovládanie teploty?",show_color_control:"Ovládanie farby?",incompatible_controls:"Niektoré ovládacie prvky sa nemusia zobraziť, pokiaľ ich svetlo nepodporuje."},fan:{show_percentage_control:"Ovládanie rýchlosti v percentách?",show_oscillate_control:"Ovládanie oscilácie?"},cover:{show_buttons_control:"Zobraziť ovládacie tlačidlá?",show_position_control:"Ovládanie pozície?",show_tilt_position_control:"Ovládanie natočenia?"},template:{primary:"Základné info",secondary:"Doplnkové info",multiline_secondary:"Viacriadkové doplnkové info?",entity_extra:"Použitá v šablónach a akciách",content:"Obsah",badge_icon:"Ikona odznaku",badge_color:"Farba odznaku",picture:"Obrázok (nahrádza ikonu)"},title:{title:"Nadpis",subtitle:"Podnadpis",title_tap_action:"Akcia klepnutia na názov",subtitle_tap_action:"Akcia klepnutia na titulky"},chips:{alignment:"Zarovnanie"},weather:{show_conditions:"Zobraziť podmienky?",show_temperature:"Zobraziť teplotu?"},update:{show_buttons_control:"Zobraziť ovládacie tlačidlá?"},vacuum:{commands:"Príkazy",commands_list:{on_off:"Zapnúť/Vypnúť"}},"media-player":{use_media_info:"Použiť info o médiu",use_media_artwork:"Použiť obrázok z média",show_volume_level:"Zobraziť úroveň hlasitosti",media_controls:"Ovládanie média",media_controls_list:{on_off:"Zap / Vyp",shuffle:"Premiešať",previous:"Predchádzajúca",play_pause_stop:"Spustiť/pauza/stop",next:"Ďalšia",repeat:"Opakovať"},volume_controls:"Ovládanie hlasitosti",volume_controls_list:{volume_buttons:"Tlačidlá hlasitosti",volume_set:"Úroveň hlasitosti",volume_mute:"Stlmiť"}},lock:{lock:"Zamknuté",unlock:"Odomknuté",open:"Otvorené"},humidifier:{show_target_humidity_control:"Ovládanie vlhkosti?"},climate:{show_temperature_control:"Ovládanie teploty?",hvac_modes:"HVAC mód"},number:{display_mode:"Režim zobrazenia",display_mode_list:{default:"Predvolené (posúvač)",slider:"Posúvač",buttons:"Tlačidlá"}}},chip:{sub_element_editor:{title:"Editor štítkov"},conditional:{chip:"Štítok"},"chip-picker":{chips:"Štítky",add:"Pridať štítok",edit:"Editovať",clear:"Vymazať",select:"Vybrať štítok",types:{action:"Akcia","alarm-control-panel":"Alarm",back:"Späť",conditional:"Podmienené",entity:"Entita",light:"Svetlo",menu:"Menu",spacer:"Medzera",template:"Šablóna",weather:"Počasie"}}}},uo={not_found:"Entita nenájdená"},ho={editor:co,card:uo},mo={form:{color_picker:{values:{default:"Standardfärg"}},info_picker:{values:{default:"Förvald information",name:"Namn",state:"Status","last-changed":"Sist ändrad","last-updated":"Sist uppdaterad",none:"Ingen"}},layout_picker:{values:{default:"Standard",vertical:"Vertikal",horizontal:"Horisontell"}},alignment_picker:{values:{default:"Standard (början)",end:"Slutet",center:"Centrerad",justify:"Anpassa"}}},card:{generic:{icon_color:"Ikonens färg",layout:"Layout",primary_info:"Primär information",secondary_info:"Sekundär information",use_entity_picture:"Använd enheten bild?",icon_animation:"Animera ikonen när fläkten är på?"},light:{show_brightness_control:"Styr ljushet?",use_light_color:"Styr ljusets färg",show_color_temp_control:"Styr färgtemperatur?",show_color_control:"Styr färg?",incompatible_controls:"Kontroller som inte stöds av enheten kommer inte visas."},fan:{show_percentage_control:"Procentuell kontroll?",show_oscillate_control:"Kontroll för oscillera?"},cover:{show_buttons_control:"Visa kontrollknappar?",show_position_control:"Visa positionskontroll?"},template:{primary:"Primär information",secondary:"Sekundär information",multiline_secondary:"Sekundär med flera rader?",content:"Innehåll"},title:{title:"Rubrik",subtitle:"Underrubrik"},chips:{alignment:"Justering"},weather:{show_conditions:"Förhållanden?",show_temperature:"Temperatur?"}},chip:{sub_element_editor:{title:"Chipredigerare"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Lägg till chip",edit:"Redigera",clear:"Rensa",select:"Välj chip",types:{action:"Händelse","alarm-control-panel":"Alarm",back:"Bakåt",conditional:"Villkorad",entity:"Enhet",light:"Ljus",menu:"Meny",template:"Mall",weather:"Väder"}}}},po={editor:mo},fo={form:{color_picker:{values:{default:"Varsayılan renk"}},info_picker:{values:{default:"Varsayılan bilgi",name:"İsim",state:"Durum","last-changed":"Son Değişim","last-updated":"Son Güncelleme",none:"None"}},layout_picker:{values:{default:"Varsayılan düzen",vertical:"Dikey düzen",horizontal:"Yatay düzen"}},alignment_picker:{values:{default:"Varsayılan hizalama",start:"Sola yasla",end:"Sağa yasla",center:"Ortala",justify:"İki yana yasla"}}},card:{generic:{icon_color:"Simge renki",layout:"Düzen",primary_info:"Birinci bilgi",secondary_info:"İkinci bilgi",content_info:"İçerik",use_entity_picture:"Varlık resmi kullanılsın",icon_animation:"Aktif olduğunda simgeyi hareket ettir"},light:{show_brightness_control:"Parlaklık kontrolü",use_light_color:"Işık rengini kullan",show_color_temp_control:"Renk ısısı kontrolü",show_color_control:"Renk kontrolü",incompatible_controls:"Kullandığınız lamba bu özellikleri desteklemiyorsa bazı kontroller görüntülenemeyebilir."},fan:{show_percentage_control:"Yüzde kontrolü",show_oscillate_control:"Salınım kontrolü"},cover:{show_buttons_control:"Düğme kontrolleri",show_position_control:"Pozisyon kontrolü"},template:{primary:"Birinci bilgi",secondary:"İkinci bilgi",multiline_secondary:"İkinci bilgi çok satır olsun",entity_extra:"Şablonlarda ve eylemlerde kullanılsın",content:"İçerik"},title:{title:"Başlık",subtitle:"Altbaşlık"},chips:{alignment:"Hizalama"},weather:{show_conditions:"Hava koşulu",show_temperature:"Sıcaklık"},update:{show_buttons_control:"Düğme kontrolü"},vacuum:{commands:"Komutlar"}},chip:{sub_element_editor:{title:"Chip düzenleyici"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Chip ekle",edit:"Düzenle",clear:"Temizle",select:"Chip seç",types:{action:"Eylem","alarm-control-panel":"Alarm",back:"Geri",conditional:"Koşullu",entity:"Varlık",light:"Işık",menu:"Menü",template:"Şablon",weather:"Hava Durumu"}}}},go={editor:fo},_o={form:{color_picker:{values:{default:"Колір за замовчуванням"}},info_picker:{values:{default:"Інформація за замовчуванням",name:"Назва",state:"Стан","last-changed":"Востаннє змінено","last-updated":"Востаннє оновлено",none:"Нічого"}},icon_type_picker:{values:{default:"За замовчуванням",icon:"Іконка","entity-picture":"Зображення сутності",none:"Нічого"}},layout_picker:{values:{default:"Розташування за замовчуванням",vertical:"Вертикальне розташування",horizontal:"Горизонтальне розташування"}},alignment_picker:{values:{default:"Вирівнювання за замовчуванням",start:"На початку",end:"В кінці",center:"По центру",justify:"Вирівняти"}}},card:{generic:{icon_color:"Колір іконки",layout:"Розташування",fill_container:"Заповнити контейнер",primary_info:"Головна інформація",secondary_info:"Додаткова інформація",icon_type:"Тип іконки",content_info:"Вміст",use_entity_picture:"Використовувати зображення сутності?",collapsible_controls:"Приховувати елементи керування коли вимкнено?",icon_animation:"Анімувати іконку при активації?"},light:{show_brightness_control:"Контроль яскравості?",use_light_color:"Використовувати колір світла",show_color_temp_control:"Керування температурою світла?",show_color_control:"Керування кольором світла?",incompatible_controls:"Деякі елементи керування можуть не відображатись якщо ваш пристрій не підтримує цю функцію."},fan:{show_percentage_control:"Керування швидкістю?",show_oscillate_control:"Керування повротом?"},cover:{show_buttons_control:"Кнопки керування?",show_position_control:"Керування позицією?",show_tilt_position_control:"Керування нахилом?"},template:{primary:"Головна інформація",secondary:"Додаткова інформаіця",multiline_secondary:"Багаторядкова додаткова інформація?",entity_extra:"Використовується в шаблонах та діях",content:"Вміст",badge_icon:"Іконка значка",badge_color:"Колір значка",picture:"Зображення (замінить іконку)"},title:{title:"Заголовок",subtitle:"Підзаголовок",title_tap_action:"Дія при дотику до заголовку",subtitle_tap_action:"Дія при дотику до підзаголовку"},chips:{alignment:"Вирівнювання"},weather:{show_conditions:"Умови?",show_temperature:"Температура?"},update:{show_buttons_control:"Кнопки керування?"},vacuum:{commands:"Команди",commands_list:{on_off:"Увімкнути/Вимкнути"}},"media-player":{use_media_info:"Використовувати інформацію медіа",use_media_artwork:"Використовувати зображення медіа",show_volume_level:"Показати рівень гучності",media_controls:"Керування медіа",media_controls_list:{on_off:"Увімкнути/Вимкнути",shuffle:"Перемішати",previous:"Попередній трек",play_pause_stop:"Відтворити/пауза/стоп",next:"Наступний трек",repeat:"Режим повторення"},volume_controls:"Елементи керування гучністю",volume_controls_list:{volume_buttons:"Кнопки гучності",volume_set:"Рівень гучності",volume_mute:"Вимк. звук"}},lock:{lock:"Зачинити",unlock:"Відчинити",open:"Відкрити"},humidifier:{show_target_humidity_control:"Керування вологістю?"},climate:{show_temperature_control:"Керування температурою?",hvac_modes:"Режими"},number:{display_mode:"Відображати режим",display_mode_list:{default:"За замовчуванням (повзунок)",slider:"Повзунок",buttons:"Кнопки"}}},chip:{sub_element_editor:{title:"Редактор міні-карток"},conditional:{chip:"Міні-картка"},"chip-picker":{chips:"Міні-картки",add:"Додати міні-картку",edit:"Редагувати",clear:"Очистити",select:"Обрати міні-картку",types:{action:"Дія","alarm-control-panel":"Сигналізація",back:"Назад",conditional:"Умовна",entity:"Сутність",light:"Світло",menu:"Меню",spacer:"Порожнє місце",template:"Вручну",weather:"Погода"}}}},vo={not_found:"Сутність не знайдено"},bo={editor:_o,card:vo},yo={form:{color_picker:{values:{default:"Màu mặc định"}},info_picker:{values:{default:"Thông tin mặc định",name:"Tên",state:"Trạng thái","last-changed":"Lần thay đổi cuối","last-updated":"Lần cập nhật cuối",none:"Không có"}},icon_type_picker:{values:{default:"Kiểu mặc định",icon:"Biểu tượng","entity-picture":"Ảnh thực thể",none:"Không có"}},layout_picker:{values:{default:"Bố cục mặc định",vertical:"Bố cục dọc",horizontal:"Bố cục ngang"}},alignment_picker:{values:{default:"Căn chỉnh mặc định",start:"Căn đầu",end:"Căn cuối",center:"Căn giữa",justify:"Căn hai bên"}}},card:{generic:{icon_color:"Màu biểu tượng",layout:"Bố cục",fill_container:"Làm đầy ô chứa",primary_info:"Thông tin chính",secondary_info:"Thông tin phụ",icon_type:"Kiểu biểu tượng",content_info:"Nội dung",use_entity_picture:"Dùng ảnh của thực thể?",collapsible_controls:"Thu nhỏ điều kiển khi tắt",icon_animation:"Biểu tượng chuyển động khi kích hoạt?"},light:{show_brightness_control:"Điều khiển độ sáng?",use_light_color:"Dùng màu đèn",show_color_temp_control:"Điều khiển nhiệt độ màu?",show_color_control:"Điều khiển màu sắc?",incompatible_controls:"Một số điều khiển sẽ không được hiển thị nếu đèn của bạn không hỗ trợ tính năng đó."},fan:{show_percentage_control:"Điều khiển dạng phần trăm?",show_oscillate_control:"Điều khiển xoay?"},cover:{show_buttons_control:"Điều khiển nút bấm?",show_position_control:"Điều khiển vị trí?",show_tilt_position_control:"Điều khiển độ nghiêng?"},template:{primary:"Thông tin chính",secondary:"Thông tin phụ",multiline_secondary:"Nhiều dòng thông tin phụ?",entity_extra:"Được sử dụng trong bản mẫu và hành động",content:"Nội dung",badge_icon:"Biểu tượng huy hiệu",badge_color:"Màu huy hiệu",picture:"Ảnh (thay cho biểu tượng)"},title:{title:"Tiêu đề",subtitle:"Phụ đề",title_tap_action:"Hành động khi nhấp tiêu đề",subtitle_tap_action:"Hành động khi nhấp phụ đề"},chips:{alignment:"Căn chỉnh"},weather:{show_conditions:"Điều kiện?",show_temperature:"Nhiệt độ?"},update:{show_buttons_control:"Điều khiển nút bấm?"},vacuum:{commands:"Mệnh lệnh",commands_list:{on_off:"Bật/tắt"}},"media-player":{use_media_info:"Dùng thông tin đa phương tiện",use_media_artwork:"Dùng ảnh đa phương tiện",show_volume_level:"Hiện mức âm lượng",media_controls:"Điều khiển đa phương tiện",media_controls_list:{on_off:"Bật/tắt",shuffle:"Xáo trộn",previous:"Bài trước",play_pause_stop:"Phát/tạm dừng/dừng",next:"Bài tiếp theo",repeat:"Chế độ lặp lại"},volume_controls:"Điều khiển âm lượng",volume_controls_list:{volume_buttons:"Nút âm lượng",volume_set:"Mức âm lượng",volume_mute:"Im lặng"}},lock:{lock:"Khóa",unlock:"Mở khóa",open:"Mở"},humidifier:{show_target_humidity_control:"Điều khiển độ ẩm?"},climate:{show_temperature_control:"Điều khiển nhiệt độ?",hvac_modes:"Chế độ điều hòa"},number:{display_mode:"Chế độ hiển thị",display_mode_list:{default:"Mặc định (thanh trượt)",slider:"Thanh trượt",buttons:"Nút"}}},chip:{sub_element_editor:{title:"Trình soạn phỉnh"},conditional:{chip:"Phỉnh"},"chip-picker":{chips:"Phỉnh",add:"Thêm phỉnh",edit:"Chỉnh sửa",clear:"Tẩy trống",select:"Chọn phỉnh",types:{action:"Hành động","alarm-control-panel":"Báo động",back:"Quay về",conditional:"Điều kiện",entity:"Thực thể",light:"Đèn",menu:"Trình đơn",spacer:"Ngăn cách",template:"Mẫu",weather:"Thời tiết"}}}},xo={not_found:"Không tìm thấy thực thể"},wo={editor:yo,card:xo},ko={form:{color_picker:{values:{default:"默认颜色"}},info_picker:{values:{default:"默认信息",name:"名称",state:"状态","last-changed":"变更时间","last-updated":"更新时间",none:"无"}},icon_type_picker:{values:{default:"默认类型",icon:"图标","entity-picture":"实体图片",none:"无"}},layout_picker:{values:{default:"默认布局",vertical:"垂直布局",horizontal:"水平布局"}},alignment_picker:{values:{default:"默认",start:"左对齐",end:"右对齐",center:"居中对齐",justify:"两端对齐"}}},card:{generic:{icon_color:"图标颜色",layout:"布局",fill_container:"填满容器",primary_info:"首要信息",secondary_info:"次要信息",icon_type:"图标类型",content_info:"内容",use_entity_picture:"使用实体图片?",collapsible_controls:"关闭时隐藏控制器",icon_animation:"激活时使用动态图标?"},light:{show_brightness_control:"亮度控制?",use_light_color:"使用灯光颜色",show_color_temp_control:"色温控制?",show_color_control:"颜色控制?",incompatible_controls:"设备不支持的控制器将不会显示。"},fan:{show_percentage_control:"百分比控制?",show_oscillate_control:"摆动控制?"},cover:{show_buttons_control:"按钮控制?",show_position_control:"位置控制?",show_tilt_position_control:"角度控制?"},template:{primary:"首要信息",secondary:"次要信息",multiline_secondary:"多行次要信息?",entity_extra:"用于模板和动作",content:"内容",badge_icon:"徽标图标",badge_color:"徽标颜色",picture:"图片 (将会替代图标)"},title:{title:"标题",subtitle:"子标题",title_tap_action:"标题点击动作",subtitle_tap_action:"子标题点击动作"},chips:{alignment:"对齐"},weather:{show_conditions:"条件?",show_temperature:"温度?"},update:{show_buttons_control:"控制按钮?"},vacuum:{commands:"命令",commands_list:{on_off:"开/关"}},"media-player":{use_media_info:"使用媒体信息",use_media_artwork:"使用媒体插图",show_volume_level:"显示音量大小",media_controls:"媒体控制",media_controls_list:{on_off:"开启/关闭",shuffle:"随机",previous:"上一曲",play_pause_stop:"播放/暂停/停止",next:"下一曲",repeat:"循环模式"},volume_controls:"音量控制",volume_controls_list:{volume_buttons:"音量按钮",volume_set:"音量等级",volume_mute:"静音"}},lock:{lock:"锁定",unlock:"解锁",open:"打开"},humidifier:{show_target_humidity_control:"湿度控制?"},climate:{show_temperature_control:"温度控制?",hvac_modes:"空调模式"},number:{display_mode:"显示模式",display_mode_list:{default:"默认 (滑块)",slider:"滑块",buttons:"按钮"}}},chip:{sub_element_editor:{title:"Chip 编辑"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"添加 chip",edit:"编辑",clear:"清除",select:"选择 chip",types:{action:"动作","alarm-control-panel":"警戒控制台",back:"返回",conditional:"条件显示",entity:"实体",light:"灯光",menu:"菜单",spacer:"占位符",template:"模板",weather:"天气"}}}},Co={not_found:"未找到实体"},$o={editor:ko,card:Co},Eo={form:{color_picker:{values:{default:"預設顏色"}},info_picker:{values:{default:"預設訊息",name:"名稱",state:"狀態","last-changed":"最近變動時間","last-updated":"最近更新時間",none:"無"}},icon_type_picker:{values:{default:"預設樣式",icon:"圖示","entity-picture":"實體圖片",none:"無"}},layout_picker:{values:{default:"預設佈局",vertical:"垂直佈局",horizontal:"水平佈局"}},alignment_picker:{values:{default:"預設對齊",start:"居左對齊",end:"居右對齊",center:"居中對齊",justify:"兩端對齊"}}},card:{generic:{icon_color:"圖示顏色",layout:"佈局",fill_container:"填滿容器",primary_info:"主要訊息",secondary_info:"次要訊息",icon_type:"圖示樣式",content_info:"內容",use_entity_picture:"使用實體圖片?",collapsible_controls:"關閉時隱藏控制項",icon_animation:"啟動時使用動態圖示?"},light:{show_brightness_control:"亮度控制?",use_light_color:"使用燈光顏色",show_color_temp_control:"色溫控制?",show_color_control:"色彩控制?",incompatible_controls:"不會顯示裝置不支援的控制。"},fan:{show_percentage_control:"百分比控制?",show_oscillate_control:"擺頭控制?"},cover:{show_buttons_control:"按鈕控制?",show_position_control:"位置控制?",show_tilt_position_control:"角度控制?"},template:{primary:"主要訊息",secondary:"次要訊息",multiline_secondary:"多行次要訊息?",entity_extra:"用於模板與動作",content:"內容",badge_icon:"角標圖示",badge_color:"角標顏色",picture:"圖片 (將會取代圖示)"},title:{title:"標題",subtitle:"副標題",title_tap_action:"標題點擊動作",subtitle_tap_action:"副標題點擊動作"},chips:{alignment:"對齊"},weather:{show_conditions:"狀況?",show_temperature:"溫度?"},update:{show_buttons_control:"按鈕控制?"},vacuum:{commands:"指令",commands_list:{on_off:"開啟、關閉"}},"media-player":{use_media_info:"使用媒體資訊",use_media_artwork:"使用媒體插圖",show_volume_level:"顯示音量大小",media_controls:"媒體控制",media_controls_list:{on_off:"開啟、關閉",shuffle:"隨機播放",previous:"上一首",play_pause_stop:"播放、暫停、停止",next:"下一首",repeat:"重複播放"},volume_controls:"音量控制",volume_controls_list:{volume_buttons:"音量按鈕",volume_set:"音量等級",volume_mute:"靜音"}},lock:{lock:"上鎖",unlock:"解鎖",open:"打開"},humidifier:{show_target_humidity_control:"溼度控制?"},climate:{show_temperature_control:"溫度控制?",hvac_modes:"空調模式"},number:{display_mode:"顯示模式",display_mode_list:{default:"預設 (滑桿)",slider:"滑桿",buttons:"按鈕"}}},chip:{sub_element_editor:{title:"小卡片編輯器"},conditional:{chip:"小卡片"},"chip-picker":{chips:"小卡片",add:"新增小卡片",edit:"編輯",clear:"清除",select:"選擇小卡片",types:{action:"動作","alarm-control-panel":"警報器控制",back:"返回",conditional:"條件",entity:"實體",light:"燈光",menu:"選單",spacer:"佔位符",template:"模板",weather:"天氣"}}}},Ao={not_found:"未找到實體"},So={editor:Eo,card:Ao};const Io={ar:Object.freeze({__proto__:null,default:li,editor:ai}),bg:Object.freeze({__proto__:null,default:ci,editor:si}),ca:Object.freeze({__proto__:null,card:ui,default:hi,editor:di}),cs:Object.freeze({__proto__:null,card:pi,default:fi,editor:mi}),da:Object.freeze({__proto__:null,default:_i,editor:gi}),de:Object.freeze({__proto__:null,card:bi,default:yi,editor:vi}),el:Object.freeze({__proto__:null,default:wi,editor:xi}),en:Object.freeze({__proto__:null,card:Ci,default:$i,editor:ki}),es:Object.freeze({__proto__:null,default:Ai,editor:Ei}),fi:Object.freeze({__proto__:null,default:Ii,editor:Si}),fr:Object.freeze({__proto__:null,default:Oi,editor:Ti}),he:Object.freeze({__proto__:null,default:Mi,editor:zi}),hu:Object.freeze({__proto__:null,card:Di,default:Li,editor:ji}),id:Object.freeze({__proto__:null,card:Ni,default:Ri,editor:Pi}),it:Object.freeze({__proto__:null,card:Vi,default:Bi,editor:Fi}),"ko-KR":Object.freeze({__proto__:null,default:Hi,editor:Ui}),nb:Object.freeze({__proto__:null,default:Wi,editor:Yi}),nl:Object.freeze({__proto__:null,card:Ki,default:Gi,editor:Xi}),pl:Object.freeze({__proto__:null,default:Zi,editor:qi}),"pt-BR":Object.freeze({__proto__:null,default:Qi,editor:Ji}),"pt-PT":Object.freeze({__proto__:null,default:eo,editor:to}),ro:Object.freeze({__proto__:null,default:oo,editor:io}),ru:Object.freeze({__proto__:null,default:ro,editor:no}),sl:Object.freeze({__proto__:null,card:lo,default:so,editor:ao}),sk:Object.freeze({__proto__:null,card:uo,default:ho,editor:co}),sv:Object.freeze({__proto__:null,default:po,editor:mo}),tr:Object.freeze({__proto__:null,default:go,editor:fo}),uk:Object.freeze({__proto__:null,card:vo,default:bo,editor:_o}),vi:Object.freeze({__proto__:null,card:xo,default:wo,editor:yo}),"zh-Hans":Object.freeze({__proto__:null,card:Co,default:$o,editor:ko}),"zh-Hant":Object.freeze({__proto__:null,card:Ao,default:So,editor:Eo})};function To(t,e){try{return t.split(".").reduce(((t,e)=>t[e]),Io[e])}catch(t){return}}function Oo(t){return function(e){var i;let o=To(e,null!==(i=null==t?void 0:t.locale.language)&&void 0!==i?i:"en");return o||(o=To(e,"en")),null!=o?o:e}} + paper-icon-item.hidden-panel, + paper-icon-item.hidden-panel span, + paper-icon-item.hidden-panel ha-icon[slot="item-icon"] { + color: var(--secondary-text-color); + cursor: pointer; + } +`,ai=(t,e,i,o)=>{const[n,r,a]=t.split(".",3);return Number(n)>e||Number(n)===e&&Number(r)>=i||void 0!==o};var si={form:{color_picker:{values:{default:"اللون الإفتراضي"}},info_picker:{values:{default:"المعلومات الافتراضية",name:"الإسم",state:"الحالة","last-changed":"آخر تغيير","last-updated":"آخر تحديث",none:"لا شئ"}},icon_type_picker:{values:{default:"النوع افتراضي",icon:"أيقونة","entity-picture":"صورة الكيان",none:"لا شئ"}},layout_picker:{values:{default:"تخطيط افتراضي",vertical:"تخطيط رأسي",horizontal:"تخطيط أفقي"}},alignment_picker:{values:{default:"المحاذاة الافتراضية",start:"بداية",end:"نهاية",center:"توسيط",justify:"مساواة"}}},card:{generic:{icon_color:"لون الأيقونة",layout:"التخطيط",fill_container:"ملئ الحاوية",primary_info:"المعلومات الأساسية",secondary_info:"المعلومات الفرعية",icon_type:"نوع الأيقونة",content_info:"المحتوى",use_entity_picture:"استخدم صورة الكيان؟",collapsible_controls:"تصغير عناصر التحكم عند الإيقاف",icon_animation:"تحريك الرمز عندما يكون نشطًا؟"},light:{show_brightness_control:"التحكم في السطوع؟",use_light_color:"استخدم لون فاتح",show_color_temp_control:"التحكم في حرارة اللون؟",show_color_control:"التحكم في اللون؟",incompatible_controls:"قد لا يتم عرض بعض عناصر التحكم إذا كان الضوء الخاص بك لا يدعم الميزة."},fan:{show_percentage_control:"التحكم في النسبة المئوية؟",show_oscillate_control:"التحكم في التذبذب؟"},cover:{show_buttons_control:"أزرار التحكم؟",show_position_control:"التحكم في الموقع؟"},template:{primary:"المعلومات الأساسية",secondary:"المعلومات الثانوية",multiline_secondary:"متعدد الأسطر الثانوية؟",entity_extra:"تستخدم في القوالب والإجراءات",content:"المحتوى",badge_icon:"أيقونة الشارة",badge_color:"لون الشارة",picture:"صورة (ستحل محل الأيقونة)"},title:{title:"العنوان",subtitle:"العنوان الفرعي"},chips:{alignment:"محاذاة"},weather:{show_conditions:"الأحوال الجوية؟",show_temperature:"الطقس؟"},update:{show_buttons_control:"أزرار التحكم؟"},vacuum:{commands:"الاوامر"},"media-player":{use_media_info:"استخدم معلومات الوسائط",use_media_artwork:"استخدم صورة الوسائط",show_volume_level:"إظهار مستوى الصوت",media_controls:"التحكم في الوسائط",media_controls_list:{on_off:"تشغيل/إيقاف",shuffle:"خلط",previous:"السابق",play_pause_stop:"تشغيل/إيقاف مؤقت/إيقاف",next:"التالي",repeat:"وضع التكرار"},volume_controls:"التحكم في الصوت",volume_controls_list:{volume_buttons:"أزرار الصوت",volume_set:"مستوى الصوت",volume_mute:"كتم"}},lock:{lock:"مقفل",unlock:"إلغاء قفل",open:"مفتوح"},humidifier:{show_target_humidity_control:"التحكم في الرطوبة؟?"},climate:{show_temperature_control:"التحكم في درجة الحرارة؟",hvac_modes:"أوضاع HVAC"}},chip:{sub_element_editor:{title:"محرر الرقاقة"},conditional:{chip:"رقاقة"},"chip-picker":{chips:"رقاقات",add:"أضف رقاقة",edit:"تعديل",clear:"مسح",select:"اختر الرقاقة",types:{action:"إجراء","alarm-control-panel":"تنبيه",back:"رجوع",conditional:"مشروط",entity:"الكيان",light:"Light",menu:"القائمة",template:"قالب",weather:"الطقس"}}}},li={editor:si},ci={form:{color_picker:{values:{default:"Основен цвят"}},info_picker:{values:{default:"Основна информация",name:"Име",state:"Състояние","last-changed":"Последно Променен","last-updated":"Последно Актуализиран",none:"Липсва"}},icon_type_picker:{values:{default:"Основен тип",icon:"Икона","entity-picture":"Картина на обекта",none:"Липсва"}},layout_picker:{values:{default:"Основно оформление",vertical:"Вертикално оформление",horizontal:"Хоризонтално оформление"}},alignment_picker:{values:{default:"Основно подравняване",start:"Старт",end:"Край",center:"Център",justify:"Подравнен"}}},card:{generic:{icon_color:"Цвят на икона",layout:"Оформление",fill_container:"Изпълване на контейнера",primary_info:"Първостепенна информация",secondary_info:"Второстепенна информация",icon_type:"Тип на икона",content_info:"Съдържание",use_entity_picture:"Използвай снимката на обекта?",collapsible_controls:"Свий контролите при изключен",icon_animation:"Анимирай иконата при активен?"},light:{show_brightness_control:"Контрол на яркостта?",use_light_color:"Използвай цвета на светлината",show_color_temp_control:"Контрол на температурата?",show_color_control:"Контрол на цвета?",incompatible_controls:"Някои опции могат да бъдат скрити при условие че осветителното тяло не поддържа фунцията."},fan:{show_percentage_control:"Процентов контрол?",show_oscillate_control:"Контрол на трептенето?"},cover:{show_buttons_control:"Контролни бутони?",show_position_control:"Контрол на позицията?",show_tilt_position_control:"Контрол на наклона?"},template:{primary:"Първостепенна информация",secondary:"Второстепенна информация",multiline_secondary:"Много-редова второстепенна информация?",entity_extra:"Използван в шаблони и действия",content:"Съдържание",badge_icon:"Икона на значка",badge_color:"Цвят на значка",picture:"Картина (ще замени иконата)"},title:{title:"Заглавие",subtitle:"Подзаглавие"},chips:{alignment:"Подравняване"},weather:{show_conditions:"Условия?",show_temperature:"Температура?"},update:{show_buttons_control:"Контролни бутони?"},vacuum:{commands:"Конади",commands_list:{on_off:"Вкл./Изкл."}},"media-player":{use_media_info:"Използвай информация от медията",use_media_artwork:"Използвай визуалните детайли от медията",show_volume_level:"Покажи контрола за звук",media_controls:"Контрол на Медиата",media_controls_list:{on_off:"Вкл./Изкл.",shuffle:"Разбъркано",previous:"Предишен",play_pause_stop:"Пусни/пауза/стоп",next:"Следващ",repeat:"Повтаряне"},volume_controls:"Контрол на звука",volume_controls_list:{volume_buttons:"Бутони за звук",volume_set:"Ниво на звука",volume_mute:"Заглуши"}},lock:{lock:"Заключен",unlock:"Отключен",open:"Отворен"},humidifier:{show_target_humidity_control:"Контрол на влажността?"},climate:{show_temperature_control:"Контрол на температурата?",hvac_modes:"HVAC Режими"}},chip:{sub_element_editor:{title:"Чип редактор"},conditional:{chip:"Чип"},"chip-picker":{chips:"Чипове",add:"Добави чип",edit:"Редактирай",clear:"Изчисти",select:"Избери чип",types:{action:"Действия","alarm-control-panel":"Аларма",back:"Назад",conditional:"Условни",entity:"Обект",light:"Осветление",menu:"Меню",template:"Шаблон",weather:"Време"}}}},di={editor:ci},ui={form:{color_picker:{values:{default:"Color per defecte"}},info_picker:{values:{default:"Informació per defecte",name:"Nom",state:"Estat","last-changed":"Últim Canvi","last-updated":"Última Actualització",none:"Cap"}},icon_type_picker:{values:{default:"Tipus per defecte",icon:"Icona","entity-picture":"Entitat d'imatge",none:"Cap"}},layout_picker:{values:{default:"Distribució per defecte",vertical:"Distribució vertical",horizontal:"Distribució horitzontal"}},alignment_picker:{values:{default:"Alineació per defecte",start:"Inici",end:"Final",center:"Centre",justify:"Justifica"}}},card:{generic:{icon_color:"Color d'icona",layout:"Distribució",fill_container:"Emplena el contenidor",primary_info:"Informació primaria",secondary_info:"Informació secundaria",icon_type:"Tipus d'icona",content_info:"Contingut",use_entity_picture:"Fer servir la imatge de l'entitat?",collapsible_controls:"Amaga els controls en desactivar",icon_animation:"Animar icona en activar?"},light:{show_brightness_control:"Control de brillantor?",use_light_color:"Fes servir el color del llum",show_color_temp_control:"Control de la temperatura del color?",show_color_control:"Control de color?",incompatible_controls:"Alguns controls no es mostraran si l'entitat no suporta eixa funció."},fan:{show_percentage_control:"Control de percentatge?",show_oscillate_control:"Control d'oscil·lació?"},cover:{show_buttons_control:"Botons de control?",show_position_control:"Control de posició?",show_tilt_position_control:"Control d'inclinació?"},template:{primary:"Informació primaria",secondary:"Informació secundaria",multiline_secondary:"Secundaria en varies línies?",entity_extra:"Utilitzats en plantilles i accions",content:"Contingut",badge_icon:"Icona de la insígnia",badge_color:"Color de la insígnia",picture:"Imatge (reemplaçarà la icona)"},title:{title:"Títol",subtitle:"Subtítol",title_tap_action:"Acció en tocar el títol",subtitle_tap_action:"Acció en tocar el subtítol"},chips:{alignment:"Alineació"},weather:{show_conditions:"Condicions?",show_temperature:"Temperatura?"},update:{show_buttons_control:"Botons de control?"},vacuum:{commands:"Comandaments",commands_list:{on_off:"Engegar/Apagar"}},"media-player":{use_media_info:"Empra la informació multimèdia",use_media_artwork:"Fes servir l'art multimèdia",show_volume_level:"Mostra el nivell de volum",media_controls:"Controls multimèdia",media_controls_list:{on_off:"Engegar/Apagar",shuffle:"Mesclar",previous:"Pista anterior",play_pause_stop:"Reproduïr/Pausar/Detindre",next:"Pista següent",repeat:"Mode de repetició"},volume_controls:"Controls de volum",volume_controls_list:{volume_buttons:"Botons de volum",volume_set:"Nivell de volum",volume_mute:"Silenci"}},lock:{lock:"Bloqueja",unlock:"Desbloqueja",open:"Obri"},humidifier:{show_target_humidity_control:"Control d'humitat?"},climate:{show_temperature_control:"Control de temperatura?",hvac_modes:"Modes HVAC"},number:{display_mode:"Mode de visualització",display_mode_list:{default:"Per defecte (lliscant)",slider:"Lliscant",buttons:"Botons"}}},chip:{sub_element_editor:{title:"Editor de xips"},conditional:{chip:"Xip"},"chip-picker":{chips:"Xips",add:"Afegir xip",edit:"Editar",clear:"Buidar",select:"Seleccionar chip",types:{action:"Acció","alarm-control-panel":"Alarma",back:"Tornar",conditional:"Condicional",entity:"Entitat",light:"Llum",menu:"Menú",spacer:"Espai",template:"Plantilla",weather:"Oratge"}}}},hi={not_found:"No s'ha trobat l'entitat"},mi={editor:ui,card:hi},pi={form:{color_picker:{values:{default:"Výchozí barva"}},info_picker:{values:{default:"Výchozí informace",name:"Název",state:"Stav","last-changed":"Poslední změna","last-updated":"Poslední aktualizace",none:"Nic"}},icon_type_picker:{values:{default:"Výchozí typ",icon:"Ikona","entity-picture":"Ikona entity",none:"Nic"}},layout_picker:{values:{default:"Výchozí rozložení",vertical:"Svislé rozložení",horizontal:"Vodorovné rozložení"}},alignment_picker:{values:{default:"Výchozí zarovnání",start:"Na začátek",end:"Na konec",center:"Na střed",justify:"Do bloku"}}},card:{generic:{icon_color:"Barva ikony",layout:"Rozložení",fill_container:"Vyplnit prostor",primary_info:"Primární informace",secondary_info:"Sekundární informace",icon_type:"Typ ikony",content_info:"Obsah",use_entity_picture:"Použít ikonu entity?",collapsible_controls:"Pokud je vypnuto, skrýt ovládací prvky",icon_animation:"Pokud je aktivní, animovat ikonu?"},light:{show_brightness_control:"Ovládání jasu?",use_light_color:"Ikona podle barvy světla?",show_color_temp_control:"Ovládání teploty světla?",show_color_control:"Ovládání barvy světla?",incompatible_controls:"Některé ovládací prvky se nemusí zobrazit, pokud vaše světlo tuto funkci nepodporuje."},fan:{show_percentage_control:"Ovládání v procentech?",show_oscillate_control:"Ovládání oscilaceM"},cover:{show_buttons_control:"Zobrazit ovládací tlačítka?",show_position_control:"Zobrazit ovládání polohy?",show_tilt_position_control:"Zobrazit ovládání náklonu?"},template:{primary:"Primární informace",secondary:"Sekundární informace",multiline_secondary:"Víceřádková sekundární informace?",entity_extra:"Použito v šablonách a akcích",content:"Obsah",badge_icon:"Ikona odznaku",badge_color:"Barva odznaku",picture:"Obrázek (nahradí ikonu)"},title:{title:"Nadpis",subtitle:"Popis",title_tap_action:"Akce při klepnutí na nadpis",subtitle_tap_action:"Akce při klepnutí na popis"},chips:{alignment:"Zarovnání"},weather:{show_conditions:"Zobrazit podmínky?",show_temperature:"Zobrazit teplotu?"},update:{show_buttons_control:"Zobrazit ovládací tlačítka?"},vacuum:{commands:"Příkazy",commands_list:{on_off:"Zapnout/Vypnout"}},"media-player":{use_media_info:"Použít informace z média",use_media_artwork:"Použít artwork z média",show_volume_level:"Zobrazit úroveň hlasitosti",media_controls:"Ovládání médií",media_controls_list:{on_off:"Zapnout/Vypnout",shuffle:"Zamíchat",previous:"Předchozí stopa",play_pause_stop:"Přehrát/Pauza/Zastavit",next:"Další stopa",repeat:"Režim opakování"},volume_controls:"Ovládání hlasitosti",volume_controls_list:{volume_buttons:"Tlačítka hlasitosti",volume_set:"Úroveň hlasitosti",volume_mute:"Ztlumit"}},lock:{lock:"Zamčeno",unlock:"Odemčeno",open:"Otevřeno"},humidifier:{show_target_humidity_control:"Ovládání vlhkosti?"},climate:{show_temperature_control:"Ovládání teploty?",hvac_modes:"Režimy HVAC"},number:{display_mode:"Režim zobrazení",display_mode_list:{default:"Výchozí (posuvník)",slider:"Posuvník",buttons:"Tlačítka"}}},chip:{sub_element_editor:{title:"Editor tlačítek"},conditional:{chip:"Tlačítko"},"chip-picker":{chips:"Tlačítka",add:"Přidat tlačítko",edit:"Upravit",clear:"Vymazat",select:"Vybrat tlačítko",types:{action:"Akce","alarm-control-panel":"Alarm",back:"Zpět",conditional:"Podmínka",entity:"Entita",light:"Světlo",menu:"Menu",spacer:"Mezera",template:"Šablona",weather:"Počasí"}}}},fi={not_found:"Entita nebyla nalezena"},gi={editor:pi,card:fi},_i={form:{color_picker:{values:{default:"Standard farve"}},info_picker:{values:{default:"Standard information",name:"Navn",state:"Status","last-changed":"Sidst ændret","last-updated":"Sidst opdateret",none:"Ingen"}},icon_type_picker:{values:{default:"Standard type",icon:"Ikon","entity-picture":"Enheds billede",none:"Ingen"}},layout_picker:{values:{default:"Standard layout",vertical:"Vertikal layout",horizontal:"Horisontal layout"}},alignment_picker:{values:{default:"Standard justering",start:"Start",end:"Slut",center:"Centrer",justify:"Lige margener"}}},card:{generic:{icon_color:"Ikon farve",layout:"Layout",fill_container:"Fyld container",primary_info:"Primær information",secondary_info:"Sekundær information",icon_type:"Ikon type",content_info:"Indhold",use_entity_picture:"Brug enheds billede?",collapsible_controls:"Skjul kontroller når slukket",icon_animation:"Animér ikon når aktiv?"},light:{show_brightness_control:"Lysstyrkekontrol?",use_light_color:"Brug lysfarve",show_color_temp_control:"Temperatur farvekontrol?",show_color_control:"Farvekontrol?",incompatible_controls:"Nogle kontroller vises muligvis ikke, hvis dit lys ikke understøtter funktionen."},fan:{show_percentage_control:"Procentvis kontrol?",show_oscillate_control:"Oscillerende kontrol?"},cover:{show_buttons_control:"Betjeningsknapper?",show_position_control:"Positionskontrol?"},template:{primary:"Primær information",secondary:"Sekundær information",multiline_secondary:"Multi-linje skundær?",entity_extra:"Anvendes i skabelober og handlinger",content:"Indhold",badge_icon:"Badge ikon",badge_color:"Badge farve",picture:"Billede (erstatter ikonen)"},title:{title:"Titel",subtitle:"Undertitel"},chips:{alignment:"Justering"},weather:{show_conditions:"Forhold?",show_temperature:"Temperatur?"},update:{show_buttons_control:"Betjeningsknapper?"},vacuum:{commands:"Kommandoer"},"media-player":{use_media_info:"Brug medie info",use_media_artwork:"Brug mediebilleder",show_volume_level:"Vis volumen niveau",media_controls:"Medie kontrol",media_controls_list:{on_off:"Tænd/Sluk",shuffle:"Bland",previous:"Forrige nummer",play_pause_stop:"Afspil/Pause/Stop",next:"Næste nummer",repeat:"Gentagelsestilstand"},volume_controls:"Volumen kontrol",volume_controls_list:{volume_buttons:"Volumen knapper",volume_set:"Volumenniveau",volume_mute:"Lydløs"}},lock:{lock:"Lås",unlock:"Lås op",open:"Åben"},humidifier:{show_target_humidity_control:"Luftfugtigheds kontrol?"},climate:{show_temperature_control:"Temperatur kontrol?",hvac_modes:"HVAC-tilstande"}},chip:{sub_element_editor:{title:"Chip-editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Tilføj chip",edit:"Rediger",clear:"Nulstil",select:"Vælg chip",types:{action:"Handling","alarm-control-panel":"Alarm",back:"Tilbage",conditional:"Betinget",entity:"Enhed",light:"Lys",menu:"Menu",template:"Skabelon",weather:"Vejr"}}}},vi={editor:_i},bi={form:{color_picker:{values:{default:"Standardfarbe"}},info_picker:{values:{default:"Standard-Information",name:"Name",state:"Zustand","last-changed":"Letzte Änderung","last-updated":"Letzte Aktualisierung",none:"Keine"}},icon_type_picker:{values:{default:"Standard-Typ",icon:"Icon","entity-picture":"Entitätsbild",none:"Keines"}},layout_picker:{values:{default:"Standard-Layout",vertical:"Vertikales Layout",horizontal:"Horizontales Layout"}},alignment_picker:{values:{default:"Standard",start:"Anfang",end:"Ende",center:"Mitte",justify:"Ausrichten"}}},card:{generic:{icon_color:"Icon-Farbe",layout:"Layout",fill_container:"Container ausfüllen",primary_info:"Primäre Information",secondary_info:"Sekundäre Information",icon_type:"Icon-Typ",content_info:"Inhalt",use_entity_picture:"Entitätsbild verwenden?",collapsible_controls:"Schieberegler einklappen, wenn aus",icon_animation:"Icon animieren, wenn aktiv?"},light:{show_brightness_control:"Helligkeitsregelung?",use_light_color:"Farbsteuerung verwenden",show_color_temp_control:"Farbtemperatursteuerung?",show_color_control:"Farbsteuerung?",incompatible_controls:"Einige Steuerelemente werden möglicherweise nicht angezeigt, wenn Ihr Licht diese Funktion nicht unterstützt."},fan:{show_percentage_control:"Prozentuale Kontrolle?",show_oscillate_control:"Oszillationssteuerung?"},cover:{show_buttons_control:"Schaltflächensteuerung?",show_position_control:"Positionssteuerung?",show_tilt_position_control:"Winkelsteuerung?"},template:{primary:"Primäre Information",secondary:"Sekundäre Information",multiline_secondary:"Mehrzeilig sekundär?",entity_extra:"Wird in Vorlagen und Aktionen verwendet",content:"Inhalt",badge_icon:"Badge-Icon",badge_color:"Badge-Farbe",picture:"Bild (ersetzt das Icon)"},title:{title:"Titel",subtitle:"Untertitel",title_tap_action:"Titel Tipp-Aktion",subtitle_tap_action:"Untertitel Tipp-Aktion"},chips:{alignment:"Ausrichtung"},weather:{show_conditions:"Bedingungen?",show_temperature:"Temperatur?"},update:{show_buttons_control:"Schaltflächensteuerung?"},vacuum:{commands:"Befehle",commands_list:{on_off:"An/Ausschalten"}},"media-player":{use_media_info:"Medieninfos verwenden",use_media_artwork:"Mediengrafik verwenden",show_volume_level:"Lautstärke-Level anzeigen",media_controls:"Mediensteuerung",media_controls_list:{on_off:"Ein/Aus",shuffle:"Zufällige Wiedergabe",previous:"Vorheriger Titel",play_pause_stop:"Play/Pause/Stop",next:"Nächster Titel",repeat:"Wiederholen"},volume_controls:"Lautstärkesteuerung",volume_controls_list:{volume_buttons:"Lautstärke-Buttons",volume_set:"Lautstärke-Level",volume_mute:"Stumm"}},lock:{lock:"Verriegeln",unlock:"Entriegeln",open:"Öffnen"},humidifier:{show_target_humidity_control:"Luftfeuchtigkeitssteuerung?"},climate:{show_temperature_control:"Temperatursteuerung?",hvac_modes:"HVAC-Modi"},number:{display_mode:"Anzeigemodus",display_mode_list:{default:"Standard (Schieberegler)",slider:"Schieberegler",buttons:"Buttons"}}},chip:{sub_element_editor:{title:"Chip Editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Chip hinzufügen",edit:"Editieren",clear:"Löschen",select:"Chip auswählen",types:{action:"Aktion","alarm-control-panel":"Alarm",back:"Zurück",conditional:"Bedingung",entity:"Entität",light:"Licht",menu:"Menü",spacer:"Abstand",template:"Vorlage",weather:"Wetter"}}}},yi={not_found:"Entität nicht gefunden"},xi={editor:bi,card:yi},wi={form:{color_picker:{values:{default:"Προεπιλεγμένο χρώμα"}},info_picker:{values:{default:"Προεπιλεγμένες πληροφορίες",name:"Όνομα",state:"Κατάσταση","last-changed":"Τελευταία αλλαγή","last-updated":"Τελευταία ενημέρωση",none:"Τίποτα"}},layout_picker:{values:{default:"Προεπιλεγμένη διάταξη",vertical:"Κάθετη διάταξη",horizontal:"Οριζόντια διάταξη"}},alignment_picker:{values:{default:"Προεπιλεγμένη στοίχιση",start:"Στοίχιση αριστερά",end:"Στοίχιση δεξιά",center:"Στοίχιση στο κέντρο",justify:"Πλήρης στοίχιση"}}},card:{generic:{icon_color:"Χρώμα εικονιδίου",layout:"Διάταξη",primary_info:"Πρωτεύουσες πληροφορίες",secondary_info:"Δευτερεύουσες πληροφορίες",content_info:"Περιεχόμενο",use_entity_picture:"Χρήση εικόνας οντότητας;",icon_animation:"Κίνηση εικονιδίου όταν είναι ενεργό;"},light:{show_brightness_control:"Έλεγχος φωτεινότητας;",use_light_color:"Χρήση χρώματος φωτος",show_color_temp_control:"Έλεγχος χρώματος θερμοκρασίας;",show_color_control:"Έλεγχος χρώματος;",incompatible_controls:"Ορισμένα στοιχεία ελέγχου ενδέχεται να μην εμφανίζονται εάν το φωτιστικό σας δεν υποστηρίζει τη λειτουργία."},fan:{show_percentage_control:"Έλεγχος ποσοστού;",show_oscillate_control:"Έλεγχος ταλάντωσης;"},cover:{show_buttons_control:"Έλεγχος κουμπιών;",show_position_control:"Έλεγχος θέσης;"},template:{primary:"Πρωτεύουσες πληροφορίες",secondary:"Δευτερεύουσες πληροφορίες",multiline_secondary:"Δευτερεύουσες πολλαπλών γραμμών;",entity_extra:"Χρησιμοποιείται σε πρότυπα και ενέργειες",content:"Περιεχόμενο"},title:{title:"Τίτλος",subtitle:"Υπότιτλος"},chips:{alignment:"Ευθυγράμμιση"},weather:{show_conditions:"Συνθήκες;",show_temperature:"Θερμοκρασία;"},update:{show_buttons_control:"Έλεγχος κουμπιών;"},vacuum:{commands:"Εντολές"},"media-player":{use_media_info:"Χρήση πληροφοριών πολυμέσων",use_media_artwork:"Χρήση έργων τέχνης πολυμέσων",media_controls:"Έλεγχος πολυμέσων",media_controls_list:{on_off:"Ενεργοποίηση/απενεργοποίηση",shuffle:"Τυχαία σειρά",previous:"Προηγούμενο κομμάτι",play_pause_stop:"Αναπαραγωγή/παύση/διακοπή",next:"Επόμενο κομμάτι",repeat:"Λειτουργία επανάληψης"},volume_controls:"Χειριστήρια έντασης ήχου",volume_controls_list:{volume_buttons:"Κουμπιά έντασης ήχου",volume_set:"Επίπεδο έντασης ήχου",volume_mute:"Σίγαση"}}},chip:{sub_element_editor:{title:"Επεξεργαστής Chip"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Προσθήκη chip",edit:"Επεξεργασία",clear:"Καθαρισμός",select:"Επιλογή chip",types:{action:"Ενέργεια","alarm-control-panel":"Συναγερμός",back:"Πίσω",conditional:"Υπό προϋποθέσεις",entity:"Οντότητα",light:"Φως",menu:"Μενού",template:"Πρότυπο",weather:"Καιρός"}}}},ki={editor:wi},Ci={form:{color_picker:{values:{default:"Default color"}},info_picker:{values:{default:"Default information",name:"Name",state:"State","last-changed":"Last Changed","last-updated":"Last Updated",none:"None"}},icon_type_picker:{values:{default:"Default type",icon:"Icon","entity-picture":"Entity picture",none:"None"}},layout_picker:{values:{default:"Default layout",vertical:"Vertical layout",horizontal:"Horizontal layout"}},alignment_picker:{values:{default:"Default alignment",start:"Start",end:"End",center:"Center",justify:"Justify"}}},card:{generic:{color:"Color",icon_color:"Icon color",layout:"Layout",fill_container:"Fill container",primary_info:"Primary information",secondary_info:"Secondary information",icon_type:"Icon type",content_info:"Content",use_entity_picture:"Use entity picture?",collapsible_controls:"Collapse controls when off",icon_animation:"Animate icon when active?"},light:{show_brightness_control:"Brightness control?",use_light_color:"Use light color",show_color_temp_control:"Temperature color control?",show_color_control:"Color control?",incompatible_controls:"Some controls may not be displayed if your light does not support the feature."},fan:{show_percentage_control:"Percentage control?",show_oscillate_control:"Oscillate control?"},cover:{show_buttons_control:"Control buttons?",show_position_control:"Position control?",show_tilt_position_control:"Tilt control?"},template:{primary:"Primary information",secondary:"Secondary information",multiline_secondary:"Multiline secondary?",entity_extra:"Used in templates and actions",label:"Label",content:"Content",badge_icon:"Badge icon",badge_color:"Badge color",picture:"Picture (will replace the icon)"},title:{title:"Title",subtitle:"Subtitle",title_tap_action:"Title tap action",subtitle_tap_action:"Subtitle tap action"},chips:{alignment:"Alignment"},weather:{show_conditions:"Conditions?",show_temperature:"Temperature?"},update:{show_buttons_control:"Control buttons?"},vacuum:{commands:"Commands",commands_list:{on_off:"Turn on/off"}},"media-player":{use_media_info:"Use media info",use_media_artwork:"Use media artwork",show_volume_level:"Show volume level",media_controls:"Media controls",media_controls_list:{on_off:"Turn on/off",shuffle:"Shuffle",previous:"Previous track",play_pause_stop:"Play/pause/stop",next:"Next track",repeat:"Repeat mode"},volume_controls:"Volume controls",volume_controls_list:{volume_buttons:"Volume buttons",volume_set:"Volume level",volume_mute:"Mute"}},lock:{lock:"Lock",unlock:"Unlock",open:"Open"},humidifier:{show_target_humidity_control:"Humidity control?"},climate:{show_temperature_control:"Temperature control?",hvac_modes:"HVAC Modes"},number:{display_mode:"Display Mode",display_mode_list:{default:"Default (slider)",slider:"Slider",buttons:"Buttons"}}},chip:{sub_element_editor:{title:"Chip editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Add chip",edit:"Edit",clear:"Clear",select:"Select chip",types:{action:"Action","alarm-control-panel":"Alarm",back:"Back",conditional:"Conditional",entity:"Entity",light:"Light",menu:"Menu",spacer:"Spacer",template:"Template",weather:"Weather"}}}},$i={not_found:"Entity not found"},Ei={editor:Ci,card:$i},Ai={form:{color_picker:{values:{default:"Color predeterminado"}},info_picker:{values:{default:"Información predeterminada",name:"Nombre",state:"Estado","last-changed":"Último cambio","last-updated":"Última actualización",none:"Ninguno"}},icon_type_picker:{values:{default:"Por defecto",icon:"Icono","entity-picture":"Imagen de entidad",none:"Ninguno"}},layout_picker:{values:{default:"Diseño predeterminado",vertical:"Diseño vertical",horizontal:"Diseño horizontal"}},alignment_picker:{values:{default:"Alineación predeterminada",start:"Inicio",end:"Final",center:"Centrado",justify:"Justificado"}}},card:{generic:{icon_color:"Color de icono",layout:"Diseño",fill_container:"Rellenar",primary_info:"Información primaria",secondary_info:"Información secundaria",icon_type:"Icono",content_info:"Contenido",use_entity_picture:"¿Usar imagen de entidad?",collapsible_controls:"Contraer controles cuando está apagado",icon_animation:"¿Icono animado cuando está activo?"},light:{show_brightness_control:"¿Controlar brillo?",use_light_color:"Usar color de la luz",show_color_temp_control:"¿Controlar temperatura del color?",show_color_control:"¿Controlar color?",incompatible_controls:"Es posible que algunos controles no se muestren si la luz no es compatible con esta función."},fan:{show_percentage_control:"¿Controlar porcentaje?",show_oscillate_control:"¿Controlar oscilación?"},cover:{show_buttons_control:"¿Botones de control?",show_position_control:"¿Control de posición?",show_tilt_position_control:"¿Control de inclinación?"},template:{primary:"Información primaria",secondary:"Información secundaria",multiline_secondary:"¿Secundaria multilínea?",entity_extra:"Utilizado en plantillas y acciones.",content:"Contenido",badge_icon:"Icono del distintivo",badge_color:"Color del distintivo",picture:"Imagen (sustituirá al icono)"},title:{title:"Título",subtitle:"Subtítulo",title_tap_action:"Acción al tocar el título",subtitle_tap_action:"Acción al tocar el subtítulo"},chips:{alignment:"Alineación"},weather:{show_conditions:"¿Condiciones?",show_temperature:"¿Temperatura?"},update:{show_buttons_control:"¿Botones de control?"},vacuum:{commands:"Comandos",commands_list:{on_off:"Activar/desactivar"}},"media-player":{use_media_info:"Usar información multimedia",use_media_artwork:"Usar ilustraciones multimedia",show_volume_level:"Mostrar nivel de volumen",media_controls:"Controles multimedia",media_controls_list:{on_off:"Activar/desactivar",shuffle:"Aleatoria",previous:"Pista anterior",play_pause_stop:"Reproducir/pausa/parar",next:"Pista siguiente",repeat:"Modo de repetición"},volume_controls:"Controles de volumen",volume_controls_list:{volume_buttons:"Botones de volumen",volume_set:"Nivel de volumen",volume_mute:"Silenciar"}},lock:{lock:"Bloquear",unlock:"Desbloquear",open:"Abrir"},humidifier:{show_target_humidity_control:"¿Controlar humedad?"},climate:{show_temperature_control:"¿Control de temperatura?",hvac_modes:"Modos de climatización"}},chip:{sub_element_editor:{title:"Editor de chip"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Añadir chip",edit:"Editar",clear:"Limpiar",select:"Seleccionar chip",types:{action:"Acción","alarm-control-panel":"Alarma",back:"Volver",conditional:"Condicional",entity:"Entidad",light:"Luz",menu:"Menú",template:"Plantilla",weather:"Clima"}}}},Si={editor:Ai},Ii={form:{color_picker:{values:{default:"Oletusväri"}},info_picker:{values:{default:"Oletustiedot",name:"Nimi",state:"Tila","last-changed":"Viimeksi muuttunut","last-updated":"Viimeksi päivittynyt",none:"Ei mitään"}},icon_type_picker:{values:{default:"Oletustyyppi",icon:"Kuvake","entity-picture":"Kohteen kuva",none:"Ei mitään"}},layout_picker:{values:{default:"Oletusasettelu",vertical:"Pystysuuntainen",horizontal:"Vaakasuuntainen"}},alignment_picker:{values:{default:"Keskitys",start:"Alku",end:"Loppu",center:"Keskitä",justify:"Sovita"}}},card:{generic:{icon_color:"Ikonin väri",layout:"Asettelu",fill_container:"Täytä alue",primary_info:"Ensisijaiset tiedot",secondary_info:"Toissijaiset tiedot",icon_type:"Kuvakkeen tyyppi",content_info:"Sisältö",use_entity_picture:"Käytä kohteen kuvaa?",collapsible_controls:"Piilota toiminnot off-tilassa",icon_animation:"Animoi kuvake, kun aktiivinen?"},light:{show_brightness_control:"Kirkkauden säätö?",use_light_color:"Käytä valaisimen väriä",show_color_temp_control:"Värilämpötilan säätö?",show_color_control:"Värin säätö?",incompatible_controls:"Jotkin toiminnot eivät näy, jos valaisimesi ei tue niitä."},fan:{show_percentage_control:"Prosentuaalinen säätö?",show_oscillate_control:"Oskillaation säätö?"},cover:{show_buttons_control:"Toimintopainikkeet?",show_position_control:"Sijainnin hallinta?"},template:{primary:"Ensisijaiset tiedot",secondary:"Toissijaiset tiedot",multiline_secondary:"Monirivinen toissijainen tieto?",entity_extra:"Käytetään malleissa ja toiminnoissa",content:"Sisältö",badge_icon:"Merkin kuvake",badge_color:"Merkin väri",picture:"Kuva (korvaa kuvakkeen)"},title:{title:"Otsikko",subtitle:"Tekstitys"},chips:{alignment:"Asettelu"},weather:{show_conditions:"Ehdot?",show_temperature:"Lämpötila?"},update:{show_buttons_control:"Toimintopainikkeet?"},vacuum:{commands:"Komennot"},"media-player":{use_media_info:"Käytä median tietoja",use_media_artwork:"Käytä median kuvituksia",show_volume_level:"Näytä äänenvoimakkuuden hallinta",media_controls:"Toiminnot",media_controls_list:{on_off:"Päälle/pois",shuffle:"Sekoita",previous:"Edellinen kappale",play_pause_stop:"Toista/keskeytä/pysäytä",next:"Seuraava kappale",repeat:"Jatkuva toisto"},volume_controls:"Äänenvoimakkuuden hallinta",volume_controls_list:{volume_buttons:"Äänenvoimakkuuspainikkeet",volume_set:"Äänenvoimakkuus",volume_mute:"Mykistä"}},lock:{lock:"Lukitse",unlock:"Poista lukitus",open:"Avaa"},humidifier:{show_target_humidity_control:"Kosteudenhallinta?"}},chip:{sub_element_editor:{title:"Merkkieditori"},conditional:{chip:"Merkki"},"chip-picker":{chips:"Merkit",add:"Lisää merkki",edit:"Muokkaa",clear:"Tyhjennä",select:"Valitse merkki",types:{action:"Toiminto","alarm-control-panel":"Hälytys",back:"Takaisin",conditional:"Ehdollinen",entity:"Kohde",light:"Valaisin",menu:"Valikko",template:"Malli",weather:"Sää"}}}},Ti={editor:Ii},zi={form:{color_picker:{values:{default:"Couleur par défaut"}},info_picker:{values:{default:"Information par défaut",name:"Nom",state:"État","last-changed":"Dernière modification","last-updated":"Dernière mise à jour",none:"Aucune"}},icon_type_picker:{values:{default:"Type par défaut",icon:"Icône","entity-picture":"Image de l'entité",none:"Aucune"}},layout_picker:{values:{default:"Disposition par défault",vertical:"Disposition verticale",horizontal:"Disposition horizontale"}},alignment_picker:{values:{default:"Alignement par défaut",start:"Début",end:"Fin",center:"Centré",justify:"Justifié"}}},card:{generic:{icon_color:"Couleur de l'icône",layout:"Disposition",fill_container:"Remplir le conteneur",primary_info:"Information principale",secondary_info:"Information secondaire",icon_type:"Type d'icône",content_info:"Contenu",use_entity_picture:"Utiliser l'image de l'entité ?",collapsible_controls:"Reduire les contrôles quand éteint",icon_animation:"Animation de l'icône ?"},light:{show_brightness_control:"Contrôle de luminosité ?",use_light_color:"Utiliser la couleur de la lumière",show_color_temp_control:"Contrôle de la température ?",show_color_control:"Contrôle de la couleur ?",incompatible_controls:"Certains contrôles peuvent ne pas être affichés si votre lumière ne supporte pas la fonctionnalité."},fan:{show_percentage_control:"Contrôle de la vitesse ?",show_oscillate_control:"Contrôle de l'oscillation ?"},cover:{show_buttons_control:"Contrôle avec boutons ?",show_position_control:"Contrôle de la position ?"},template:{primary:"Information principale",secondary:"Information secondaire",multiline_secondary:"Information secondaire sur plusieurs lignes ?",entity_extra:"Utilisée pour les templates et les actions",content:"Contenu",badge_icon:"Icône du badge",badge_color:"Couleur du badge",picture:"Picture (remplacera l'icône)"},title:{title:"Titre",subtitle:"Sous-titre",title_tap_action:"Appui sur le titre",subtitle_tap_action:"Appui sur le sous-titre"},chips:{alignment:"Alignement"},weather:{show_conditons:"Conditions ?",show_temperature:"Température ?"},update:{show_buttons_control:"Contrôle avec boutons ?"},vacuum:{commands:"Commandes",commands_list:{on_off:"Allumer/Éteindre"}},"media-player":{use_media_info:"Utiliser les informations du media",use_media_artwork:"Utiliser l'illustration du media",show_volume_level:"Afficher le niveau de volume",media_controls:"Contrôles du media",media_controls_list:{on_off:"Allumer/Éteindre",shuffle:"Lecture aléatoire",previous:"Précédent",play_pause_stop:"Lecture/pause/stop",next:"Suivant",repeat:"Mode de répétition"},volume_controls:"Contrôles du volume",volume_controls_list:{volume_buttons:"Bouton de volume",volume_set:"Niveau de volume",volume_mute:"Muet"}},lock:{lock:"Verrouiller",unlock:"Déverrouiller",open:"Ouvrir"},humidifier:{show_target_humidity_control:"Contrôle d'humidité ?"},climate:{show_temperature_control:"Contrôle de la température?",hvac_modes:"Modes du thermostat"},number:{display_mode:"Mode d'affichage",display_mode_list:{default:"Par défaut (Curseur)",slider:"Curseur",buttons:"Boutons"}}},chip:{sub_element_editor:{title:'Éditeur de "chip"'},conditional:{chip:"Chip"},"chip-picker":{chips:'"Chips"',add:'Ajouter une "chip"',edit:"Modifier",clear:"Effacer",select:'Sélectionner une "chip"',types:{action:"Action","alarm-control-panel":"Alarme",back:"Retour",conditional:"Conditionnel",entity:"Entité",light:"Lumière",menu:"Menu",spacer:"Espacement",template:"Template",weather:"Météo"}}}},Oi={editor:zi},Mi={form:{color_picker:{values:{default:"צבע ברירת מחדל"}},info_picker:{values:{default:"מידע ברירת מחדל",name:"שם",state:"מצב","last-changed":"שונה לאחרונה","last-updated":"עודכן לאחרונה",none:"ריק"}},layout_picker:{values:{default:"סידור ברירת מחדל",vertical:"סידור מאונך",horizontal:"סידור מאוזן"}},alignment_picker:{values:{default:"יישור ברירת מחדל",start:"התחלה",end:"סוף",center:"אמצע",justify:"מוצדק"}}},card:{generic:{icon_color:"צבע אייקון",layout:"סידור",fill_container:"מלא גבולות",primary_info:"מידע ראשי",secondary_info:"מידע מישני",content_info:"תוכן",use_entity_picture:"השתמש בתמונת ישות",collapsible_controls:"הסתר שליטה כשאר מכובה",icon_animation:"הנפש אייקון"},light:{show_brightness_control:"שליטה בבהירות?",use_light_color:"השתמש בצבע האור",show_color_temp_control:"הצג פקד גוון תאורה?",show_color_control:"הצג פקד צבע",incompatible_controls:"יתכן וחלק מהכפתורים לא יופיעו אם התאורה אינה תומכת בתכונה."},fan:{show_percentage_control:"שליטה באחוז?",show_oscillate_control:"שליטה בהתנדנדות?"},cover:{show_buttons_control:"הצג כפתורי שליטה",show_position_control:"הצג פקדי מיקום"},template:{primary:"מידע ראשי",secondary:"מידע מישני",multiline_secondary:"מידע מישני רב קווי",entity_extra:"משמש בתבניות ופעולות",content:"תוכן"},title:{title:"כותרת",subtitle:"כתובית"},chips:{alignment:"יישור"},weather:{show_conditions:"הצג תנאים?",show_temperature:"הצג טמפרטורה?"},update:{show_buttons_control:"הצג כפתורי שליטה?"},vacuum:{commands:"פקודות",icon_animation:"הנפשת אייקון"},"media-player":{use_media_info:"השתמש במידע מדיה",use_media_artwork:"השתמש באומנות מדיה",show_volume_level:"הצג שליטת ווליום",media_controls:"שליטה במדיה",media_controls_list:{on_off:"הדלק/כבה",shuffle:"ערבב",previous:"רצועה קודמת",play_pause_stop:"נגן/השהה/הפסק",next:"רצועה הבאה",repeat:"חזרה"},volume_controls:"שליטה בווליום",volume_controls_list:{volume_buttons:"כפתורי ווליום",volume_set:"רמת ווליום",volume_mute:"השתק"}},lock:{lock:"נעל",unlock:"בטל נעילה",open:"פתח"},humidifier:{show_target_humidity_control:"הצג פקדי לחות"}},chip:{sub_element_editor:{title:"עורך שבב"},conditional:{chip:"שבב"},"chip-picker":{chips:"שבבים",add:"הוסף שבב",edit:"ערוך",clear:"נקה",select:"בחר שבב",types:{action:"פעולה","alarm-control-panel":"אזעקה",back:"חזור",conditional:"מותנה",entity:"ישות",light:"אור",menu:"תפריט",template:"תבנית",weather:"מזג אוויר"}}}},ji={editor:Mi},Di={form:{color_picker:{values:{default:"Alapértelmezett szín"}},info_picker:{values:{default:"Alepértelmezett információ",name:"Név",state:"Állapot","last-changed":"Utoljára módosítva","last-updated":"Utoljára frissítve",none:"Egyik sem"}},icon_type_picker:{values:{default:"Alapértelmezett típus",icon:"Ikon","entity-picture":"Entitás kép",none:"Egyik sem"}},layout_picker:{values:{default:"Alapértelmezet elrendezés",vertical:"Függőleges elrendezés",horizontal:"Vízszintes elrendezés"}},alignment_picker:{values:{default:"Alapértelmezett rendezés",start:"Kezdete",end:"Vége",center:"Közepe",justify:"Sorkizárt"}}},card:{generic:{icon_color:"Ikon szín",layout:"Elrendezés",fill_container:"Tároló kitöltése",primary_info:"Elsődleges információ",secondary_info:"Másodlagos információ",icon_type:"Ikon típus",content_info:"Tartalom",use_entity_picture:"Entitás kép használata",collapsible_controls:"Vezérlők összezárása kikapcsolt állapotban",icon_animation:"Ikon animálása aktív állapotban"},light:{show_brightness_control:"Fényerő vezérlő",use_light_color:"Fény szín használata",show_color_temp_control:"Színhőmérséklet vezérlő",show_color_control:"Szín vezérlő",incompatible_controls:"Azok a vezérlők nem lesznek megjelenítve, amelyeket a fényforrás nem támogat."},fan:{show_percentage_control:"Százalékos vezérlő",show_oscillate_control:"Oszcilláció vezérlő"},cover:{show_buttons_control:"Vezérlő gombok",show_position_control:"Pozíció vezérlő",show_tilt_position_control:"Dőlésszög szabályzó"},template:{primary:"Elsődleges információ",secondary:"Másodlagos információ",multiline_secondary:"Másodlagost több sorba?",entity_extra:"Műveletek és sablonok használatakor",content:"Tartalom",badge_icon:"Jelvény ikon",badge_color:"Jelvény szín",picture:"Kép (lecseréli az ikont)"},title:{title:"Fejléc",subtitle:"Alcím",title_tap_action:"Fejlécre koppintáskor",subtitle_tap_action:"Alcímre koppintáskor"},chips:{alignment:"Rendezés"},weather:{show_conditions:"Állapotok",show_temperature:"Hőmérséklet"},update:{show_buttons_control:"Vezérlő gombok"},vacuum:{commands:"Utasítások",commands_list:{on_off:"Ki/Bekapcsolás"}},"media-player":{use_media_info:"Média infó használata",use_media_artwork:"Média borító használata",show_volume_level:"Hangerő mutatása",media_controls:"Média vezérlők",media_controls_list:{on_off:"Ki/bekapcsolás",shuffle:"Véletlen lejátszás",previous:"Előző szám",play_pause_stop:"Lejátszás/szünet/állj",next:"Következő szám",repeat:"Ismétlés módja"},volume_controls:"Hangerő vezérlők",volume_controls_list:{volume_buttons:"Hangerő gombok",volume_set:"Hangerő szint",volume_mute:"Némítás"}},lock:{lock:"Zár",unlock:"Nyit",open:"Nyitva"},humidifier:{show_target_humidity_control:"Páratartalom vezérlő"},climate:{show_temperature_control:"Hőmérséklet vezérlő",hvac_modes:"HVAC mód"},number:{display_mode:"Megjelenítési mód",display_mode_list:{default:"Alepértelmezett (csúszka)",slider:"Csúszka",buttons:"Gombok"}}},chip:{sub_element_editor:{title:"Chip szerkesztő"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chip-ek",add:"Chip hozzáadása",edit:"Szerkesztés",clear:"Ürítés",select:"Chip kiválasztása",types:{action:"Művelet","alarm-control-panel":"Riasztó",back:"Vissza",conditional:"Feltételes",entity:"Entitás",light:"Fényforrás",menu:"Menü",spacer:"Térköz",template:"Sablon",weather:"Időjárás"}}}},Li={not_found:"Entitás nem található"},Pi={editor:Di,card:Li},Ni={form:{color_picker:{values:{default:"Warna bawaan"}},info_picker:{values:{default:"Informasi bawaan",name:"Nama",state:"Status","last-changed":"Terakhir Diubah","last-updated":"Terakhir Diperbarui",none:"Tidak ada"}},icon_type_picker:{values:{default:"Tipe bawaan",icon:"Ikon","entity-picture":"Gambar entitas",none:"Tidak ada"}},layout_picker:{values:{default:"Tata letak bawaan",vertical:"Tata letak vertikal",horizontal:"Tata letak horizontal"}},alignment_picker:{values:{default:"Perataan bawaan",start:"Awal",end:"Akhir",center:"Tengah",justify:"Rata kanan-kiri"}}},card:{generic:{icon_color:"Warna ikon",layout:"Tata letak",fill_container:"Isi kontainer",primary_info:"Informasi primer",secondary_info:"Informasi sekunder",icon_type:"Tipe ikon",content_info:"Konten",use_entity_picture:"Gunakan gambar entitas?",collapsible_controls:"Sembunyikan kontrol saat mati",icon_animation:"Animasikan ikon saat aktif?"},light:{show_brightness_control:"Kontrol kecerahan?",use_light_color:"Gunakan warna lampu",show_color_temp_control:"Kontrol suhu warna?",show_color_control:"Kontrol warna?",incompatible_controls:"Beberapa kontrol mungkin tidak ditampilkan jika lampu Anda tidak mendukung fitur tersebut."},fan:{show_percentage_control:"Kontrol persentase?",show_oscillate_control:"Kontrol osilasi?"},cover:{show_buttons_control:"Tombol kontrol?",show_position_control:"Kontrol posisi?",show_tilt_position_control:"Kontrol kemiringan?"},template:{primary:"Informasi primer",secondary:"Informasi sekunder",multiline_secondary:"Info sekunder multibaris?",entity_extra:"Digunakan dalam templat dan tindakan",content:"Konten",badge_icon:"Ikon lencana",badge_color:"Warna lencana",picture:"Gambar (akan menggantikan ikon)"},title:{title:"Judul",subtitle:"Subjudul",title_tap_action:"Tindakan ketuk judul",subtitle_tap_action:"Tindakan ketuk subjudul"},chips:{alignment:"Perataan"},weather:{show_conditions:"Kondisi?",show_temperature:"Suhu?"},update:{show_buttons_control:"Tombol kontrol?"},vacuum:{commands:"Perintah",commands_list:{on_off:"Nyalakan/Matikan"}},"media-player":{use_media_info:"Gunakan info media",use_media_artwork:"Gunakan gambar seni media",show_volume_level:"Tampilkan level volume",media_controls:"Kontrol media",media_controls_list:{on_off:"Nyalakan/Matikan",shuffle:"Acak",previous:"Lagu sebelumnya",play_pause_stop:"Putar/jeda/stop",next:"Lagu berikutnya",repeat:"Mode pengulangan"},volume_controls:"Kontrol volume",volume_controls_list:{volume_buttons:"Tombol volume",volume_set:"Level volume",volume_mute:"Bisukan"}},lock:{lock:"Kunci",unlock:"Buka kunci",open:"Buka"},humidifier:{show_target_humidity_control:"Kontrol kelembapan?"},climate:{show_temperature_control:"Kontrol suhu?",hvac_modes:"Mode HVAC"},number:{display_mode:"Mode Tampilan",display_mode_list:{default:"Bawaan (geser)",slider:"Geser",buttons:"Tombol"}}},chip:{sub_element_editor:{title:"Editor cip"},conditional:{chip:"Cip"},"chip-picker":{chips:"Cip",add:"Tambah cip",edit:"Edit",clear:"Hapus",select:"Pilih cip",types:{action:"Tindakan","alarm-control-panel":"Alarm",back:"Kembali",conditional:"Kondisional",entity:"Entitas",light:"Lampu",menu:"Menu",spacer:"Pemisah",template:"Templat",weather:"Cuaca"}}}},Ri={not_found:"Entitas tidak ditemukan"},Fi={editor:Ni,card:Ri},Vi={form:{color_picker:{values:{default:"Colore predefinito"}},info_picker:{values:{default:"Informazione predefinita",name:"Nome",state:"Stato","last-changed":"Ultimo cambiamento","last-updated":"Ultimo aggiornamento",none:"Nessuno"}},icon_type_picker:{values:{default:"Tipo predefinito",icon:"Icona","entity-picture":"Immagine dell'entità",none:"Nessuna"}},layout_picker:{values:{default:"Disposizione predefinita",vertical:"Disposizione verticale",horizontal:"Disposizione orizzontale"}},alignment_picker:{values:{default:"Allineamento predefinito",start:"Inizio",end:"Fine",center:"Centro",justify:"Giustificato"}}},card:{generic:{icon_color:"Colore dell'icona",layout:"Disposizione",fill_container:"Riempi il contenitore",primary_info:"Informazione primaria",secondary_info:"Informazione secondaria",icon_type:"Tipo icona",content_info:"Contenuto",use_entity_picture:"Usa l'immagine dell'entità",collapsible_controls:"Nascondi i controlli quando spento",icon_animation:"Anima l'icona quando attiva"},light:{use_light_color:"Usa il colore della luce",show_brightness_control:"Controllo luminosità",show_color_temp_control:"Controllo temperatura",show_color_control:"Controllo colore",incompatible_controls:"Alcuni controlli potrebbero non essere mostrati se la tua luce non li supporta."},fan:{show_percentage_control:"Controllo potenza",show_oscillate_control:"Controllo oscillazione"},cover:{show_buttons_control:"Pulsanti di controllo",show_position_control:"Controllo percentuale apertura",show_tilt_position_control:"Controllo percentuale inclinazione"},template:{primary:"Informazione primaria",secondary:"Informazione secondaria",multiline_secondary:"Abilita frasi multilinea",entity_extra:"Usato in templates ed azioni",content:"Contenuto",badge_icon:"Icona del badge",badge_color:"Colore del badge",picture:"Immagine (sostituirà l'icona)"},title:{title:"Titolo",subtitle:"Sottotitolo",title_tap_action:"Azione di tap sul titolo",subtitle_tap_action:"Azione di tap sul sottotitolo"},chips:{alignment:"Allineamento"},weather:{show_conditions:"Condizioni",show_temperature:"Temperatura"},update:{show_buttons_control:"Pulsanti di controllo"},vacuum:{commands:"Comandi",commands_list:{on_off:"Accendi/Spegni"}},"media-player":{use_media_info:"Mostra le informazioni della sorgente",use_media_artwork:"Usa la copertina della sorgente",show_volume_level:"Mostra volume",media_controls:"Controlli media",media_controls_list:{on_off:"Accendi/Spegni",shuffle:"Riproduzione casuale",previous:"Traccia precedente",play_pause_stop:"Play/Pausa/Stop",next:"Traccia successiva",repeat:"Ciclo continuo"},volume_controls:"Controlli del Volume",volume_controls_list:{volume_buttons:"Bottoni del volume",volume_set:"Livello del volume",volume_mute:"Silenzia"}},lock:{lock:"Blocca",unlock:"Sblocca",open:"Aperto"},humidifier:{show_target_humidity_control:"Controllo umidità"},climate:{show_temperature_control:"Controllo della temperatura?",hvac_modes:"Modalità del termostato"},number:{display_mode:"Modalità di visualizzazione",display_mode_list:{default:"Predefinito (cursore)",slider:"Cursore",buttons:"Pulsanti"}}},chip:{sub_element_editor:{title:"Editor di chip"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Aggiungi chip",edit:"Modifica",clear:"Rimuovi",select:"Seleziona chip",types:{action:"Azione","alarm-control-panel":"Allarme",back:"Pulsante indietro",conditional:"Condizione",entity:"Entità",light:"Luce",menu:"Menù",spacer:"Distanziere",template:"Modello",weather:"Meteo"}}}},Bi={not_found:"Entità non trovata"},Ui={editor:Vi,card:Bi},Hi={form:{color_picker:{values:{default:"기본 색"}},info_picker:{values:{default:"기본 정보",name:"이름",state:"상태","last-changed":"마지막 변경","last-updated":"마지막 업데이트",none:"없음"}},icon_type_picker:{values:{default:"기본 타입",icon:"아이콘","entity-picture":"엔티티 사진",none:"없음"}},layout_picker:{values:{default:"기본 레이아웃",vertical:"수직 레이아웃",horizontal:"수평 레이아웃"}},alignment_picker:{values:{default:"기본 정렬",start:"시작",end:"끝",center:"중앙",justify:"행 정렬"}}},card:{generic:{icon_color:"아이콘 색",layout:"레이아웃",fill_container:"콘테이너 채우기",primary_info:"기본 정보",secondary_info:"보조 정보",icon_type:"아이콘 타입",content_info:"내용 정보",use_entity_picture:"엔티티 사진 사용",collapsible_controls:"꺼져있을 때 컨트롤 접기",icon_animation:"활성화 시 아이콘 애니메이션 사용"},light:{show_brightness_control:"밝기 컨트롤 표시",use_light_color:"조명 색 사용",show_color_temp_control:"색 온도 컨트롤 표시",show_color_control:"색 컨트롤 표시",incompatible_controls:"조명이 기능을 지원하지 않는 경우 일부 컨트롤이 표시되지 않을 수 있습니다."},fan:{show_percentage_control:"퍼센트 컨트롤",show_oscillate_control:"오실레이트 컨트롤"},cover:{show_buttons_control:"컨트롤 버튼 표시",show_position_control:"위치 컨트롤 표시",show_tilt_position_control:"기울기 컨트롤 표시"},template:{primary:"기본 정보",secondary:"보조 정보",multiline_secondary:"Multiline secondary?",entity_extra:"템플릿 및 작업에 사용",content:"내용",badge_icon:"뱃지 아이콘",badge_color:"뱃지 색",picture:"그림 (아이콘 대체)"},title:{title:"제목",subtitle:"부제목",title_tap_action:"제목 탭 액션",subtitle_tap_action:"부제목 탭 액션"},chips:{alignment:"정렬"},weather:{show_conditions:"조건 표시",show_temperature:"온도 표시"},update:{show_buttons_control:"컨트롤 버튼 표시"},vacuum:{commands:"명령어",commands_list:{on_off:"켜기/끄기"}},"media-player":{use_media_info:"미디어 정보 사용",use_media_artwork:"미디어 아트워크 사용",show_volume_level:"볼륨 레벨 표시",media_controls:"미디어 컨트롤",media_controls_list:{on_off:"켜기/끄기",shuffle:"섞기",previous:"이전 트랙",play_pause_stop:"재생/일시 정지/정지",next:"다음 트랙",repeat:"반복 모드"},volume_controls:"볼륨 컨트롤",volume_controls_list:{volume_buttons:"볼륨 버튼",volume_set:"볼륨 레벨",volume_mute:"음소거"}},lock:{lock:"잠금",unlock:"잠금 해제",open:"열기"},humidifier:{show_target_humidity_control:"습도 조절 표시"},climate:{show_temperature_control:"온도 조절 표시",hvac_modes:"HVAC 모드"}},chip:{sub_element_editor:{title:"칩 에디터"},conditional:{chip:"칩"},"chip-picker":{chips:"칩",add:"칩 추가",edit:"수정",clear:"클리어",select:"칩 선택",types:{action:"액션","alarm-control-panel":"알람",back:"이전",conditional:"Conditional",entity:"엔티티",light:"조명",menu:"메뉴",template:"템플릿",weather:"날씨"}}}},Yi={editor:Hi},Wi={form:{color_picker:{values:{default:"Standard farge"}},info_picker:{values:{default:"Standard informasjon",name:"Navn",state:"Tilstand","last-changed":"Sist endret","last-updated":"Sist oppdatert",none:"Ingen"}},layout_picker:{values:{default:"Standardoppsett",vertical:"Vertikalt oppsett",horizontal:"Horisontalt oppsett"}},alignment_picker:{values:{default:"Standard justering",start:"Start",end:"Slutt",center:"Senter",justify:"Bekreft"}}},card:{generic:{icon_color:"Ikon farge",layout:"Oppsett",primary_info:"Primærinformasjon",secondary_info:"Sekundærinformasjon",content_info:"Innhold",use_entity_picture:"Bruk enhetsbilde?",icon_animation:"Animer ikon når aktivt?"},light:{show_brightness_control:"Lysstyrkekontroll?",use_light_color:"Bruk lys farge",show_color_temp_control:"Temperatur fargekontroll?",show_color_control:"Fargekontroll?",incompatible_controls:"Noen kontroller vises kanskje ikke hvis lyset ditt ikke støtter denne funksjonen."},fan:{show_percentage_control:"Prosentvis kontroll?",show_oscillate_control:"Oscillerende kontroll?"},cover:{show_buttons_control:"Kontollere med knapper?",show_position_control:"Posisjonskontroll?"},template:{primary:"Primærinformasjon",secondary:"Sekundærinformasjon",multiline_secondary:"Multiline sekundær?",entity_extra:"Brukes i maler og handlinger",content:"Inhold"},title:{title:"Tittel",subtitle:"Undertekst"},chips:{alignment:"Justering"},weather:{show_conditions:"Forhold?",show_temperature:"Temperatur?"},vacuum:{commands:"Kommandoer"}},chip:{sub_element_editor:{title:"Chip redaktør"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Legg til chip",edit:"Endre",clear:"Klare",select:"Velg chip",types:{action:"Handling","alarm-control-panel":"Alarm",back:"Tilbake",conditional:"Betinget",entity:"Entitet",light:"Lys",menu:"Meny",template:"Mal",weather:"Vær"}}}},Xi={editor:Wi},Ki={form:{color_picker:{values:{default:"Standaard kleur"}},info_picker:{values:{default:"Standaard informatie",name:"Naam",state:"Staat","last-changed":"Laatst gewijzigd","last-updated":"Laatst bijgewerkt",none:"Geen"}},icon_type_picker:{values:{default:"Standaard icoon type",icon:"Icoon","entity-picture":"Entiteit afbeelding",none:"Geen"}},layout_picker:{values:{default:"Standaard lay-out",vertical:"Verticale lay-out",horizontal:"Horizontale lay-out"}},alignment_picker:{values:{default:"Standaard uitlijning",start:"Begin",end:"Einde",center:"Midden",justify:"Uitlijnen "}}},card:{generic:{icon_color:"Icoon kleur",layout:"Lay-out",fill_container:"Vul container",primary_info:"Primaire informatie",secondary_info:"Secundaire informatie",icon_type:"Icoon type",content_info:"Inhoud",use_entity_picture:"Gebruik entiteit afbeelding",collapsible_controls:"Bedieningselementen verbergen wanneer uitgeschakeld",icon_animation:"Pictogram animeren indien actief"},light:{show_brightness_control:"Bediening helderheid",use_light_color:"Gebruik licht kleur",show_color_temp_control:"Bediening kleurtemperatuur",show_color_control:"Bediening kleur",incompatible_controls:"Sommige bedieningselementen worden mogelijk niet weergegeven als uw lamp deze functie niet ondersteunt."},fan:{show_percentage_control:"Bediening middels percentage",show_oscillate_control:"Bediening oscillatie"},cover:{show_buttons_control:"Toon knoppen",show_position_control:"Toon positie bediening",show_tilt_position_control:"Toon tilt control"},template:{primary:"Primaire informatie",secondary:"Secundaire informatie",multiline_secondary:"Secundaire informatie op meerdere lijnen weergeven",entity_extra:"Gebruikt in sjablonen en acties",content:"Inhoud",badge_icon:"Badge icoon",badge_color:"Badge kleur",picture:"Afbeelding (zal het icoon vervangen)"},title:{title:"Titel",subtitle:"Ondertitel",title_tap_action:"Titel tik actie",subtitle_tap_action:"Ondertitel tik actie"},chips:{alignment:"Uitlijning"},weather:{show_conditions:"Weerbeeld",show_temperature:"Temperatuur"},update:{show_buttons_control:"Bedieningsknoppen"},vacuum:{commands:"Commando's",commands_list:{on_off:"Zet aan/uit"}},"media-player":{use_media_info:"Gebruik media informatie",use_media_artwork:"Gebruik media omslag",show_volume_level:"Toon volumeniveau",media_controls:"Mediabediening",media_controls_list:{on_off:"zet aan/uit",shuffle:"Shuffle",previous:"Vorige nummer",play_pause_stop:"Speel/pauze/stop",next:"Volgende nummer",repeat:"Herhalen"},volume_controls:"Volumeregeling",volume_controls_list:{volume_buttons:"Volume knoppen",volume_set:"Volumeniveau",volume_mute:"Dempen"}},lock:{lock:"Vergrendel",unlock:"Ontgrendel",open:"Open"},humidifier:{show_target_humidity_control:"Vochtigheid controle?"},climate:{show_temperature_control:"Temperatuur controle",hvac_modes:"HVAC Modes"},number:{display_mode:"Weergave Modus",display_mode_list:{default:"Standaard (schuifbalk)",slider:"Schuifbalk",buttons:"Knoppen"}}},chip:{sub_element_editor:{title:"Chip editor"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Toevoegen chip",edit:"Bewerk",clear:"Maak leeg",select:"Selecteer chip",types:{action:"Actie","alarm-control-panel":"Alarm",back:"Terug",conditional:"Voorwaardelijk",entity:"Entiteit",light:"Licht",menu:"Menu",spacer:"Afstandhouder",template:"Sjabloon",weather:"Weer"}}}},Gi={not_found:"Entiteit niet gevonden"},qi={editor:Ki,card:Gi},Zi={form:{color_picker:{values:{default:"Domyślny kolor"}},info_picker:{values:{default:"Domyślne informacje",name:"Nazwa",state:"Stan","last-changed":"Ostatnia zmiana","last-updated":"Ostatnia aktualizacja",none:"Brak"}},icon_type_picker:{values:{default:"Domyślny typ",icon:"Ikona","entity-picture":"Obraz encji",none:"Brak"}},layout_picker:{values:{default:"Układ domyślny",vertical:"Układ pionowy",horizontal:"Układ poziomy"}},alignment_picker:{values:{default:"Wyrównanie domyślne",start:"Wyrównanie do lewej",end:"Wyrównanie do prawej",center:"Wyśrodkowanie",justify:"Justowanie"}}},card:{generic:{icon_color:"Kolor ikony",layout:"Układ",fill_container:"Wypełnij zawartością",primary_info:"Informacje główne",secondary_info:"Informacje drugorzędne",icon_type:"Typ ikony",content_info:"Zawartość",use_entity_picture:"Użyć obrazu encji?",collapsible_controls:"Zwiń sterowanie, jeśli wyłączone",icon_animation:"Animować, gdy aktywny?"},light:{show_brightness_control:"Sterowanie jasnością?",use_light_color:"Użyj koloru światła",show_color_temp_control:"Sterowanie temperaturą światła?",show_color_control:"Sterowanie kolorami?",incompatible_controls:"Niektóre funkcje są niewidoczne, jeśli światło ich nie obsługuje."},fan:{show_percentage_control:"Sterowanie procentowe?",show_oscillate_control:"Sterowanie oscylacją?"},cover:{show_buttons_control:"Przyciski sterujące?",show_position_control:"Sterowanie położeniem?",show_tilt_position_control:"Sterowanie poziomem otwarcia?"},template:{primary:"Informacje główne",secondary:"Informacje drugorzędne",multiline_secondary:"Drugorzędne wielowierszowe?",entity_extra:"Używane w szablonach i akcjach",content:"Zawartość",badge_icon:"Ikona odznaki",badge_color:"Kolor odznaki",picture:"Obraz (zamiast ikony)"},title:{title:"Tytuł",subtitle:"Podtytuł"},chips:{alignment:"Wyrównanie"},weather:{show_conditions:"Warunki?",show_temperature:"Temperatura?"},update:{show_buttons_control:"Przyciski sterujące?"},vacuum:{commands:"Polecenia"},"media-player":{use_media_info:"Użyj informacji o multimediach",use_media_artwork:"Użyj okładek multimediów",show_volume_level:"Wyświetl poziom głośności",media_controls:"Sterowanie multimediami",media_controls_list:{on_off:"Włącz/wyłącz",shuffle:"Losowo",previous:"Poprzednie nagranie",play_pause_stop:"Odtwórz/Pauza/Zatrzymaj",next:"Następne nagranie",repeat:"Powtarzanie"},volume_controls:"Sterowanie głośnością",volume_controls_list:{volume_buttons:"Przyciski głośności",volume_set:"Poziom głośności",volume_mute:"Wycisz"}},lock:{lock:"Zablokuj",unlock:"Odblokuj",open:"Otwórz"},humidifier:{show_target_humidity_control:"Sterowanie wilgotnością?"},climate:{show_temperature_control:"Sterowanie temperaturą?",hvac_modes:"Tryby urządzenia"}},chip:{sub_element_editor:{title:"Edytor czipów"},conditional:{chip:"Czip"},"chip-picker":{chips:"Czipy",add:"Dodaj czip",edit:"Edytuj",clear:"Wyczyść",select:"Wybierz czip",types:{action:"Akcja","alarm-control-panel":"Alarm",back:"Wstecz",conditional:"Warunkowy",entity:"Encja",light:"Światło",menu:"Menu",spacer:"Odstęp",template:"Szablon",weather:"Pogoda"}}}},Ji={editor:Zi},Qi={form:{color_picker:{values:{default:"Cor padrão"}},info_picker:{values:{default:"Informações padrão",name:"Nome",state:"Estado","last-changed":"Última alteração","last-updated":"Última atualização",none:"Nenhum"}},layout_picker:{values:{default:"Layout padrão",vertical:"Layout vertical",horizontal:"Layout horizontal"}},alignment_picker:{values:{default:"Padrão (inicio)",end:"Final",center:"Centro",justify:"Justificado"}}},card:{generic:{icon_color:"Cor do ícone?",layout:"Layout",primary_info:"Informações primárias",secondary_info:"Informações secundárias",use_entity_picture:"Usar imagem da entidade?",icon_animation:"Animar ícone quando ativo?"},light:{show_brightness_control:"Mostrar controle de brilho?",use_light_color:"Usar cor da luz?",show_color_temp_control:"Mostrar controle de temperatura?",show_color_control:"Mostrar controle de cor?",incompatible_controls:"Alguns controles podem não ser exibidos se sua luz não suportar o recurso."},fan:{show_percentage_control:"Mostrar controle de porcentagem?",show_oscillate_control:"Mostrar controle de oscilação?"},cover:{show_buttons_control:"Mostrar botões?",show_position_control:"Mostrar controle de posição?"},template:{primary:"Informações primárias",secondary:"Informações secundárias",multiline_secondary:"Multilinha secundária?",content:"Conteúdo"},title:{title:"Título",subtitle:"Subtítulo"},chips:{alignment:"Alinhamento"},weather:{show_conditions:"Condições?",show_temperature:"Temperatura?"}},chip:{sub_element_editor:{title:"Editor de fichas"},conditional:{chip:"Ficha"},"chip-picker":{chips:"Fichas",add:"Adicionar ficha",edit:"Editar",clear:"Limpar",select:"Selecionar ficha",types:{action:"Ação","alarm-control-panel":"Alarme",back:"Voltar",conditional:"Condicional",entity:"Entidade",light:"Iluminação",menu:"Menu",template:"Modelo",weather:"Clima"}}}},to={editor:Qi},eo={form:{color_picker:{values:{default:"Cor padrão"}},info_picker:{values:{default:"Informações padrão",name:"Nome",state:"Estado","last-changed":"Última alteração","last-updated":"Última atualização",none:"Nenhum"}},layout_picker:{values:{default:"Layout padrão",vertical:"Layout vertical",horizontal:"Layout horizontal"}},alignment_picker:{values:{default:"Padrão (inicio)",end:"Fim",center:"Centrado",justify:"Justificado"}}},card:{generic:{icon_color:"Cor do ícone?",layout:"Layout",primary_info:"Informações primárias",secondary_info:"Informações secundárias",use_entity_picture:"Usar imagem da entidade?",icon_animation:"Animar ícone quando ativo?"},light:{show_brightness_control:"Mostrar controle de brilho?",use_light_color:"Usar cor da luz?",show_color_temp_control:"Mostrar controle de temperatura?",show_color_control:"Mostrar controle de cor?",incompatible_controls:"Alguns controles podem não ser exibidos se a luz não suportar o recurso."},fan:{show_percentage_control:"Mostrar controle de porcentagem?",show_oscillate_control:"Mostrar controle de oscilação?"},cover:{show_buttons_control:"Mostrar botões?",show_position_control:"Mostrar controle de posição?"},template:{primary:"Informações primárias",secondary:"Informações secundárias",multiline_secondary:"Multilinha secundária?",content:"Conteúdo"},title:{title:"Título",subtitle:"Subtítulo"},chips:{alignment:"Alinhamento"},weather:{show_conditions:"Condições?",show_temperature:"Temperatura?"}},chip:{sub_element_editor:{title:"Editor de fichas"},conditional:{chip:"Ficha"},"chip-picker":{chips:"Fichas",add:"Adicionar ficha",edit:"Editar",clear:"Limpar",select:"Selecionar ficha",types:{action:"Ação","alarm-control-panel":"Alarme",back:"Voltar",conditional:"Condicional",entity:"Entidade",light:"Iluminação",menu:"Menu",template:"Modelo",weather:"Clima"}}}},io={editor:eo},oo={form:{color_picker:{values:{default:"Culoare implicită"}},info_picker:{values:{default:"Informație implicită",name:"Nume",state:"Stare","last-changed":"Ultima modificare","last-updated":"Ultima actulizare",none:"Niciuna"}},icon_type_picker:{values:{default:"Tip implicit",icon:"Pictogramă","entity-picture":"Imagine",none:"Niciuna"}},layout_picker:{values:{default:"Aranjare implicită",vertical:"Verticală",horizontal:"Orizontală"}},alignment_picker:{values:{default:"Aliniere implicită",start:"Stânga",end:"Dreapta",center:"Centrat",justify:"Umplere"}}},card:{generic:{icon_color:"Culoare pictogramă",layout:"Aranjare",fill_container:"Umplere container",primary_info:"Informație principală",secondary_info:"Informație secundară",icon_type:"Tip pictogramă",content_info:"Conținut",use_entity_picture:"Imagine?",collapsible_controls:"Restrângere la dezactivare"},light:{show_brightness_control:"Comandă pentru strălucire?",use_light_color:"Folosește culoarea luminii",show_color_temp_control:"Comandă pentru temperatură de culoare?",show_color_control:"Comandă pentru culoare?",incompatible_controls:"Unele comenzi ar putea să nu fie afișate dacă lumina nu suportă această caracteristică."},fan:{icon_animation:"Animare pictograma la activare?",show_percentage_control:"Comandă procent?",show_oscillate_control:"Comandă oscilație?"},cover:{show_buttons_control:"Comenzi pentru control?",show_position_control:"Comandă pentru poziție?",show_tilt_position_control:"Comandă pentru înclinare?"},template:{primary:"Informație principală",secondary:"Informație secundară",multiline_secondary:"Informație secundară pe mai multe linii?",entity_extra:"Folosită în șabloane și acțiuni",content:"Conținut",badge_icon:"Pictogramă insignă",badge_color:"Culoare insignă",picture:"Imagine (inlocuiește pictograma)"},title:{title:"Titlu",subtitle:"Subtitlu"},chips:{alignment:"Aliniere"},weather:{show_conditions:"Condiții?",show_temperature:"Temperatură?"},update:{show_buttons_control:"Comenzi control?"},vacuum:{commands:"Comenzi"},"media-player":{use_media_info:"Informații media",use_media_artwork:"Grafică media",show_volume_level:"Nivel volum",media_controls:"Comenzi media",media_controls_list:{on_off:"Pornit/Oprit",shuffle:"Amestecare",previous:"Pista anterioară",play_pause_stop:"Redare/Pauză/Stop",next:"Pista următoare",repeat:"Mod repetare"},volume_controls:"Comenzi volum",volume_controls_list:{volume_buttons:"Comenzi volum",volume_set:"Nivel volum",volume_mute:"Dezactivare sunet"}},lock:{lock:"Încuie",unlock:"Descuie",open:"Deschide"},humidifier:{show_target_humidity_control:"Comenzi umiditate?"},climate:{show_temperature_control:"Comenzi temperatură?",hvac_modes:"Moduri HVAC"}},chip:{sub_element_editor:{title:"Editor jeton"},conditional:{chip:"Jeton"},"chip-picker":{chips:"Jetoane",add:"Adaugă jeton",edit:"Modifică",clear:"Șterge",select:"Alege jeton",types:{action:"Acțiune","alarm-control-panel":"Alarmă",back:"Înapoi",conditional:"Condițional",entity:"Entitate",light:"Lumină",menu:"Meniu",template:"Șablon",weather:"Vreme"}}}},no={editor:oo},ro={form:{color_picker:{values:{default:"Цвет по умолчанию"}},info_picker:{values:{default:"По умолчанию",name:"Имя",state:"Статус","last-changed":"Последнее изменение","last-updated":"Последнее обновление",none:"Нет"}},icon_type_picker:{values:{default:"По умолчанию",icon:"Иконка","entity-picture":"Изображение",none:"Нет"}},layout_picker:{values:{default:"Расположение по умолчанию",vertical:"Вертикальное расположение",horizontal:"Горизонтальное расположение"}},alignment_picker:{values:{default:"Выравнивание по умолчанию",start:"К началу",end:"К концу",center:"По центру",justify:"На всю ширину"}}},card:{generic:{icon_color:"Цвет иконки",layout:"Расположение",fill_container:"Заполнение",primary_info:"Основная информация",secondary_info:"Второстепенная информация",icon_type:"Тип иконки",content_info:"Содержимое",use_entity_picture:"Использовать изображение объекта?",collapsible_controls:"Сворачивать элементы управления при выключении"},light:{show_brightness_control:"Управлять яркостью?",use_light_color:"Использовать текущий цвет света",show_color_temp_control:"Управлять цветовой температурой?",show_color_control:"Управлять цветом?",incompatible_controls:"Некоторые элементы управления могут не отображаться, если ваш светильник не поддерживает эти функции."},fan:{icon_animation:"Анимировать иконку когда включено?",show_percentage_control:"Управлять процентами?",show_oscillate_control:"Oscillate control?"},cover:{show_buttons_control:"Добавить кнопки управления?",show_position_control:"Управлять позицией?",show_tilt_position_control:"Управлять наклоном?"},template:{primary:"Основная информация",secondary:"Второстепенная информация",multiline_secondary:"Многострочная Второстепенная информация?",entity_extra:"Используется в шаблонах и действиях",content:"Содержимое",badge_icon:"Иконка значка",badge_color:"Цвет значка",picture:"Изображение (заменить иконку)"},title:{title:"Заголовок",subtitle:"Подзаголовок"},chips:{alignment:"Выравнивание"},weather:{show_conditions:"Условия?",show_temperature:"Температура?"},update:{show_buttons_control:"Кнопки управления?"},vacuum:{commands:"Команды"},"media-player":{use_media_info:"Использовать информацию с медиа-устройства",use_media_artwork:"Использовать обложку с медиа-устройства",show_volume_level:"Показать уровень громкости",media_controls:"Управление медиа-устройством",media_controls_list:{on_off:"Включение/выключение",shuffle:"Перемешивание",previous:"Предыдущий трек",play_pause_stop:"Воспроизведение/пауза/остановка",next:"Следующий трек",repeat:"Режим повтора"},volume_controls:"Регулятор громкости",volume_controls_list:{volume_buttons:"Кнопки громкости",volume_set:"Уровень громкости",volume_mute:"Без звука"}},lock:{lock:"Закрыто",unlock:"Разблокировано",open:"Открыто"},humidifier:{show_target_humidity_control:"Управлять целевым уровенем влажности?"},climate:{show_temperature_control:"Управлять целевой температурой?",hvac_modes:"Режимы работы"}},chip:{sub_element_editor:{title:"Редактор мини-карточек"},conditional:{chip:"Мини-карточка"},"chip-picker":{chips:"Мини-карточки",add:"Добавить мини-карточку",edit:"Изменить",clear:"Очистить",select:"Выбрать мини-карточку",types:{action:"Действие","alarm-control-panel":"Тревога",back:"Назад",conditional:"Условия",entity:"Объект",light:"Освещение",menu:"Меню",template:"Шаблон",weather:"Погода"}}}},ao={editor:ro},so={form:{color_picker:{values:{default:"Privzeta barva"}},info_picker:{values:{default:"Privzete informacije",name:"Naziv",state:"Stanje","last-changed":"Zadnja sprememba","last-updated":"Zadnja posodobitev",none:"Brez"}},icon_type_picker:{values:{default:"Privzeta vrsta",icon:"Ikona","entity-picture":"Slika entitete",none:"Brez"}},layout_picker:{values:{default:"Privzeta postavitev",vertical:"Vertikalna postavitev",horizontal:"Horizontalna postavitev"}},alignment_picker:{values:{default:"Privzeta poravnava",start:"Pričetek",end:"Konec",center:"Center",justify:"Poravnava"}}},card:{generic:{icon_color:"Barva ikone",layout:"Postavitev",fill_container:"Zapolnitev prostora",primary_info:"Primarna informacija",secondary_info:"Sekundarna informacija",icon_type:"Vrsta ikone",content_info:"Vsebina",use_entity_picture:"Uporabi sliko entitete?",collapsible_controls:"Strni kontrolnike, ko so izklopljeni",icon_animation:"Animacija ikone, ko je aktivna?"},light:{show_brightness_control:"Nadzor svetlosti?",use_light_color:"Uporabi svetlo barvo",show_color_temp_control:"Nadzor temperature barve?",show_color_control:"Nadzor barv?",incompatible_controls:"Nekateri kontrolniki morda ne bodo prikazani, če vaša luč ne podpira te funkcije."},fan:{show_percentage_control:"Kontrola v odstotkih?",show_oscillate_control:"Kontrola nihanja?"},cover:{show_buttons_control:"Gumbi za upravljanje?",show_position_control:"Nadzor položaja?",show_tilt_position_control:"Nadzor nagiba?"},template:{primary:"Primarna informacija",secondary:"Sekundarna informacija",multiline_secondary:"Večvrstični sekundarni?",entity_extra:"Uporablja se v predlogah in dejanjih",content:"Vsebina",badge_icon:"Ikona značke",badge_color:"Barva značke",picture:"Slika (nadomestila bo ikono)"},title:{title:"Naziv",subtitle:"Podnaslov",title_tap_action:"Dejanje dotika naslova",subtitle_tap_action:"Dejanje dotika podnapisov"},chips:{alignment:"Poravnava"},weather:{show_conditions:"Pogoji?",show_temperature:"Temperatura?"},update:{show_buttons_control:"Gumbi za upravljanje?"},vacuum:{commands:"Ukazi",commands_list:{on_off:"Vklop/izklop"}},"media-player":{use_media_info:"Uporabite informacije o medijih",use_media_artwork:"Uporabite medijsko umetniško delo",show_volume_level:"Pokaži raven glasnosti",media_controls:"Nadzor medijev",media_controls_list:{on_off:"Vklop/izklop",shuffle:"Naključno",previous:"Prejšnja skladba",play_pause_stop:"Predvajaj/pavza/ustavi",next:"Naslednja skladba",repeat:"Ponavljajoči način"},volume_controls:"Kontrole glasnosti",volume_controls_list:{volume_buttons:"Gumbi za glasnost",volume_set:"Raven glasnosti",volume_mute:"Tiho"}},lock:{lock:"Zaklepanje",unlock:"Odkleni",open:"Odprto"},humidifier:{show_target_humidity_control:"Nadzor vlažnosti?"},climate:{show_temperature_control:"Nadzor temperature?",hvac_modes:"HVAC načini"},number:{display_mode:"Način prikaza",display_mode_list:{default:"Privzeto (drsnik)",slider:"Drsnik",buttons:"Gumbi"}}},chip:{sub_element_editor:{title:"Urejevalnik čipov"},conditional:{chip:"Ćiš"},"chip-picker":{chips:"Čipi",add:"Dodaj čip",edit:"Uredi",clear:"Pobriši",select:"Izbira čipa",types:{action:"Dejanje","alarm-control-panel":"Alarm",back:"Nazaj",conditional:"Pogojno",entity:"Entiteta",light:"Svetloba",menu:"Meni",spacer:"Distančnik",template:"Predloga",weather:"Vreme"}}}},lo={not_found:"Entiteta ni najdena"},co={editor:so,card:lo},uo={form:{color_picker:{values:{default:"Predvolená farba"}},info_picker:{values:{default:"Predvolené informácie",name:"Názov",state:"Stav","last-changed":"Posledná zmena","last-updated":"Posledná aktualizácia",none:"Žiadna"}},icon_type_picker:{values:{default:"Predvolený typ",icon:"Ikona","entity-picture":"Obrázok entity",none:"Žiadny"}},layout_picker:{values:{default:"Predvolené rozloženie",vertical:"Zvislé rozloženie",horizontal:"Vodorovné rozloženie"}},alignment_picker:{values:{default:"Predvolené zarovnanie",start:"Začiatok",end:"Koniec",center:"Stred",justify:"Vyplniť"}}},card:{generic:{icon_color:"Farba ikony",layout:"Rozloženie",fill_container:"Vyplniť priestor",primary_info:"Základné info",secondary_info:"Doplnkové info",icon_type:"Typ ikony",content_info:"Obsah",use_entity_picture:"Použiť obrázok entity?",collapsible_controls:"Skryť ovládanie v stave VYP.",icon_animation:"Animovaná ikona v stave ZAP?"},light:{show_brightness_control:"Ovládanie jasu?",use_light_color:"Použiť farbu svetla",show_color_temp_control:"Ovládanie teploty?",show_color_control:"Ovládanie farby?",incompatible_controls:"Niektoré ovládacie prvky sa nemusia zobraziť, pokiaľ ich svetlo nepodporuje."},fan:{show_percentage_control:"Ovládanie rýchlosti v percentách?",show_oscillate_control:"Ovládanie oscilácie?"},cover:{show_buttons_control:"Zobraziť ovládacie tlačidlá?",show_position_control:"Ovládanie pozície?",show_tilt_position_control:"Ovládanie natočenia?"},template:{primary:"Základné info",secondary:"Doplnkové info",multiline_secondary:"Viacriadkové doplnkové info?",entity_extra:"Použitá v šablónach a akciách",content:"Obsah",badge_icon:"Ikona odznaku",badge_color:"Farba odznaku",picture:"Obrázok (nahrádza ikonu)"},title:{title:"Nadpis",subtitle:"Podnadpis",title_tap_action:"Akcia klepnutia na názov",subtitle_tap_action:"Akcia klepnutia na titulky"},chips:{alignment:"Zarovnanie"},weather:{show_conditions:"Zobraziť podmienky?",show_temperature:"Zobraziť teplotu?"},update:{show_buttons_control:"Zobraziť ovládacie tlačidlá?"},vacuum:{commands:"Príkazy",commands_list:{on_off:"Zapnúť/Vypnúť"}},"media-player":{use_media_info:"Použiť info o médiu",use_media_artwork:"Použiť obrázok z média",show_volume_level:"Zobraziť úroveň hlasitosti",media_controls:"Ovládanie média",media_controls_list:{on_off:"Zap / Vyp",shuffle:"Premiešať",previous:"Predchádzajúca",play_pause_stop:"Spustiť/pauza/stop",next:"Ďalšia",repeat:"Opakovať"},volume_controls:"Ovládanie hlasitosti",volume_controls_list:{volume_buttons:"Tlačidlá hlasitosti",volume_set:"Úroveň hlasitosti",volume_mute:"Stlmiť"}},lock:{lock:"Zamknuté",unlock:"Odomknuté",open:"Otvorené"},humidifier:{show_target_humidity_control:"Ovládanie vlhkosti?"},climate:{show_temperature_control:"Ovládanie teploty?",hvac_modes:"HVAC mód"},number:{display_mode:"Režim zobrazenia",display_mode_list:{default:"Predvolené (posúvač)",slider:"Posúvač",buttons:"Tlačidlá"}}},chip:{sub_element_editor:{title:"Editor štítkov"},conditional:{chip:"Štítok"},"chip-picker":{chips:"Štítky",add:"Pridať štítok",edit:"Editovať",clear:"Vymazať",select:"Vybrať štítok",types:{action:"Akcia","alarm-control-panel":"Alarm",back:"Späť",conditional:"Podmienené",entity:"Entita",light:"Svetlo",menu:"Menu",spacer:"Medzera",template:"Šablóna",weather:"Počasie"}}}},ho={not_found:"Entita nenájdená"},mo={editor:uo,card:ho},po={form:{color_picker:{values:{default:"Standardfärg"}},info_picker:{values:{default:"Förvald information",name:"Namn",state:"Status","last-changed":"Sist ändrad","last-updated":"Sist uppdaterad",none:"Ingen"}},layout_picker:{values:{default:"Standard",vertical:"Vertikal",horizontal:"Horisontell"}},alignment_picker:{values:{default:"Standard (början)",end:"Slutet",center:"Centrerad",justify:"Anpassa"}}},card:{generic:{icon_color:"Ikonens färg",layout:"Layout",primary_info:"Primär information",secondary_info:"Sekundär information",use_entity_picture:"Använd enheten bild?",icon_animation:"Animera ikonen när fläkten är på?"},light:{show_brightness_control:"Styr ljushet?",use_light_color:"Styr ljusets färg",show_color_temp_control:"Styr färgtemperatur?",show_color_control:"Styr färg?",incompatible_controls:"Kontroller som inte stöds av enheten kommer inte visas."},fan:{show_percentage_control:"Procentuell kontroll?",show_oscillate_control:"Kontroll för oscillera?"},cover:{show_buttons_control:"Visa kontrollknappar?",show_position_control:"Visa positionskontroll?"},template:{primary:"Primär information",secondary:"Sekundär information",multiline_secondary:"Sekundär med flera rader?",content:"Innehåll"},title:{title:"Rubrik",subtitle:"Underrubrik"},chips:{alignment:"Justering"},weather:{show_conditions:"Förhållanden?",show_temperature:"Temperatur?"}},chip:{sub_element_editor:{title:"Chipredigerare"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Lägg till chip",edit:"Redigera",clear:"Rensa",select:"Välj chip",types:{action:"Händelse","alarm-control-panel":"Alarm",back:"Bakåt",conditional:"Villkorad",entity:"Enhet",light:"Ljus",menu:"Meny",template:"Mall",weather:"Väder"}}}},fo={editor:po},go={form:{color_picker:{values:{default:"Varsayılan renk"}},info_picker:{values:{default:"Varsayılan bilgi",name:"İsim",state:"Durum","last-changed":"Son Değişim","last-updated":"Son Güncelleme",none:"None"}},layout_picker:{values:{default:"Varsayılan düzen",vertical:"Dikey düzen",horizontal:"Yatay düzen"}},alignment_picker:{values:{default:"Varsayılan hizalama",start:"Sola yasla",end:"Sağa yasla",center:"Ortala",justify:"İki yana yasla"}}},card:{generic:{icon_color:"Simge renki",layout:"Düzen",primary_info:"Birinci bilgi",secondary_info:"İkinci bilgi",content_info:"İçerik",use_entity_picture:"Varlık resmi kullanılsın",icon_animation:"Aktif olduğunda simgeyi hareket ettir"},light:{show_brightness_control:"Parlaklık kontrolü",use_light_color:"Işık rengini kullan",show_color_temp_control:"Renk ısısı kontrolü",show_color_control:"Renk kontrolü",incompatible_controls:"Kullandığınız lamba bu özellikleri desteklemiyorsa bazı kontroller görüntülenemeyebilir."},fan:{show_percentage_control:"Yüzde kontrolü",show_oscillate_control:"Salınım kontrolü"},cover:{show_buttons_control:"Düğme kontrolleri",show_position_control:"Pozisyon kontrolü"},template:{primary:"Birinci bilgi",secondary:"İkinci bilgi",multiline_secondary:"İkinci bilgi çok satır olsun",entity_extra:"Şablonlarda ve eylemlerde kullanılsın",content:"İçerik"},title:{title:"Başlık",subtitle:"Altbaşlık"},chips:{alignment:"Hizalama"},weather:{show_conditions:"Hava koşulu",show_temperature:"Sıcaklık"},update:{show_buttons_control:"Düğme kontrolü"},vacuum:{commands:"Komutlar"}},chip:{sub_element_editor:{title:"Chip düzenleyici"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"Chip ekle",edit:"Düzenle",clear:"Temizle",select:"Chip seç",types:{action:"Eylem","alarm-control-panel":"Alarm",back:"Geri",conditional:"Koşullu",entity:"Varlık",light:"Işık",menu:"Menü",template:"Şablon",weather:"Hava Durumu"}}}},_o={editor:go},vo={form:{color_picker:{values:{default:"Колір за замовчуванням"}},info_picker:{values:{default:"Інформація за замовчуванням",name:"Назва",state:"Стан","last-changed":"Востаннє змінено","last-updated":"Востаннє оновлено",none:"Нічого"}},icon_type_picker:{values:{default:"За замовчуванням",icon:"Іконка","entity-picture":"Зображення сутності",none:"Нічого"}},layout_picker:{values:{default:"Розташування за замовчуванням",vertical:"Вертикальне розташування",horizontal:"Горизонтальне розташування"}},alignment_picker:{values:{default:"Вирівнювання за замовчуванням",start:"На початку",end:"В кінці",center:"По центру",justify:"Вирівняти"}}},card:{generic:{icon_color:"Колір іконки",layout:"Розташування",fill_container:"Заповнити контейнер",primary_info:"Головна інформація",secondary_info:"Додаткова інформація",icon_type:"Тип іконки",content_info:"Вміст",use_entity_picture:"Використовувати зображення сутності?",collapsible_controls:"Приховувати елементи керування коли вимкнено?",icon_animation:"Анімувати іконку при активації?"},light:{show_brightness_control:"Контроль яскравості?",use_light_color:"Використовувати колір світла",show_color_temp_control:"Керування температурою світла?",show_color_control:"Керування кольором світла?",incompatible_controls:"Деякі елементи керування можуть не відображатись якщо ваш пристрій не підтримує цю функцію."},fan:{show_percentage_control:"Керування швидкістю?",show_oscillate_control:"Керування повротом?"},cover:{show_buttons_control:"Кнопки керування?",show_position_control:"Керування позицією?",show_tilt_position_control:"Керування нахилом?"},template:{primary:"Головна інформація",secondary:"Додаткова інформаіця",multiline_secondary:"Багаторядкова додаткова інформація?",entity_extra:"Використовується в шаблонах та діях",content:"Вміст",badge_icon:"Іконка значка",badge_color:"Колір значка",picture:"Зображення (замінить іконку)"},title:{title:"Заголовок",subtitle:"Підзаголовок",title_tap_action:"Дія при дотику до заголовку",subtitle_tap_action:"Дія при дотику до підзаголовку"},chips:{alignment:"Вирівнювання"},weather:{show_conditions:"Умови?",show_temperature:"Температура?"},update:{show_buttons_control:"Кнопки керування?"},vacuum:{commands:"Команди",commands_list:{on_off:"Увімкнути/Вимкнути"}},"media-player":{use_media_info:"Використовувати інформацію медіа",use_media_artwork:"Використовувати зображення медіа",show_volume_level:"Показати рівень гучності",media_controls:"Керування медіа",media_controls_list:{on_off:"Увімкнути/Вимкнути",shuffle:"Перемішати",previous:"Попередній трек",play_pause_stop:"Відтворити/пауза/стоп",next:"Наступний трек",repeat:"Режим повторення"},volume_controls:"Елементи керування гучністю",volume_controls_list:{volume_buttons:"Кнопки гучності",volume_set:"Рівень гучності",volume_mute:"Вимк. звук"}},lock:{lock:"Зачинити",unlock:"Відчинити",open:"Відкрити"},humidifier:{show_target_humidity_control:"Керування вологістю?"},climate:{show_temperature_control:"Керування температурою?",hvac_modes:"Режими"},number:{display_mode:"Відображати режим",display_mode_list:{default:"За замовчуванням (повзунок)",slider:"Повзунок",buttons:"Кнопки"}}},chip:{sub_element_editor:{title:"Редактор міні-карток"},conditional:{chip:"Міні-картка"},"chip-picker":{chips:"Міні-картки",add:"Додати міні-картку",edit:"Редагувати",clear:"Очистити",select:"Обрати міні-картку",types:{action:"Дія","alarm-control-panel":"Сигналізація",back:"Назад",conditional:"Умовна",entity:"Сутність",light:"Світло",menu:"Меню",spacer:"Порожнє місце",template:"Вручну",weather:"Погода"}}}},bo={not_found:"Сутність не знайдено"},yo={editor:vo,card:bo},xo={form:{color_picker:{values:{default:"Màu mặc định"}},info_picker:{values:{default:"Thông tin mặc định",name:"Tên",state:"Trạng thái","last-changed":"Lần thay đổi cuối","last-updated":"Lần cập nhật cuối",none:"Không có"}},icon_type_picker:{values:{default:"Kiểu mặc định",icon:"Biểu tượng","entity-picture":"Ảnh thực thể",none:"Không có"}},layout_picker:{values:{default:"Bố cục mặc định",vertical:"Bố cục dọc",horizontal:"Bố cục ngang"}},alignment_picker:{values:{default:"Căn chỉnh mặc định",start:"Căn đầu",end:"Căn cuối",center:"Căn giữa",justify:"Căn hai bên"}}},card:{generic:{icon_color:"Màu biểu tượng",layout:"Bố cục",fill_container:"Làm đầy ô chứa",primary_info:"Thông tin chính",secondary_info:"Thông tin phụ",icon_type:"Kiểu biểu tượng",content_info:"Nội dung",use_entity_picture:"Dùng ảnh của thực thể?",collapsible_controls:"Thu nhỏ điều kiển khi tắt",icon_animation:"Biểu tượng chuyển động khi kích hoạt?"},light:{show_brightness_control:"Điều khiển độ sáng?",use_light_color:"Dùng màu đèn",show_color_temp_control:"Điều khiển nhiệt độ màu?",show_color_control:"Điều khiển màu sắc?",incompatible_controls:"Một số điều khiển sẽ không được hiển thị nếu đèn của bạn không hỗ trợ tính năng đó."},fan:{show_percentage_control:"Điều khiển dạng phần trăm?",show_oscillate_control:"Điều khiển xoay?"},cover:{show_buttons_control:"Điều khiển nút bấm?",show_position_control:"Điều khiển vị trí?",show_tilt_position_control:"Điều khiển độ nghiêng?"},template:{primary:"Thông tin chính",secondary:"Thông tin phụ",multiline_secondary:"Nhiều dòng thông tin phụ?",entity_extra:"Được sử dụng trong bản mẫu và hành động",content:"Nội dung",badge_icon:"Biểu tượng huy hiệu",badge_color:"Màu huy hiệu",picture:"Ảnh (thay cho biểu tượng)"},title:{title:"Tiêu đề",subtitle:"Phụ đề",title_tap_action:"Hành động khi nhấp tiêu đề",subtitle_tap_action:"Hành động khi nhấp phụ đề"},chips:{alignment:"Căn chỉnh"},weather:{show_conditions:"Điều kiện?",show_temperature:"Nhiệt độ?"},update:{show_buttons_control:"Điều khiển nút bấm?"},vacuum:{commands:"Mệnh lệnh",commands_list:{on_off:"Bật/tắt"}},"media-player":{use_media_info:"Dùng thông tin đa phương tiện",use_media_artwork:"Dùng ảnh đa phương tiện",show_volume_level:"Hiện mức âm lượng",media_controls:"Điều khiển đa phương tiện",media_controls_list:{on_off:"Bật/tắt",shuffle:"Xáo trộn",previous:"Bài trước",play_pause_stop:"Phát/tạm dừng/dừng",next:"Bài tiếp theo",repeat:"Chế độ lặp lại"},volume_controls:"Điều khiển âm lượng",volume_controls_list:{volume_buttons:"Nút âm lượng",volume_set:"Mức âm lượng",volume_mute:"Im lặng"}},lock:{lock:"Khóa",unlock:"Mở khóa",open:"Mở"},humidifier:{show_target_humidity_control:"Điều khiển độ ẩm?"},climate:{show_temperature_control:"Điều khiển nhiệt độ?",hvac_modes:"Chế độ điều hòa"},number:{display_mode:"Chế độ hiển thị",display_mode_list:{default:"Mặc định (thanh trượt)",slider:"Thanh trượt",buttons:"Nút"}}},chip:{sub_element_editor:{title:"Trình soạn phỉnh"},conditional:{chip:"Phỉnh"},"chip-picker":{chips:"Phỉnh",add:"Thêm phỉnh",edit:"Chỉnh sửa",clear:"Tẩy trống",select:"Chọn phỉnh",types:{action:"Hành động","alarm-control-panel":"Báo động",back:"Quay về",conditional:"Điều kiện",entity:"Thực thể",light:"Đèn",menu:"Trình đơn",spacer:"Ngăn cách",template:"Mẫu",weather:"Thời tiết"}}}},wo={not_found:"Không tìm thấy thực thể"},ko={editor:xo,card:wo},Co={form:{color_picker:{values:{default:"默认颜色"}},info_picker:{values:{default:"默认信息",name:"名称",state:"状态","last-changed":"变更时间","last-updated":"更新时间",none:"无"}},icon_type_picker:{values:{default:"默认类型",icon:"图标","entity-picture":"实体图片",none:"无"}},layout_picker:{values:{default:"默认布局",vertical:"垂直布局",horizontal:"水平布局"}},alignment_picker:{values:{default:"默认",start:"左对齐",end:"右对齐",center:"居中对齐",justify:"两端对齐"}}},card:{generic:{icon_color:"图标颜色",layout:"布局",fill_container:"填满容器",primary_info:"首要信息",secondary_info:"次要信息",icon_type:"图标类型",content_info:"内容",use_entity_picture:"使用实体图片?",collapsible_controls:"关闭时隐藏控制器",icon_animation:"激活时使用动态图标?"},light:{show_brightness_control:"亮度控制?",use_light_color:"使用灯光颜色",show_color_temp_control:"色温控制?",show_color_control:"颜色控制?",incompatible_controls:"设备不支持的控制器将不会显示。"},fan:{show_percentage_control:"百分比控制?",show_oscillate_control:"摆动控制?"},cover:{show_buttons_control:"按钮控制?",show_position_control:"位置控制?",show_tilt_position_control:"角度控制?"},template:{primary:"首要信息",secondary:"次要信息",multiline_secondary:"多行次要信息?",entity_extra:"用于模板和动作",content:"内容",badge_icon:"徽标图标",badge_color:"徽标颜色",picture:"图片 (将会替代图标)"},title:{title:"标题",subtitle:"子标题",title_tap_action:"标题点击动作",subtitle_tap_action:"子标题点击动作"},chips:{alignment:"对齐"},weather:{show_conditions:"条件?",show_temperature:"温度?"},update:{show_buttons_control:"控制按钮?"},vacuum:{commands:"命令",commands_list:{on_off:"开/关"}},"media-player":{use_media_info:"使用媒体信息",use_media_artwork:"使用媒体插图",show_volume_level:"显示音量大小",media_controls:"媒体控制",media_controls_list:{on_off:"开启/关闭",shuffle:"随机",previous:"上一曲",play_pause_stop:"播放/暂停/停止",next:"下一曲",repeat:"循环模式"},volume_controls:"音量控制",volume_controls_list:{volume_buttons:"音量按钮",volume_set:"音量等级",volume_mute:"静音"}},lock:{lock:"锁定",unlock:"解锁",open:"打开"},humidifier:{show_target_humidity_control:"湿度控制?"},climate:{show_temperature_control:"温度控制?",hvac_modes:"空调模式"},number:{display_mode:"显示模式",display_mode_list:{default:"默认 (滑块)",slider:"滑块",buttons:"按钮"}}},chip:{sub_element_editor:{title:"Chip 编辑"},conditional:{chip:"Chip"},"chip-picker":{chips:"Chips",add:"添加 chip",edit:"编辑",clear:"清除",select:"选择 chip",types:{action:"动作","alarm-control-panel":"警戒控制台",back:"返回",conditional:"条件显示",entity:"实体",light:"灯光",menu:"菜单",spacer:"占位符",template:"模板",weather:"天气"}}}},$o={not_found:"未找到实体"},Eo={editor:Co,card:$o},Ao={form:{color_picker:{values:{default:"預設顏色"}},info_picker:{values:{default:"預設訊息",name:"名稱",state:"狀態","last-changed":"最近變動時間","last-updated":"最近更新時間",none:"無"}},icon_type_picker:{values:{default:"預設樣式",icon:"圖示","entity-picture":"實體圖片",none:"無"}},layout_picker:{values:{default:"預設佈局",vertical:"垂直佈局",horizontal:"水平佈局"}},alignment_picker:{values:{default:"預設對齊",start:"居左對齊",end:"居右對齊",center:"居中對齊",justify:"兩端對齊"}}},card:{generic:{icon_color:"圖示顏色",layout:"佈局",fill_container:"填滿容器",primary_info:"主要訊息",secondary_info:"次要訊息",icon_type:"圖示樣式",content_info:"內容",use_entity_picture:"使用實體圖片?",collapsible_controls:"關閉時隱藏控制項",icon_animation:"啟動時使用動態圖示?"},light:{show_brightness_control:"亮度控制?",use_light_color:"使用燈光顏色",show_color_temp_control:"色溫控制?",show_color_control:"色彩控制?",incompatible_controls:"不會顯示裝置不支援的控制。"},fan:{show_percentage_control:"百分比控制?",show_oscillate_control:"擺頭控制?"},cover:{show_buttons_control:"按鈕控制?",show_position_control:"位置控制?",show_tilt_position_control:"角度控制?"},template:{primary:"主要訊息",secondary:"次要訊息",multiline_secondary:"多行次要訊息?",entity_extra:"用於模板與動作",content:"內容",badge_icon:"角標圖示",badge_color:"角標顏色",picture:"圖片 (將會取代圖示)"},title:{title:"標題",subtitle:"副標題",title_tap_action:"標題點擊動作",subtitle_tap_action:"副標題點擊動作"},chips:{alignment:"對齊"},weather:{show_conditions:"狀況?",show_temperature:"溫度?"},update:{show_buttons_control:"按鈕控制?"},vacuum:{commands:"指令",commands_list:{on_off:"開啟、關閉"}},"media-player":{use_media_info:"使用媒體資訊",use_media_artwork:"使用媒體插圖",show_volume_level:"顯示音量大小",media_controls:"媒體控制",media_controls_list:{on_off:"開啟、關閉",shuffle:"隨機播放",previous:"上一首",play_pause_stop:"播放、暫停、停止",next:"下一首",repeat:"重複播放"},volume_controls:"音量控制",volume_controls_list:{volume_buttons:"音量按鈕",volume_set:"音量等級",volume_mute:"靜音"}},lock:{lock:"上鎖",unlock:"解鎖",open:"打開"},humidifier:{show_target_humidity_control:"溼度控制?"},climate:{show_temperature_control:"溫度控制?",hvac_modes:"空調模式"},number:{display_mode:"顯示模式",display_mode_list:{default:"預設 (滑桿)",slider:"滑桿",buttons:"按鈕"}}},chip:{sub_element_editor:{title:"小卡片編輯器"},conditional:{chip:"小卡片"},"chip-picker":{chips:"小卡片",add:"新增小卡片",edit:"編輯",clear:"清除",select:"選擇小卡片",types:{action:"動作","alarm-control-panel":"警報器控制",back:"返回",conditional:"條件",entity:"實體",light:"燈光",menu:"選單",spacer:"佔位符",template:"模板",weather:"天氣"}}}},So={not_found:"未找到實體"},Io={editor:Ao,card:So};const To={ar:Object.freeze({__proto__:null,default:li,editor:si}),bg:Object.freeze({__proto__:null,default:di,editor:ci}),ca:Object.freeze({__proto__:null,card:hi,default:mi,editor:ui}),cs:Object.freeze({__proto__:null,card:fi,default:gi,editor:pi}),da:Object.freeze({__proto__:null,default:vi,editor:_i}),de:Object.freeze({__proto__:null,card:yi,default:xi,editor:bi}),el:Object.freeze({__proto__:null,default:ki,editor:wi}),en:Object.freeze({__proto__:null,card:$i,default:Ei,editor:Ci}),es:Object.freeze({__proto__:null,default:Si,editor:Ai}),fi:Object.freeze({__proto__:null,default:Ti,editor:Ii}),fr:Object.freeze({__proto__:null,default:Oi,editor:zi}),he:Object.freeze({__proto__:null,default:ji,editor:Mi}),hu:Object.freeze({__proto__:null,card:Li,default:Pi,editor:Di}),id:Object.freeze({__proto__:null,card:Ri,default:Fi,editor:Ni}),it:Object.freeze({__proto__:null,card:Bi,default:Ui,editor:Vi}),"ko-KR":Object.freeze({__proto__:null,default:Yi,editor:Hi}),nb:Object.freeze({__proto__:null,default:Xi,editor:Wi}),nl:Object.freeze({__proto__:null,card:Gi,default:qi,editor:Ki}),pl:Object.freeze({__proto__:null,default:Ji,editor:Zi}),"pt-BR":Object.freeze({__proto__:null,default:to,editor:Qi}),"pt-PT":Object.freeze({__proto__:null,default:io,editor:eo}),ro:Object.freeze({__proto__:null,default:no,editor:oo}),ru:Object.freeze({__proto__:null,default:ao,editor:ro}),sl:Object.freeze({__proto__:null,card:lo,default:co,editor:so}),sk:Object.freeze({__proto__:null,card:ho,default:mo,editor:uo}),sv:Object.freeze({__proto__:null,default:fo,editor:po}),tr:Object.freeze({__proto__:null,default:_o,editor:go}),uk:Object.freeze({__proto__:null,card:bo,default:yo,editor:vo}),vi:Object.freeze({__proto__:null,card:wo,default:ko,editor:xo}),"zh-Hans":Object.freeze({__proto__:null,card:$o,default:Eo,editor:Co}),"zh-Hant":Object.freeze({__proto__:null,card:So,default:Io,editor:Ao})};function zo(t,e){try{return t.split(".").reduce(((t,e)=>t[e]),To[e])}catch(t){return}}function Oo(t){return function(e){var i;let o=zo(e,null!==(i=null==t?void 0:t.locale.language)&&void 0!==i?i:"en");return o||(o=zo(e,"en")),null!=o?o:e}} /** * @license * Copyright 2020 Google Inc. @@ -189,7 +189,7 @@ const pt=t=>e=>"function"==typeof e?((t,e)=>(customElements.define(t,e),e))(t,e) * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */var zo="Unknown",Mo="Backspace",jo="Enter",Do="Spacebar",Lo="PageUp",Po="PageDown",No="End",Ro="Home",Fo="ArrowLeft",Vo="ArrowUp",Bo="ArrowRight",Uo="ArrowDown",Ho="Delete",Yo="Escape",Wo="Tab",Xo=new Set;Xo.add(Mo),Xo.add(jo),Xo.add(Do),Xo.add(Lo),Xo.add(Po),Xo.add(No),Xo.add(Ro),Xo.add(Fo),Xo.add(Vo),Xo.add(Bo),Xo.add(Uo),Xo.add(Ho),Xo.add(Yo),Xo.add(Wo);var Ko=8,Go=13,qo=32,Zo=33,Jo=34,Qo=35,tn=36,en=37,on=38,nn=39,rn=40,an=46,ln=27,sn=9,cn=new Map;cn.set(Ko,Mo),cn.set(Go,jo),cn.set(qo,Do),cn.set(Zo,Lo),cn.set(Jo,Po),cn.set(Qo,No),cn.set(tn,Ro),cn.set(en,Fo),cn.set(on,Vo),cn.set(nn,Bo),cn.set(rn,Uo),cn.set(an,Ho),cn.set(ln,Yo),cn.set(sn,Wo);var dn=new Set;function un(t){var e=t.key;if(Xo.has(e))return e;var i=cn.get(t.keyCode);return i||zo} + */var Mo="Unknown",jo="Backspace",Do="Enter",Lo="Spacebar",Po="PageUp",No="PageDown",Ro="End",Fo="Home",Vo="ArrowLeft",Bo="ArrowUp",Uo="ArrowRight",Ho="ArrowDown",Yo="Delete",Wo="Escape",Xo="Tab",Ko=new Set;Ko.add(jo),Ko.add(Do),Ko.add(Lo),Ko.add(Po),Ko.add(No),Ko.add(Ro),Ko.add(Fo),Ko.add(Vo),Ko.add(Bo),Ko.add(Uo),Ko.add(Ho),Ko.add(Yo),Ko.add(Wo),Ko.add(Xo);var Go=8,qo=13,Zo=32,Jo=33,Qo=34,tn=35,en=36,on=37,nn=38,rn=39,an=40,sn=46,ln=27,cn=9,dn=new Map;dn.set(Go,jo),dn.set(qo,Do),dn.set(Zo,Lo),dn.set(Jo,Po),dn.set(Qo,No),dn.set(tn,Ro),dn.set(en,Fo),dn.set(on,Vo),dn.set(nn,Bo),dn.set(rn,Uo),dn.set(an,Ho),dn.set(sn,Yo),dn.set(ln,Wo),dn.set(cn,Xo);var un=new Set;function hn(t){var e=t.key;if(Ko.has(e))return e;var i=dn.get(t.keyCode);return i||Mo} /** * @license * Copyright 2020 Google Inc. @@ -211,7 +211,7 @@ const pt=t=>e=>"function"==typeof e?((t,e)=>(customElements.define(t,e),e))(t,e) * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */dn.add(Lo),dn.add(Po),dn.add(No),dn.add(Ro),dn.add(Fo),dn.add(Vo),dn.add(Bo),dn.add(Uo);var hn={UNKNOWN:"Unknown",BACKSPACE:"Backspace",ENTER:"Enter",SPACEBAR:"Spacebar",PAGE_UP:"PageUp",PAGE_DOWN:"PageDown",END:"End",HOME:"Home",ARROW_LEFT:"ArrowLeft",ARROW_UP:"ArrowUp",ARROW_RIGHT:"ArrowRight",ARROW_DOWN:"ArrowDown",DELETE:"Delete",ESCAPE:"Escape",TAB:"Tab"},mn=new Set;mn.add(hn.BACKSPACE),mn.add(hn.ENTER),mn.add(hn.SPACEBAR),mn.add(hn.PAGE_UP),mn.add(hn.PAGE_DOWN),mn.add(hn.END),mn.add(hn.HOME),mn.add(hn.ARROW_LEFT),mn.add(hn.ARROW_UP),mn.add(hn.ARROW_RIGHT),mn.add(hn.ARROW_DOWN),mn.add(hn.DELETE),mn.add(hn.ESCAPE),mn.add(hn.TAB);var pn=8,fn=13,gn=32,_n=33,vn=34,bn=35,yn=36,xn=37,wn=38,kn=39,Cn=40,$n=46,En=27,An=9,Sn=new Map;Sn.set(pn,hn.BACKSPACE),Sn.set(fn,hn.ENTER),Sn.set(gn,hn.SPACEBAR),Sn.set(_n,hn.PAGE_UP),Sn.set(vn,hn.PAGE_DOWN),Sn.set(bn,hn.END),Sn.set(yn,hn.HOME),Sn.set(xn,hn.ARROW_LEFT),Sn.set(wn,hn.ARROW_UP),Sn.set(kn,hn.ARROW_RIGHT),Sn.set(Cn,hn.ARROW_DOWN),Sn.set($n,hn.DELETE),Sn.set(En,hn.ESCAPE),Sn.set(An,hn.TAB);var In,Tn,On=new Set;function zn(t){var e=t.key;if(mn.has(e))return e;var i=Sn.get(t.keyCode);return i||hn.UNKNOWN} + */un.add(Po),un.add(No),un.add(Ro),un.add(Fo),un.add(Vo),un.add(Bo),un.add(Uo),un.add(Ho);var mn={UNKNOWN:"Unknown",BACKSPACE:"Backspace",ENTER:"Enter",SPACEBAR:"Spacebar",PAGE_UP:"PageUp",PAGE_DOWN:"PageDown",END:"End",HOME:"Home",ARROW_LEFT:"ArrowLeft",ARROW_UP:"ArrowUp",ARROW_RIGHT:"ArrowRight",ARROW_DOWN:"ArrowDown",DELETE:"Delete",ESCAPE:"Escape",TAB:"Tab"},pn=new Set;pn.add(mn.BACKSPACE),pn.add(mn.ENTER),pn.add(mn.SPACEBAR),pn.add(mn.PAGE_UP),pn.add(mn.PAGE_DOWN),pn.add(mn.END),pn.add(mn.HOME),pn.add(mn.ARROW_LEFT),pn.add(mn.ARROW_UP),pn.add(mn.ARROW_RIGHT),pn.add(mn.ARROW_DOWN),pn.add(mn.DELETE),pn.add(mn.ESCAPE),pn.add(mn.TAB);var fn=8,gn=13,_n=32,vn=33,bn=34,yn=35,xn=36,wn=37,kn=38,Cn=39,$n=40,En=46,An=27,Sn=9,In=new Map;In.set(fn,mn.BACKSPACE),In.set(gn,mn.ENTER),In.set(_n,mn.SPACEBAR),In.set(vn,mn.PAGE_UP),In.set(bn,mn.PAGE_DOWN),In.set(yn,mn.END),In.set(xn,mn.HOME),In.set(wn,mn.ARROW_LEFT),In.set(kn,mn.ARROW_UP),In.set(Cn,mn.ARROW_RIGHT),In.set($n,mn.ARROW_DOWN),In.set(En,mn.DELETE),In.set(An,mn.ESCAPE),In.set(Sn,mn.TAB);var Tn,zn,On=new Set;function Mn(t){var e=t.key;if(pn.has(e))return e;var i=In.get(t.keyCode);return i||mn.UNKNOWN} /** * @license * Copyright 2018 Google Inc. @@ -233,7 +233,7 @@ const pt=t=>e=>"function"==typeof e?((t,e)=>(customElements.define(t,e),e))(t,e) * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */On.add(hn.PAGE_UP),On.add(hn.PAGE_DOWN),On.add(hn.END),On.add(hn.HOME),On.add(hn.ARROW_LEFT),On.add(hn.ARROW_UP),On.add(hn.ARROW_RIGHT),On.add(hn.ARROW_DOWN);var Mn="mdc-list-item--activated",jn="mdc-list-item",Dn="mdc-list-item--disabled",Ln="mdc-list-item--selected",Pn="mdc-list-item__text",Nn="mdc-list-item__primary-text",Rn="mdc-list";(In={})[""+Mn]="mdc-list-item--activated",In[""+jn]="mdc-list-item",In[""+Dn]="mdc-list-item--disabled",In[""+Ln]="mdc-list-item--selected",In[""+Nn]="mdc-list-item__primary-text",In[""+Rn]="mdc-list";var Fn=((Tn={})[""+Mn]="mdc-deprecated-list-item--activated",Tn[""+jn]="mdc-deprecated-list-item",Tn[""+Dn]="mdc-deprecated-list-item--disabled",Tn[""+Ln]="mdc-deprecated-list-item--selected",Tn[""+Pn]="mdc-deprecated-list-item__text",Tn[""+Nn]="mdc-deprecated-list-item__primary-text",Tn[""+Rn]="mdc-deprecated-list",Tn);Fn[jn],Fn[jn],Fn[jn],Fn[jn],Fn[jn],Fn[jn];var Vn={UNSET_INDEX:-1,TYPEAHEAD_BUFFER_CLEAR_TIMEOUT_MS:300},Bn=["input","button","textarea","select"],Un=function(t){var e=t.target;if(e){var i=(""+e.tagName).toLowerCase();-1===Bn.indexOf(i)&&t.preventDefault()}}; + */On.add(mn.PAGE_UP),On.add(mn.PAGE_DOWN),On.add(mn.END),On.add(mn.HOME),On.add(mn.ARROW_LEFT),On.add(mn.ARROW_UP),On.add(mn.ARROW_RIGHT),On.add(mn.ARROW_DOWN);var jn="mdc-list-item--activated",Dn="mdc-list-item",Ln="mdc-list-item--disabled",Pn="mdc-list-item--selected",Nn="mdc-list-item__text",Rn="mdc-list-item__primary-text",Fn="mdc-list";(Tn={})[""+jn]="mdc-list-item--activated",Tn[""+Dn]="mdc-list-item",Tn[""+Ln]="mdc-list-item--disabled",Tn[""+Pn]="mdc-list-item--selected",Tn[""+Rn]="mdc-list-item__primary-text",Tn[""+Fn]="mdc-list";var Vn=((zn={})[""+jn]="mdc-deprecated-list-item--activated",zn[""+Dn]="mdc-deprecated-list-item",zn[""+Ln]="mdc-deprecated-list-item--disabled",zn[""+Pn]="mdc-deprecated-list-item--selected",zn[""+Nn]="mdc-deprecated-list-item__text",zn[""+Rn]="mdc-deprecated-list-item__primary-text",zn[""+Fn]="mdc-deprecated-list",zn);Vn[Dn],Vn[Dn],Vn[Dn],Vn[Dn],Vn[Dn],Vn[Dn];var Bn={UNSET_INDEX:-1,TYPEAHEAD_BUFFER_CLEAR_TIMEOUT_MS:300},Un=["input","button","textarea","select"],Hn=function(t){var e=t.target;if(e){var i=(""+e.tagName).toLowerCase();-1===Un.indexOf(i)&&t.preventDefault()}}; /** * @license * Copyright 2020 Google Inc. @@ -255,24 +255,24 @@ const pt=t=>e=>"function"==typeof e?((t,e)=>(customElements.define(t,e),e))(t,e) * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */function Hn(t,e){for(var i=new Map,o=0;oe&&!i(r[l].index)){s=l;break}if(-1!==s)return o.sortedIndexCursor=s,r[o.sortedIndexCursor].index;return-1}(r,a,s,e):function(t,e,i){var o=i.typeaheadBuffer[0],n=t.get(o);if(!n)return-1;var r=n[i.sortedIndexCursor];if(0===r.text.lastIndexOf(i.typeaheadBuffer,0)&&!e(r.index))return r.index;var a=(i.sortedIndexCursor+1)%n.length,l=-1;for(;a!==i.sortedIndexCursor;){var s=n[a],c=0===s.text.lastIndexOf(i.typeaheadBuffer,0),d=!e(s.index);if(c&&d){l=a;break}a=(a+1)%n.length}if(-1!==l)return i.sortedIndexCursor=l,n[i.sortedIndexCursor].index;return-1}(r,s,e),-1===i||l||n(i),i}function Wn(t){return t.typeaheadBuffer.length>0}function Xn(t){return{addClass:e=>{t.classList.add(e)},removeClass:e=>{t.classList.remove(e)},hasClass:e=>t.classList.contains(e)}}const Kn=()=>{},Gn={get passive(){return!1}};document.addEventListener("x",Kn,Gn),document.removeEventListener("x",Kn); + */function Yn(t,e){for(var i=new Map,o=0;oe&&!i(r[s].index)){l=s;break}if(-1!==l)return o.sortedIndexCursor=l,r[o.sortedIndexCursor].index;return-1}(r,a,l,e):function(t,e,i){var o=i.typeaheadBuffer[0],n=t.get(o);if(!n)return-1;var r=n[i.sortedIndexCursor];if(0===r.text.lastIndexOf(i.typeaheadBuffer,0)&&!e(r.index))return r.index;var a=(i.sortedIndexCursor+1)%n.length,s=-1;for(;a!==i.sortedIndexCursor;){var l=n[a],c=0===l.text.lastIndexOf(i.typeaheadBuffer,0),d=!e(l.index);if(c&&d){s=a;break}a=(a+1)%n.length}if(-1!==s)return i.sortedIndexCursor=s,n[i.sortedIndexCursor].index;return-1}(r,l,e),-1===i||s||n(i),i}function Xn(t){return t.typeaheadBuffer.length>0}function Kn(t){return{addClass:e=>{t.classList.add(e)},removeClass:e=>{t.classList.remove(e)},hasClass:e=>t.classList.contains(e)}}const Gn=()=>{},qn={get passive(){return!1}};document.addEventListener("x",Gn,qn),document.removeEventListener("x",Gn); /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -class qn extends ht{click(){if(this.mdcRoot)return this.mdcRoot.focus(),void this.mdcRoot.click();super.click()}createFoundation(){void 0!==this.mdcFoundation&&this.mdcFoundation.destroy(),this.mdcFoundationClass&&(this.mdcFoundation=new this.mdcFoundationClass(this.createAdapter()),this.mdcFoundation.init())}firstUpdated(){this.createFoundation()}} +class Zn extends ht{click(){if(this.mdcRoot)return this.mdcRoot.focus(),void this.mdcRoot.click();super.click()}createFoundation(){void 0!==this.mdcFoundation&&this.mdcFoundation.destroy(),this.mdcFoundationClass&&(this.mdcFoundation=new this.mdcFoundationClass(this.createAdapter()),this.mdcFoundation.init())}firstUpdated(){this.createFoundation()}} /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: Apache-2.0 - */var Zn,Jn;const Qn=null!==(Jn=null===(Zn=window.ShadyDOM)||void 0===Zn?void 0:Zn.inUse)&&void 0!==Jn&&Jn;class tr extends qn{constructor(){super(...arguments),this.disabled=!1,this.containingForm=null,this.formDataListener=t=>{this.disabled||this.setFormData(t.formData)}}findFormElement(){if(!this.shadowRoot||Qn)return null;const t=this.getRootNode().querySelectorAll("form");for(const e of Array.from(t))if(e.contains(this))return e;return null}connectedCallback(){var t;super.connectedCallback(),this.containingForm=this.findFormElement(),null===(t=this.containingForm)||void 0===t||t.addEventListener("formdata",this.formDataListener)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this.containingForm)||void 0===t||t.removeEventListener("formdata",this.formDataListener),this.containingForm=null}click(){this.formElement&&!this.disabled&&(this.formElement.focus(),this.formElement.click())}firstUpdated(){super.firstUpdated(),this.shadowRoot&&this.mdcRoot.addEventListener("change",(t=>{this.dispatchEvent(new Event("change",t))}))}}tr.shadowRootOptions={mode:"open",delegatesFocus:!0},n([_t({type:Boolean})],tr.prototype,"disabled",void 0); + */var Jn,Qn;const tr=null!==(Qn=null===(Jn=window.ShadyDOM)||void 0===Jn?void 0:Jn.inUse)&&void 0!==Qn&&Qn;class er extends Zn{constructor(){super(...arguments),this.disabled=!1,this.containingForm=null,this.formDataListener=t=>{this.disabled||this.setFormData(t.formData)}}findFormElement(){if(!this.shadowRoot||tr)return null;const t=this.getRootNode().querySelectorAll("form");for(const e of Array.from(t))if(e.contains(this))return e;return null}connectedCallback(){var t;super.connectedCallback(),this.containingForm=this.findFormElement(),null===(t=this.containingForm)||void 0===t||t.addEventListener("formdata",this.formDataListener)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this.containingForm)||void 0===t||t.removeEventListener("formdata",this.formDataListener),this.containingForm=null}click(){this.formElement&&!this.disabled&&(this.formElement.focus(),this.formElement.click())}firstUpdated(){super.firstUpdated(),this.shadowRoot&&this.mdcRoot.addEventListener("change",(t=>{this.dispatchEvent(new Event("change",t))}))}}er.shadowRootOptions={mode:"open",delegatesFocus:!0},n([_t({type:Boolean})],er.prototype,"disabled",void 0); /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: Apache-2.0 */ -const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnProperty("_observers")){const t=e.constructor._observers;e.constructor._observers=new Map,t.forEach(((t,i)=>e.constructor._observers.set(i,t)))}}else{e.constructor._observers=new Map;const t=e.updated;e.updated=function(e){t.call(this,e),e.forEach(((t,e)=>{const i=this.constructor._observers.get(e);void 0!==i&&i.call(this,this[e],t)}))}}e.constructor._observers.set(i,t)} +const ir=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnProperty("_observers")){const t=e.constructor._observers;e.constructor._observers=new Map,t.forEach(((t,i)=>e.constructor._observers.set(i,t)))}}else{e.constructor._observers=new Map;const t=e.updated;e.updated=function(e){t.call(this,e),e.forEach(((t,e)=>{const i=this.constructor._observers.get(e);void 0!==i&&i.call(this,this[e],t)}))}}e.constructor._observers.set(i,t)} /** * @license * Copyright 2016 Google Inc. @@ -294,7 +294,7 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */;var ir=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),or={LABEL_FLOAT_ABOVE:"mdc-floating-label--float-above",LABEL_REQUIRED:"mdc-floating-label--required",LABEL_SHAKE:"mdc-floating-label--shake",ROOT:"mdc-floating-label"},nr=function(t){function e(i){var n=t.call(this,o(o({},e.defaultAdapter),i))||this;return n.shakeAnimationEndHandler=function(){n.handleShakeAnimationEnd()},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return or},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},getWidth:function(){return 0},registerInteractionHandler:function(){},deregisterInteractionHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.getWidth=function(){return this.adapter.getWidth()},e.prototype.shake=function(t){var i=e.cssClasses.LABEL_SHAKE;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.float=function(t){var i=e.cssClasses,o=i.LABEL_FLOAT_ABOVE,n=i.LABEL_SHAKE;t?this.adapter.addClass(o):(this.adapter.removeClass(o),this.adapter.removeClass(n))},e.prototype.setRequired=function(t){var i=e.cssClasses.LABEL_REQUIRED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.handleShakeAnimationEnd=function(){var t=e.cssClasses.LABEL_SHAKE;this.adapter.removeClass(t)},e}(ir); + */;var or=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),nr={LABEL_FLOAT_ABOVE:"mdc-floating-label--float-above",LABEL_REQUIRED:"mdc-floating-label--required",LABEL_SHAKE:"mdc-floating-label--shake",ROOT:"mdc-floating-label"},rr=function(t){function e(i){var n=t.call(this,o(o({},e.defaultAdapter),i))||this;return n.shakeAnimationEndHandler=function(){n.handleShakeAnimationEnd()},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return nr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},getWidth:function(){return 0},registerInteractionHandler:function(){},deregisterInteractionHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.getWidth=function(){return this.adapter.getWidth()},e.prototype.shake=function(t){var i=e.cssClasses.LABEL_SHAKE;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.float=function(t){var i=e.cssClasses,o=i.LABEL_FLOAT_ABOVE,n=i.LABEL_SHAKE;t?this.adapter.addClass(o):(this.adapter.removeClass(o),this.adapter.removeClass(n))},e.prototype.setRequired=function(t){var i=e.cssClasses.LABEL_REQUIRED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.handleShakeAnimationEnd=function(){var t=e.cssClasses.LABEL_SHAKE;this.adapter.removeClass(t)},e}(or); /** * @license * Copyright 2016 Google Inc. @@ -316,7 +316,7 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */const rr=He(class extends Ye{constructor(t){switch(super(t),this.foundation=null,this.previousPart=null,t.type){case Ve:case Be:break;default:throw new Error("FloatingLabel directive only support attribute and property parts")}}update(t,[e]){if(t!==this.previousPart){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-floating-label");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),getWidth:()=>t.scrollWidth,registerInteractionHandler:(e,i)=>{t.addEventListener(e,i)},deregisterInteractionHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new nr(i),this.foundation.init()}return this.render(e)}render(t){return this.foundation}}); + */const ar=He(class extends Ye{constructor(t){switch(super(t),this.foundation=null,this.previousPart=null,t.type){case Ve:case Be:break;default:throw new Error("FloatingLabel directive only support attribute and property parts")}}update(t,[e]){if(t!==this.previousPart){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-floating-label");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),getWidth:()=>t.scrollWidth,registerInteractionHandler:(e,i)=>{t.addEventListener(e,i)},deregisterInteractionHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new rr(i),this.foundation.init()}return this.render(e)}render(t){return this.foundation}}); /** * @license * Copyright 2016 Google Inc. @@ -338,7 +338,7 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */var ar=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),lr={LINE_RIPPLE_ACTIVE:"mdc-line-ripple--active",LINE_RIPPLE_DEACTIVATING:"mdc-line-ripple--deactivating"},sr=function(t){function e(i){var n=t.call(this,o(o({},e.defaultAdapter),i))||this;return n.transitionEndHandler=function(t){n.handleTransitionEnd(t)},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return lr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},setStyle:function(){},registerEventHandler:function(){},deregisterEventHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerEventHandler("transitionend",this.transitionEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterEventHandler("transitionend",this.transitionEndHandler)},e.prototype.activate=function(){this.adapter.removeClass(lr.LINE_RIPPLE_DEACTIVATING),this.adapter.addClass(lr.LINE_RIPPLE_ACTIVE)},e.prototype.setRippleCenter=function(t){this.adapter.setStyle("transform-origin",t+"px center")},e.prototype.deactivate=function(){this.adapter.addClass(lr.LINE_RIPPLE_DEACTIVATING)},e.prototype.handleTransitionEnd=function(t){var e=this.adapter.hasClass(lr.LINE_RIPPLE_DEACTIVATING);"opacity"===t.propertyName&&e&&(this.adapter.removeClass(lr.LINE_RIPPLE_ACTIVE),this.adapter.removeClass(lr.LINE_RIPPLE_DEACTIVATING))},e}(ar); + */var sr=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),lr={LINE_RIPPLE_ACTIVE:"mdc-line-ripple--active",LINE_RIPPLE_DEACTIVATING:"mdc-line-ripple--deactivating"},cr=function(t){function e(i){var n=t.call(this,o(o({},e.defaultAdapter),i))||this;return n.transitionEndHandler=function(t){n.handleTransitionEnd(t)},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return lr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},setStyle:function(){},registerEventHandler:function(){},deregisterEventHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerEventHandler("transitionend",this.transitionEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterEventHandler("transitionend",this.transitionEndHandler)},e.prototype.activate=function(){this.adapter.removeClass(lr.LINE_RIPPLE_DEACTIVATING),this.adapter.addClass(lr.LINE_RIPPLE_ACTIVE)},e.prototype.setRippleCenter=function(t){this.adapter.setStyle("transform-origin",t+"px center")},e.prototype.deactivate=function(){this.adapter.addClass(lr.LINE_RIPPLE_DEACTIVATING)},e.prototype.handleTransitionEnd=function(t){var e=this.adapter.hasClass(lr.LINE_RIPPLE_DEACTIVATING);"opacity"===t.propertyName&&e&&(this.adapter.removeClass(lr.LINE_RIPPLE_ACTIVE),this.adapter.removeClass(lr.LINE_RIPPLE_DEACTIVATING))},e}(sr); /** * @license * Copyright 2018 Google Inc. @@ -360,7 +360,7 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */const cr=He(class extends Ye{constructor(t){switch(super(t),this.previousPart=null,this.foundation=null,t.type){case Ve:case Be:return;default:throw new Error("LineRipple only support attribute and property parts.")}}update(t,e){if(this.previousPart!==t){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-line-ripple");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),hasClass:e=>t.classList.contains(e),setStyle:(e,i)=>t.style.setProperty(e,i),registerEventHandler:(e,i)=>{t.addEventListener(e,i)},deregisterEventHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new sr(i),this.foundation.init()}return this.render()}render(){return this.foundation}}); + */const dr=He(class extends Ye{constructor(t){switch(super(t),this.previousPart=null,this.foundation=null,t.type){case Ve:case Be:return;default:throw new Error("LineRipple only support attribute and property parts.")}}update(t,e){if(this.previousPart!==t){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-line-ripple");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),hasClass:e=>t.classList.contains(e),setStyle:(e,i)=>t.style.setProperty(e,i),registerEventHandler:(e,i)=>{t.addEventListener(e,i)},deregisterEventHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new cr(i),this.foundation.init()}return this.render()}render(){return this.foundation}}); /** * @license * Copyright 2016 Google Inc. @@ -382,7 +382,7 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */var dr=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),ur="Unknown",hr="Backspace",mr="Enter",pr="Spacebar",fr="PageUp",gr="PageDown",_r="End",vr="Home",br="ArrowLeft",yr="ArrowUp",xr="ArrowRight",wr="ArrowDown",kr="Delete",Cr="Escape",$r="Tab",Er=new Set; + */var ur=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),hr="Unknown",mr="Backspace",pr="Enter",fr="Spacebar",gr="PageUp",_r="PageDown",vr="End",br="Home",yr="ArrowLeft",xr="ArrowUp",wr="ArrowRight",kr="ArrowDown",Cr="Delete",$r="Escape",Er="Tab",Ar=new Set; /** * @license * Copyright 2020 Google Inc. @@ -404,7 +404,7 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */Er.add(hr),Er.add(mr),Er.add(pr),Er.add(fr),Er.add(gr),Er.add(_r),Er.add(vr),Er.add(br),Er.add(yr),Er.add(xr),Er.add(wr),Er.add(kr),Er.add(Cr),Er.add($r);var Ar=8,Sr=13,Ir=32,Tr=33,Or=34,zr=35,Mr=36,jr=37,Dr=38,Lr=39,Pr=40,Nr=46,Rr=27,Fr=9,Vr=new Map;Vr.set(Ar,hr),Vr.set(Sr,mr),Vr.set(Ir,pr),Vr.set(Tr,fr),Vr.set(Or,gr),Vr.set(zr,_r),Vr.set(Mr,vr),Vr.set(jr,br),Vr.set(Dr,yr),Vr.set(Lr,xr),Vr.set(Pr,wr),Vr.set(Nr,kr),Vr.set(Rr,Cr),Vr.set(Fr,$r);var Br,Ur,Hr=new Set;function Yr(t){var e=t.key;if(Er.has(e))return e;var i=Vr.get(t.keyCode);return i||ur} + */Ar.add(mr),Ar.add(pr),Ar.add(fr),Ar.add(gr),Ar.add(_r),Ar.add(vr),Ar.add(br),Ar.add(yr),Ar.add(xr),Ar.add(wr),Ar.add(kr),Ar.add(Cr),Ar.add($r),Ar.add(Er);var Sr=8,Ir=13,Tr=32,zr=33,Or=34,Mr=35,jr=36,Dr=37,Lr=38,Pr=39,Nr=40,Rr=46,Fr=27,Vr=9,Br=new Map;Br.set(Sr,mr),Br.set(Ir,pr),Br.set(Tr,fr),Br.set(zr,gr),Br.set(Or,_r),Br.set(Mr,vr),Br.set(jr,br),Br.set(Dr,yr),Br.set(Lr,xr),Br.set(Pr,wr),Br.set(Nr,kr),Br.set(Rr,Cr),Br.set(Fr,$r),Br.set(Vr,Er);var Ur,Hr,Yr=new Set;function Wr(t){var e=t.key;if(Ar.has(e))return e;var i=Br.get(t.keyCode);return i||hr} /** * @license * Copyright 2018 Google Inc. @@ -426,7 +426,7 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */Hr.add(fr),Hr.add(gr),Hr.add(_r),Hr.add(vr),Hr.add(br),Hr.add(yr),Hr.add(xr),Hr.add(wr),function(t){t[t.BOTTOM=1]="BOTTOM",t[t.CENTER=2]="CENTER",t[t.RIGHT=4]="RIGHT",t[t.FLIP_RTL=8]="FLIP_RTL"}(Br||(Br={})),function(t){t[t.TOP_LEFT=0]="TOP_LEFT",t[t.TOP_RIGHT=4]="TOP_RIGHT",t[t.BOTTOM_LEFT=1]="BOTTOM_LEFT",t[t.BOTTOM_RIGHT=5]="BOTTOM_RIGHT",t[t.TOP_START=8]="TOP_START",t[t.TOP_END=12]="TOP_END",t[t.BOTTOM_START=9]="BOTTOM_START",t[t.BOTTOM_END=13]="BOTTOM_END"}(Ur||(Ur={})); + */Yr.add(gr),Yr.add(_r),Yr.add(vr),Yr.add(br),Yr.add(yr),Yr.add(xr),Yr.add(wr),Yr.add(kr),function(t){t[t.BOTTOM=1]="BOTTOM",t[t.CENTER=2]="CENTER",t[t.RIGHT=4]="RIGHT",t[t.FLIP_RTL=8]="FLIP_RTL"}(Ur||(Ur={})),function(t){t[t.TOP_LEFT=0]="TOP_LEFT",t[t.TOP_RIGHT=4]="TOP_RIGHT",t[t.BOTTOM_LEFT=1]="BOTTOM_LEFT",t[t.BOTTOM_RIGHT=5]="BOTTOM_RIGHT",t[t.TOP_START=8]="TOP_START",t[t.TOP_END=12]="TOP_END",t[t.BOTTOM_START=9]="BOTTOM_START",t[t.BOTTOM_END=13]="BOTTOM_END"}(Hr||(Hr={})); /** * @license * Copyright 2016 Google Inc. @@ -449,25 +449,25 @@ const er=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnPropert * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -var Wr={ACTIVATED:"mdc-select--activated",DISABLED:"mdc-select--disabled",FOCUSED:"mdc-select--focused",INVALID:"mdc-select--invalid",MENU_INVALID:"mdc-select__menu--invalid",OUTLINED:"mdc-select--outlined",REQUIRED:"mdc-select--required",ROOT:"mdc-select",WITH_LEADING_ICON:"mdc-select--with-leading-icon"},Xr={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",ARIA_SELECTED_ATTR:"aria-selected",CHANGE_EVENT:"MDCSelect:change",HIDDEN_INPUT_SELECTOR:'input[type="hidden"]',LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-select__icon",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",MENU_SELECTOR:".mdc-select__menu",OUTLINE_SELECTOR:".mdc-notched-outline",SELECTED_TEXT_SELECTOR:".mdc-select__selected-text",SELECT_ANCHOR_SELECTOR:".mdc-select__anchor",VALUE_ATTR:"data-value"},Kr={LABEL_SCALE:.75,UNSET_INDEX:-1,CLICK_DEBOUNCE_TIMEOUT_MS:330},Gr=function(t){function e(i,n){void 0===n&&(n={});var r=t.call(this,o(o({},e.defaultAdapter),i))||this;return r.disabled=!1,r.isMenuOpen=!1,r.useDefaultValidation=!0,r.customValidity=!0,r.lastSelectedIndex=Kr.UNSET_INDEX,r.clickDebounceTimeout=0,r.recentlyClicked=!1,r.leadingIcon=n.leadingIcon,r.helperText=n.helperText,r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Wr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return Kr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Xr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},activateBottomLine:function(){},deactivateBottomLine:function(){},getSelectedIndex:function(){return-1},setSelectedIndex:function(){},hasLabel:function(){return!1},floatLabel:function(){},getLabelWidth:function(){return 0},setLabelRequired:function(){},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){},setRippleCenter:function(){},notifyChange:function(){},setSelectedText:function(){},isSelectAnchorFocused:function(){return!1},getSelectAnchorAttr:function(){return""},setSelectAnchorAttr:function(){},removeSelectAnchorAttr:function(){},addMenuClass:function(){},removeMenuClass:function(){},openMenu:function(){},closeMenu:function(){},getAnchorElement:function(){return null},setMenuAnchorElement:function(){},setMenuAnchorCorner:function(){},setMenuWrapFocus:function(){},focusMenuItemAtIndex:function(){},getMenuItemCount:function(){return 0},getMenuItemValues:function(){return[]},getMenuItemTextAtIndex:function(){return""},isTypeaheadInProgress:function(){return!1},typeaheadMatchItem:function(){return-1}}},enumerable:!1,configurable:!0}),e.prototype.getSelectedIndex=function(){return this.adapter.getSelectedIndex()},e.prototype.setSelectedIndex=function(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1),t>=this.adapter.getMenuItemCount()||(t===Kr.UNSET_INDEX?this.adapter.setSelectedText(""):this.adapter.setSelectedText(this.adapter.getMenuItemTextAtIndex(t).trim()),this.adapter.setSelectedIndex(t),e&&this.adapter.closeMenu(),i||this.lastSelectedIndex===t||this.handleChange(),this.lastSelectedIndex=t)},e.prototype.setValue=function(t,e){void 0===e&&(e=!1);var i=this.adapter.getMenuItemValues().indexOf(t);this.setSelectedIndex(i,!1,e)},e.prototype.getValue=function(){var t=this.adapter.getSelectedIndex(),e=this.adapter.getMenuItemValues();return t!==Kr.UNSET_INDEX?e[t]:""},e.prototype.getDisabled=function(){return this.disabled},e.prototype.setDisabled=function(t){this.disabled=t,this.disabled?(this.adapter.addClass(Wr.DISABLED),this.adapter.closeMenu()):this.adapter.removeClass(Wr.DISABLED),this.leadingIcon&&this.leadingIcon.setDisabled(this.disabled),this.disabled?this.adapter.removeSelectAnchorAttr("tabindex"):this.adapter.setSelectAnchorAttr("tabindex","0"),this.adapter.setSelectAnchorAttr("aria-disabled",this.disabled.toString())},e.prototype.openMenu=function(){this.adapter.addClass(Wr.ACTIVATED),this.adapter.openMenu(),this.isMenuOpen=!0,this.adapter.setSelectAnchorAttr("aria-expanded","true")},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.layout=function(){if(this.adapter.hasLabel()){var t=this.getValue().length>0,e=this.adapter.hasClass(Wr.FOCUSED),i=t||e,o=this.adapter.hasClass(Wr.REQUIRED);this.notchOutline(i),this.adapter.floatLabel(i),this.adapter.setLabelRequired(o)}},e.prototype.layoutOptions=function(){var t=this.adapter.getMenuItemValues().indexOf(this.getValue());this.setSelectedIndex(t,!1,!0)},e.prototype.handleMenuOpened=function(){if(0!==this.adapter.getMenuItemValues().length){var t=this.getSelectedIndex(),e=t>=0?t:0;this.adapter.focusMenuItemAtIndex(e)}},e.prototype.handleMenuClosing=function(){this.adapter.setSelectAnchorAttr("aria-expanded","false")},e.prototype.handleMenuClosed=function(){this.adapter.removeClass(Wr.ACTIVATED),this.isMenuOpen=!1,this.adapter.isSelectAnchorFocused()||this.blur()},e.prototype.handleChange=function(){this.layout(),this.adapter.notifyChange(this.getValue()),this.adapter.hasClass(Wr.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.handleMenuItemAction=function(t){this.setSelectedIndex(t,!0)},e.prototype.handleFocus=function(){this.adapter.addClass(Wr.FOCUSED),this.layout(),this.adapter.activateBottomLine()},e.prototype.handleBlur=function(){this.isMenuOpen||this.blur()},e.prototype.handleClick=function(t){this.disabled||this.recentlyClicked||(this.setClickDebounceTimeout(),this.isMenuOpen?this.adapter.closeMenu():(this.adapter.setRippleCenter(t),this.openMenu()))},e.prototype.handleKeydown=function(t){if(!this.isMenuOpen&&this.adapter.hasClass(Wr.FOCUSED)){var e=Yr(t)===mr,i=Yr(t)===pr,o=Yr(t)===yr,n=Yr(t)===wr;if(!(t.ctrlKey||t.metaKey)&&(!i&&t.key&&1===t.key.length||i&&this.adapter.isTypeaheadInProgress())){var r=i?" ":t.key,a=this.adapter.typeaheadMatchItem(r,this.getSelectedIndex());return a>=0&&this.setSelectedIndex(a),void t.preventDefault()}(e||i||o||n)&&(this.openMenu(),t.preventDefault())}},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()){var e=this.adapter.hasClass(Wr.FOCUSED);if(t){var i=Kr.LABEL_SCALE,o=this.adapter.getLabelWidth()*i;this.adapter.notchOutline(o)}else e||this.adapter.closeOutline()}},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.getUseDefaultValidation=function(){return this.useDefaultValidation},e.prototype.setUseDefaultValidation=function(t){this.useDefaultValidation=t},e.prototype.setValid=function(t){this.useDefaultValidation||(this.customValidity=t),this.adapter.setSelectAnchorAttr("aria-invalid",(!t).toString()),t?(this.adapter.removeClass(Wr.INVALID),this.adapter.removeMenuClass(Wr.MENU_INVALID)):(this.adapter.addClass(Wr.INVALID),this.adapter.addMenuClass(Wr.MENU_INVALID)),this.syncHelperTextValidity(t)},e.prototype.isValid=function(){return this.useDefaultValidation&&this.adapter.hasClass(Wr.REQUIRED)&&!this.adapter.hasClass(Wr.DISABLED)?this.getSelectedIndex()!==Kr.UNSET_INDEX&&(0!==this.getSelectedIndex()||Boolean(this.getValue())):this.customValidity},e.prototype.setRequired=function(t){t?this.adapter.addClass(Wr.REQUIRED):this.adapter.removeClass(Wr.REQUIRED),this.adapter.setSelectAnchorAttr("aria-required",t.toString()),this.adapter.setLabelRequired(t)},e.prototype.getRequired=function(){return"true"===this.adapter.getSelectAnchorAttr("aria-required")},e.prototype.init=function(){var t=this.adapter.getAnchorElement();t&&(this.adapter.setMenuAnchorElement(t),this.adapter.setMenuAnchorCorner(Ur.BOTTOM_START)),this.adapter.setMenuWrapFocus(!1),this.setDisabled(this.adapter.hasClass(Wr.DISABLED)),this.syncHelperTextValidity(!this.adapter.hasClass(Wr.INVALID)),this.layout(),this.layoutOptions()},e.prototype.blur=function(){this.adapter.removeClass(Wr.FOCUSED),this.layout(),this.adapter.deactivateBottomLine(),this.adapter.hasClass(Wr.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.syncHelperTextValidity=function(t){if(this.helperText){this.helperText.setValidity(t);var e=this.helperText.isVisible(),i=this.helperText.getId();e&&i?this.adapter.setSelectAnchorAttr(Xr.ARIA_DESCRIBEDBY,i):this.adapter.removeSelectAnchorAttr(Xr.ARIA_DESCRIBEDBY)}},e.prototype.setClickDebounceTimeout=function(){var t=this;clearTimeout(this.clickDebounceTimeout),this.clickDebounceTimeout=setTimeout((function(){t.recentlyClicked=!1}),Kr.CLICK_DEBOUNCE_TIMEOUT_MS),this.recentlyClicked=!0},e}(dr); +var Xr={ACTIVATED:"mdc-select--activated",DISABLED:"mdc-select--disabled",FOCUSED:"mdc-select--focused",INVALID:"mdc-select--invalid",MENU_INVALID:"mdc-select__menu--invalid",OUTLINED:"mdc-select--outlined",REQUIRED:"mdc-select--required",ROOT:"mdc-select",WITH_LEADING_ICON:"mdc-select--with-leading-icon"},Kr={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",ARIA_SELECTED_ATTR:"aria-selected",CHANGE_EVENT:"MDCSelect:change",HIDDEN_INPUT_SELECTOR:'input[type="hidden"]',LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-select__icon",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",MENU_SELECTOR:".mdc-select__menu",OUTLINE_SELECTOR:".mdc-notched-outline",SELECTED_TEXT_SELECTOR:".mdc-select__selected-text",SELECT_ANCHOR_SELECTOR:".mdc-select__anchor",VALUE_ATTR:"data-value"},Gr={LABEL_SCALE:.75,UNSET_INDEX:-1,CLICK_DEBOUNCE_TIMEOUT_MS:330},qr=function(t){function e(i,n){void 0===n&&(n={});var r=t.call(this,o(o({},e.defaultAdapter),i))||this;return r.disabled=!1,r.isMenuOpen=!1,r.useDefaultValidation=!0,r.customValidity=!0,r.lastSelectedIndex=Gr.UNSET_INDEX,r.clickDebounceTimeout=0,r.recentlyClicked=!1,r.leadingIcon=n.leadingIcon,r.helperText=n.helperText,r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Xr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return Gr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Kr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},activateBottomLine:function(){},deactivateBottomLine:function(){},getSelectedIndex:function(){return-1},setSelectedIndex:function(){},hasLabel:function(){return!1},floatLabel:function(){},getLabelWidth:function(){return 0},setLabelRequired:function(){},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){},setRippleCenter:function(){},notifyChange:function(){},setSelectedText:function(){},isSelectAnchorFocused:function(){return!1},getSelectAnchorAttr:function(){return""},setSelectAnchorAttr:function(){},removeSelectAnchorAttr:function(){},addMenuClass:function(){},removeMenuClass:function(){},openMenu:function(){},closeMenu:function(){},getAnchorElement:function(){return null},setMenuAnchorElement:function(){},setMenuAnchorCorner:function(){},setMenuWrapFocus:function(){},focusMenuItemAtIndex:function(){},getMenuItemCount:function(){return 0},getMenuItemValues:function(){return[]},getMenuItemTextAtIndex:function(){return""},isTypeaheadInProgress:function(){return!1},typeaheadMatchItem:function(){return-1}}},enumerable:!1,configurable:!0}),e.prototype.getSelectedIndex=function(){return this.adapter.getSelectedIndex()},e.prototype.setSelectedIndex=function(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1),t>=this.adapter.getMenuItemCount()||(t===Gr.UNSET_INDEX?this.adapter.setSelectedText(""):this.adapter.setSelectedText(this.adapter.getMenuItemTextAtIndex(t).trim()),this.adapter.setSelectedIndex(t),e&&this.adapter.closeMenu(),i||this.lastSelectedIndex===t||this.handleChange(),this.lastSelectedIndex=t)},e.prototype.setValue=function(t,e){void 0===e&&(e=!1);var i=this.adapter.getMenuItemValues().indexOf(t);this.setSelectedIndex(i,!1,e)},e.prototype.getValue=function(){var t=this.adapter.getSelectedIndex(),e=this.adapter.getMenuItemValues();return t!==Gr.UNSET_INDEX?e[t]:""},e.prototype.getDisabled=function(){return this.disabled},e.prototype.setDisabled=function(t){this.disabled=t,this.disabled?(this.adapter.addClass(Xr.DISABLED),this.adapter.closeMenu()):this.adapter.removeClass(Xr.DISABLED),this.leadingIcon&&this.leadingIcon.setDisabled(this.disabled),this.disabled?this.adapter.removeSelectAnchorAttr("tabindex"):this.adapter.setSelectAnchorAttr("tabindex","0"),this.adapter.setSelectAnchorAttr("aria-disabled",this.disabled.toString())},e.prototype.openMenu=function(){this.adapter.addClass(Xr.ACTIVATED),this.adapter.openMenu(),this.isMenuOpen=!0,this.adapter.setSelectAnchorAttr("aria-expanded","true")},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.layout=function(){if(this.adapter.hasLabel()){var t=this.getValue().length>0,e=this.adapter.hasClass(Xr.FOCUSED),i=t||e,o=this.adapter.hasClass(Xr.REQUIRED);this.notchOutline(i),this.adapter.floatLabel(i),this.adapter.setLabelRequired(o)}},e.prototype.layoutOptions=function(){var t=this.adapter.getMenuItemValues().indexOf(this.getValue());this.setSelectedIndex(t,!1,!0)},e.prototype.handleMenuOpened=function(){if(0!==this.adapter.getMenuItemValues().length){var t=this.getSelectedIndex(),e=t>=0?t:0;this.adapter.focusMenuItemAtIndex(e)}},e.prototype.handleMenuClosing=function(){this.adapter.setSelectAnchorAttr("aria-expanded","false")},e.prototype.handleMenuClosed=function(){this.adapter.removeClass(Xr.ACTIVATED),this.isMenuOpen=!1,this.adapter.isSelectAnchorFocused()||this.blur()},e.prototype.handleChange=function(){this.layout(),this.adapter.notifyChange(this.getValue()),this.adapter.hasClass(Xr.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.handleMenuItemAction=function(t){this.setSelectedIndex(t,!0)},e.prototype.handleFocus=function(){this.adapter.addClass(Xr.FOCUSED),this.layout(),this.adapter.activateBottomLine()},e.prototype.handleBlur=function(){this.isMenuOpen||this.blur()},e.prototype.handleClick=function(t){this.disabled||this.recentlyClicked||(this.setClickDebounceTimeout(),this.isMenuOpen?this.adapter.closeMenu():(this.adapter.setRippleCenter(t),this.openMenu()))},e.prototype.handleKeydown=function(t){if(!this.isMenuOpen&&this.adapter.hasClass(Xr.FOCUSED)){var e=Wr(t)===pr,i=Wr(t)===fr,o=Wr(t)===xr,n=Wr(t)===kr;if(!(t.ctrlKey||t.metaKey)&&(!i&&t.key&&1===t.key.length||i&&this.adapter.isTypeaheadInProgress())){var r=i?" ":t.key,a=this.adapter.typeaheadMatchItem(r,this.getSelectedIndex());return a>=0&&this.setSelectedIndex(a),void t.preventDefault()}(e||i||o||n)&&(this.openMenu(),t.preventDefault())}},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()){var e=this.adapter.hasClass(Xr.FOCUSED);if(t){var i=Gr.LABEL_SCALE,o=this.adapter.getLabelWidth()*i;this.adapter.notchOutline(o)}else e||this.adapter.closeOutline()}},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.getUseDefaultValidation=function(){return this.useDefaultValidation},e.prototype.setUseDefaultValidation=function(t){this.useDefaultValidation=t},e.prototype.setValid=function(t){this.useDefaultValidation||(this.customValidity=t),this.adapter.setSelectAnchorAttr("aria-invalid",(!t).toString()),t?(this.adapter.removeClass(Xr.INVALID),this.adapter.removeMenuClass(Xr.MENU_INVALID)):(this.adapter.addClass(Xr.INVALID),this.adapter.addMenuClass(Xr.MENU_INVALID)),this.syncHelperTextValidity(t)},e.prototype.isValid=function(){return this.useDefaultValidation&&this.adapter.hasClass(Xr.REQUIRED)&&!this.adapter.hasClass(Xr.DISABLED)?this.getSelectedIndex()!==Gr.UNSET_INDEX&&(0!==this.getSelectedIndex()||Boolean(this.getValue())):this.customValidity},e.prototype.setRequired=function(t){t?this.adapter.addClass(Xr.REQUIRED):this.adapter.removeClass(Xr.REQUIRED),this.adapter.setSelectAnchorAttr("aria-required",t.toString()),this.adapter.setLabelRequired(t)},e.prototype.getRequired=function(){return"true"===this.adapter.getSelectAnchorAttr("aria-required")},e.prototype.init=function(){var t=this.adapter.getAnchorElement();t&&(this.adapter.setMenuAnchorElement(t),this.adapter.setMenuAnchorCorner(Hr.BOTTOM_START)),this.adapter.setMenuWrapFocus(!1),this.setDisabled(this.adapter.hasClass(Xr.DISABLED)),this.syncHelperTextValidity(!this.adapter.hasClass(Xr.INVALID)),this.layout(),this.layoutOptions()},e.prototype.blur=function(){this.adapter.removeClass(Xr.FOCUSED),this.layout(),this.adapter.deactivateBottomLine(),this.adapter.hasClass(Xr.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.syncHelperTextValidity=function(t){if(this.helperText){this.helperText.setValidity(t);var e=this.helperText.isVisible(),i=this.helperText.getId();e&&i?this.adapter.setSelectAnchorAttr(Kr.ARIA_DESCRIBEDBY,i):this.adapter.removeSelectAnchorAttr(Kr.ARIA_DESCRIBEDBY)}},e.prototype.setClickDebounceTimeout=function(){var t=this;clearTimeout(this.clickDebounceTimeout),this.clickDebounceTimeout=setTimeout((function(){t.recentlyClicked=!1}),Gr.CLICK_DEBOUNCE_TIMEOUT_MS),this.recentlyClicked=!0},e}(ur); /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -const qr=He(class extends Ye{constructor(t){var e;if(super(t),t.type!==Ve||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter((e=>t[e])).join(" ")+" "}update(t,[e]){var i,o;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter((t=>""!==t))));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach((t=>{t in e||(n.remove(t),this.it.delete(t))}));for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(o=this.nt)||void 0===o?void 0:o.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return X}}),Zr=t=>null!=t?t:K +const Zr=He(class extends Ye{constructor(t){var e;if(super(t),t.type!==Ve||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter((e=>t[e])).join(" ")+" "}update(t,[e]){var i,o;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter((t=>""!==t))));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach((t=>{t in e||(n.remove(t),this.it.delete(t))}));for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(o=this.nt)||void 0===o?void 0:o.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return X}}),Jr=t=>null!=t?t:K /** * @license * Copyright 2020 Google LLC * SPDX-License-Identifier: Apache-2.0 - */,Jr=(t={})=>{const e={};for(const i in t)e[i]=t[i];return Object.assign({badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:!0,valueMissing:!1},e)}; + */,Qr=(t={})=>{const e={};for(const i in t)e[i]=t[i];return Object.assign({badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:!0,valueMissing:!1},e)}; /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */class Qr extends tr{constructor(){super(...arguments),this.mdcFoundationClass=Gr,this.disabled=!1,this.outlined=!1,this.label="",this.outlineOpen=!1,this.outlineWidth=0,this.value="",this.name="",this.selectedText="",this.icon="",this.menuOpen=!1,this.helper="",this.validateOnInitialRender=!1,this.validationMessage="",this.required=!1,this.naturalMenuWidth=!1,this.isUiValid=!0,this.fixedMenuPosition=!1,this.typeaheadState={bufferClearTimeout:0,currentFirstChar:"",sortedIndexCursor:0,typeaheadBuffer:""},this.sortedIndexByFirstChar=new Map,this.menuElement_=null,this.listeners=[],this.onBodyClickBound=()=>{},this._menuUpdateComplete=null,this.valueSetDirectly=!1,this.validityTransform=null,this._validity=Jr()}get items(){return this.menuElement_||(this.menuElement_=this.menuElement),this.menuElement_?this.menuElement_.items:[]}get selected(){const t=this.menuElement;return t?t.selected:null}get index(){const t=this.menuElement;return t?t.index:-1}get shouldRenderHelperText(){return!!this.helper||!!this.validationMessage}get validity(){return this._checkValidity(this.value),this._validity}render(){const t={"mdc-select--disabled":this.disabled,"mdc-select--no-label":!this.label,"mdc-select--filled":!this.outlined,"mdc-select--outlined":this.outlined,"mdc-select--with-leading-icon":!!this.icon,"mdc-select--required":this.required,"mdc-select--invalid":!this.isUiValid},e=this.label?"label":void 0,i=this.shouldRenderHelperText?"helper-text":void 0;return Y` + */class ta extends er{constructor(){super(...arguments),this.mdcFoundationClass=qr,this.disabled=!1,this.outlined=!1,this.label="",this.outlineOpen=!1,this.outlineWidth=0,this.value="",this.name="",this.selectedText="",this.icon="",this.menuOpen=!1,this.helper="",this.validateOnInitialRender=!1,this.validationMessage="",this.required=!1,this.naturalMenuWidth=!1,this.isUiValid=!0,this.fixedMenuPosition=!1,this.typeaheadState={bufferClearTimeout:0,currentFirstChar:"",sortedIndexCursor:0,typeaheadBuffer:""},this.sortedIndexByFirstChar=new Map,this.menuElement_=null,this.listeners=[],this.onBodyClickBound=()=>{},this._menuUpdateComplete=null,this.valueSetDirectly=!1,this.validityTransform=null,this._validity=Qr()}get items(){return this.menuElement_||(this.menuElement_=this.menuElement),this.menuElement_?this.menuElement_.items:[]}get selected(){const t=this.menuElement;return t?t.selected:null}get index(){const t=this.menuElement;return t?t.index:-1}get shouldRenderHelperText(){return!!this.helper||!!this.validationMessage}get validity(){return this._checkValidity(this.value),this._validity}render(){const t={"mdc-select--disabled":this.disabled,"mdc-select--no-label":!this.label,"mdc-select--filled":!this.outlined,"mdc-select--outlined":this.outlined,"mdc-select--with-leading-icon":!!this.icon,"mdc-select--required":this.required,"mdc-select--invalid":!this.isUiValid},e=this.label?"label":void 0,i=this.shouldRenderHelperText?"helper-text":void 0;return Y`
    + class="mdc-select ${Zr(t)}"> `:K}renderLabel(){return this.label?Y` ${this.label} `:K}renderLeadingIcon(){return this.icon?Y`
    ${this.icon}
    `:K}renderLineRipple(){return this.outlined?K:Y` - + `}renderHelperText(){if(!this.shouldRenderHelperText)return K;const t=this.validationMessage&&!this.isUiValid;return Y`

    ${t?this.validationMessage:this.helper}

    `}createAdapter(){return Object.assign(Object.assign({},Xn(this.mdcRoot)),{activateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.activate()},deactivateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.deactivate()},hasLabel:()=>!!this.label,floatLabel:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.float(t)},getLabelWidth:()=>this.labelElement?this.labelElement.floatingLabelFoundation.getWidth():0,setLabelRequired:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.setRequired(t)},hasOutline:()=>this.outlined,notchOutline:t=>{this.outlineElement&&!this.outlineOpen&&(this.outlineWidth=t,this.outlineOpen=!0)},closeOutline:()=>{this.outlineElement&&(this.outlineOpen=!1)},setRippleCenter:t=>{if(this.lineRippleElement){this.lineRippleElement.lineRippleFoundation.setRippleCenter(t)}},notifyChange:async t=>{if(!this.valueSetDirectly&&t===this.value)return;this.valueSetDirectly=!1,this.value=t,await this.updateComplete;const e=new Event("change",{bubbles:!0});this.dispatchEvent(e)},setSelectedText:t=>this.selectedText=t,isSelectAnchorFocused:()=>{const t=this.anchorElement;if(!t)return!1;return t.getRootNode().activeElement===t},getSelectAnchorAttr:t=>{const e=this.anchorElement;return e?e.getAttribute(t):null},setSelectAnchorAttr:(t,e)=>{const i=this.anchorElement;i&&i.setAttribute(t,e)},removeSelectAnchorAttr:t=>{const e=this.anchorElement;e&&e.removeAttribute(t)},openMenu:()=>{this.menuOpen=!0},closeMenu:()=>{this.menuOpen=!1},addMenuClass:()=>{},removeMenuClass:()=>{},getAnchorElement:()=>this.anchorElement,setMenuAnchorElement:()=>{},setMenuAnchorCorner:()=>{const t=this.menuElement;t&&(t.corner="BOTTOM_START")},setMenuWrapFocus:t=>{const e=this.menuElement;e&&(e.wrapFocus=t)},focusMenuItemAtIndex:t=>{const e=this.menuElement;if(!e)return;const i=e.items[t];i&&i.focus()},getMenuItemCount:()=>{const t=this.menuElement;return t?t.items.length:0},getMenuItemValues:()=>{const t=this.menuElement;if(!t)return[];return t.items.map((t=>t.value))},getMenuItemTextAtIndex:t=>{const e=this.menuElement;if(!e)return"";const i=e.items[t];return i?i.text:""},getSelectedIndex:()=>this.index,setSelectedIndex:()=>{},isTypeaheadInProgress:()=>Wn(this.typeaheadState),typeaheadMatchItem:(t,e)=>{if(!this.menuElement)return-1;const i={focusItemAtIndex:t=>{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e||this.menuElement.getFocusedItemIndex(),nextChar:t,sortedIndexByFirstChar:this.sortedIndexByFirstChar,skipFocus:!1,isItemAtIndexDisabled:t=>this.items[t].disabled},o=Yn(i,this.typeaheadState);return-1!==o&&this.select(o),o}})}checkValidity(){const t=this._checkValidity(this.value);if(!t){const t=new Event("invalid",{bubbles:!1,cancelable:!0});this.dispatchEvent(t)}return t}reportValidity(){const t=this.checkValidity();return this.isUiValid=t,t}_checkValidity(t){const e=this.formElement.validity;let i=Jr(e);if(this.validityTransform){const e=this.validityTransform(t,i);i=Object.assign(Object.assign({},i),e)}return this._validity=i,this._validity.valid}setCustomValidity(t){this.validationMessage=t,this.formElement.setCustomValidity(t)}async getUpdateComplete(){await this._menuUpdateComplete;return await super.getUpdateComplete()}async firstUpdated(){const t=this.menuElement;if(t&&(this._menuUpdateComplete=t.updateComplete,await this._menuUpdateComplete),super.firstUpdated(),this.mdcFoundation.isValid=()=>!0,this.mdcFoundation.setValid=()=>{},this.mdcFoundation.setDisabled(this.disabled),this.validateOnInitialRender&&this.reportValidity(),!this.selected){!this.items.length&&this.slotElement&&this.slotElement.assignedNodes({flatten:!0}).length&&(await new Promise((t=>requestAnimationFrame(t))),await this.layout());const t=this.items.length&&""===this.items[0].value;if(!this.value&&t)return void this.select(0);this.selectByValue(this.value)}this.sortedIndexByFirstChar=Hn(this.items.length,(t=>this.items[t].text))}onItemsUpdated(){this.sortedIndexByFirstChar=Hn(this.items.length,(t=>this.items[t].text))}select(t){const e=this.menuElement;e&&e.select(t)}selectByValue(t){let e=-1;for(let i=0;i0,n=i&&this.index{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e,isTargetListItem:!!i&&i.hasAttribute("mwc-list-item"),sortedIndexByFirstChar:this.sortedIndexByFirstChar,isItemAtIndexDisabled:t=>this.items[t].disabled};!function(t,e){var i=t.event,o=t.isTargetListItem,n=t.focusedItemIndex,r=t.focusItemAtIndex,a=t.sortedIndexByFirstChar,l=t.isItemAtIndexDisabled,s="ArrowLeft"===zn(i),c="ArrowUp"===zn(i),d="ArrowRight"===zn(i),u="ArrowDown"===zn(i),h="Home"===zn(i),m="End"===zn(i),p="Enter"===zn(i),f="Spacebar"===zn(i);i.altKey||i.ctrlKey||i.metaKey||s||c||d||u||h||m||p||(f||1!==i.key.length?f&&(o&&Un(i),o&&Wn(e)&&Yn({focusItemAtIndex:r,focusedItemIndex:n,nextChar:" ",sortedIndexByFirstChar:a,skipFocus:!1,isItemAtIndexDisabled:l},e)):(Un(i),Yn({focusItemAtIndex:r,focusedItemIndex:n,nextChar:i.key.toLowerCase(),sortedIndexByFirstChar:a,skipFocus:!1,isItemAtIndexDisabled:l},e)))} + class="mdc-select-helper-text ${Zr({"mdc-select-helper-text--validation-msg":t})}" + id="helper-text">${t?this.validationMessage:this.helper}

    `}createAdapter(){return Object.assign(Object.assign({},Kn(this.mdcRoot)),{activateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.activate()},deactivateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.deactivate()},hasLabel:()=>!!this.label,floatLabel:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.float(t)},getLabelWidth:()=>this.labelElement?this.labelElement.floatingLabelFoundation.getWidth():0,setLabelRequired:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.setRequired(t)},hasOutline:()=>this.outlined,notchOutline:t=>{this.outlineElement&&!this.outlineOpen&&(this.outlineWidth=t,this.outlineOpen=!0)},closeOutline:()=>{this.outlineElement&&(this.outlineOpen=!1)},setRippleCenter:t=>{if(this.lineRippleElement){this.lineRippleElement.lineRippleFoundation.setRippleCenter(t)}},notifyChange:async t=>{if(!this.valueSetDirectly&&t===this.value)return;this.valueSetDirectly=!1,this.value=t,await this.updateComplete;const e=new Event("change",{bubbles:!0});this.dispatchEvent(e)},setSelectedText:t=>this.selectedText=t,isSelectAnchorFocused:()=>{const t=this.anchorElement;if(!t)return!1;return t.getRootNode().activeElement===t},getSelectAnchorAttr:t=>{const e=this.anchorElement;return e?e.getAttribute(t):null},setSelectAnchorAttr:(t,e)=>{const i=this.anchorElement;i&&i.setAttribute(t,e)},removeSelectAnchorAttr:t=>{const e=this.anchorElement;e&&e.removeAttribute(t)},openMenu:()=>{this.menuOpen=!0},closeMenu:()=>{this.menuOpen=!1},addMenuClass:()=>{},removeMenuClass:()=>{},getAnchorElement:()=>this.anchorElement,setMenuAnchorElement:()=>{},setMenuAnchorCorner:()=>{const t=this.menuElement;t&&(t.corner="BOTTOM_START")},setMenuWrapFocus:t=>{const e=this.menuElement;e&&(e.wrapFocus=t)},focusMenuItemAtIndex:t=>{const e=this.menuElement;if(!e)return;const i=e.items[t];i&&i.focus()},getMenuItemCount:()=>{const t=this.menuElement;return t?t.items.length:0},getMenuItemValues:()=>{const t=this.menuElement;if(!t)return[];return t.items.map((t=>t.value))},getMenuItemTextAtIndex:t=>{const e=this.menuElement;if(!e)return"";const i=e.items[t];return i?i.text:""},getSelectedIndex:()=>this.index,setSelectedIndex:()=>{},isTypeaheadInProgress:()=>Xn(this.typeaheadState),typeaheadMatchItem:(t,e)=>{if(!this.menuElement)return-1;const i={focusItemAtIndex:t=>{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e||this.menuElement.getFocusedItemIndex(),nextChar:t,sortedIndexByFirstChar:this.sortedIndexByFirstChar,skipFocus:!1,isItemAtIndexDisabled:t=>this.items[t].disabled},o=Wn(i,this.typeaheadState);return-1!==o&&this.select(o),o}})}checkValidity(){const t=this._checkValidity(this.value);if(!t){const t=new Event("invalid",{bubbles:!1,cancelable:!0});this.dispatchEvent(t)}return t}reportValidity(){const t=this.checkValidity();return this.isUiValid=t,t}_checkValidity(t){const e=this.formElement.validity;let i=Qr(e);if(this.validityTransform){const e=this.validityTransform(t,i);i=Object.assign(Object.assign({},i),e)}return this._validity=i,this._validity.valid}setCustomValidity(t){this.validationMessage=t,this.formElement.setCustomValidity(t)}async getUpdateComplete(){await this._menuUpdateComplete;return await super.getUpdateComplete()}async firstUpdated(){const t=this.menuElement;if(t&&(this._menuUpdateComplete=t.updateComplete,await this._menuUpdateComplete),super.firstUpdated(),this.mdcFoundation.isValid=()=>!0,this.mdcFoundation.setValid=()=>{},this.mdcFoundation.setDisabled(this.disabled),this.validateOnInitialRender&&this.reportValidity(),!this.selected){!this.items.length&&this.slotElement&&this.slotElement.assignedNodes({flatten:!0}).length&&(await new Promise((t=>requestAnimationFrame(t))),await this.layout());const t=this.items.length&&""===this.items[0].value;if(!this.value&&t)return void this.select(0);this.selectByValue(this.value)}this.sortedIndexByFirstChar=Yn(this.items.length,(t=>this.items[t].text))}onItemsUpdated(){this.sortedIndexByFirstChar=Yn(this.items.length,(t=>this.items[t].text))}select(t){const e=this.menuElement;e&&e.select(t)}selectByValue(t){let e=-1;for(let i=0;i0,n=i&&this.index{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e,isTargetListItem:!!i&&i.hasAttribute("mwc-list-item"),sortedIndexByFirstChar:this.sortedIndexByFirstChar,isItemAtIndexDisabled:t=>this.items[t].disabled};!function(t,e){var i=t.event,o=t.isTargetListItem,n=t.focusedItemIndex,r=t.focusItemAtIndex,a=t.sortedIndexByFirstChar,s=t.isItemAtIndexDisabled,l="ArrowLeft"===Mn(i),c="ArrowUp"===Mn(i),d="ArrowRight"===Mn(i),u="ArrowDown"===Mn(i),h="Home"===Mn(i),m="End"===Mn(i),p="Enter"===Mn(i),f="Spacebar"===Mn(i);i.altKey||i.ctrlKey||i.metaKey||l||c||d||u||h||m||p||(f||1!==i.key.length?f&&(o&&Hn(i),o&&Xn(e)&&Wn({focusItemAtIndex:r,focusedItemIndex:n,nextChar:" ",sortedIndexByFirstChar:a,skipFocus:!1,isItemAtIndexDisabled:s},e)):(Hn(i),Wn({focusItemAtIndex:r,focusedItemIndex:n,nextChar:i.key.toLowerCase(),sortedIndexByFirstChar:a,skipFocus:!1,isItemAtIndexDisabled:s},e)))} /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: Apache-2.0 - */(o,this.typeaheadState)}async onSelected(t){this.mdcFoundation||await this.updateComplete,this.mdcFoundation.handleMenuItemAction(t.detail.index);const e=this.items[t.detail.index];e&&(this.value=e.value)}onOpened(){this.mdcFoundation&&(this.menuOpen=!0,this.mdcFoundation.handleMenuOpened())}onClosed(){this.mdcFoundation&&(this.menuOpen=!1,this.mdcFoundation.handleMenuClosed())}setFormData(t){this.name&&null!==this.selected&&t.append(this.name,this.value)}async layout(t=!0){this.mdcFoundation&&this.mdcFoundation.layout(),await this.updateComplete;const e=this.menuElement;e&&e.layout(t);const i=this.labelElement;if(!i)return void(this.outlineOpen=!1);const o=!!this.label&&!!this.value;if(i.floatingLabelFoundation.float(o),!this.outlined)return;this.outlineOpen=o,await this.updateComplete;const n=i.floatingLabelFoundation.getWidth();this.outlineOpen&&(this.outlineWidth=n)}async layoutOptions(){this.mdcFoundation&&this.mdcFoundation.layoutOptions()}}n([xt(".mdc-select")],Qr.prototype,"mdcRoot",void 0),n([xt(".formElement")],Qr.prototype,"formElement",void 0),n([xt("slot")],Qr.prototype,"slotElement",void 0),n([xt("select")],Qr.prototype,"nativeSelectElement",void 0),n([xt("input")],Qr.prototype,"nativeInputElement",void 0),n([xt(".mdc-line-ripple")],Qr.prototype,"lineRippleElement",void 0),n([xt(".mdc-floating-label")],Qr.prototype,"labelElement",void 0),n([xt("mwc-notched-outline")],Qr.prototype,"outlineElement",void 0),n([xt(".mdc-menu")],Qr.prototype,"menuElement",void 0),n([xt(".mdc-select__anchor")],Qr.prototype,"anchorElement",void 0),n([_t({type:Boolean,attribute:"disabled",reflect:!0}),er((function(t){this.mdcFoundation&&this.mdcFoundation.setDisabled(t)}))],Qr.prototype,"disabled",void 0),n([_t({type:Boolean}),er((function(t,e){void 0!==e&&this.outlined!==e&&this.layout(!1)}))],Qr.prototype,"outlined",void 0),n([_t({type:String}),er((function(t,e){void 0!==e&&this.label!==e&&this.layout(!1)}))],Qr.prototype,"label",void 0),n([vt()],Qr.prototype,"outlineOpen",void 0),n([vt()],Qr.prototype,"outlineWidth",void 0),n([_t({type:String}),er((function(t){if(this.mdcFoundation){const e=null===this.selected&&!!t,i=this.selected&&this.selected.value!==t;(e||i)&&this.selectByValue(t),this.reportValidity()}}))],Qr.prototype,"value",void 0),n([_t()],Qr.prototype,"name",void 0),n([vt()],Qr.prototype,"selectedText",void 0),n([_t({type:String})],Qr.prototype,"icon",void 0),n([vt()],Qr.prototype,"menuOpen",void 0),n([_t({type:String})],Qr.prototype,"helper",void 0),n([_t({type:Boolean})],Qr.prototype,"validateOnInitialRender",void 0),n([_t({type:String})],Qr.prototype,"validationMessage",void 0),n([_t({type:Boolean})],Qr.prototype,"required",void 0),n([_t({type:Boolean})],Qr.prototype,"naturalMenuWidth",void 0),n([vt()],Qr.prototype,"isUiValid",void 0),n([_t({type:Boolean})],Qr.prototype,"fixedMenuPosition",void 0),n([yt({capture:!0})],Qr.prototype,"handleTypeahead",null); + */(o,this.typeaheadState)}async onSelected(t){this.mdcFoundation||await this.updateComplete,this.mdcFoundation.handleMenuItemAction(t.detail.index);const e=this.items[t.detail.index];e&&(this.value=e.value)}onOpened(){this.mdcFoundation&&(this.menuOpen=!0,this.mdcFoundation.handleMenuOpened())}onClosed(){this.mdcFoundation&&(this.menuOpen=!1,this.mdcFoundation.handleMenuClosed())}setFormData(t){this.name&&null!==this.selected&&t.append(this.name,this.value)}async layout(t=!0){this.mdcFoundation&&this.mdcFoundation.layout(),await this.updateComplete;const e=this.menuElement;e&&e.layout(t);const i=this.labelElement;if(!i)return void(this.outlineOpen=!1);const o=!!this.label&&!!this.value;if(i.floatingLabelFoundation.float(o),!this.outlined)return;this.outlineOpen=o,await this.updateComplete;const n=i.floatingLabelFoundation.getWidth();this.outlineOpen&&(this.outlineWidth=n)}async layoutOptions(){this.mdcFoundation&&this.mdcFoundation.layoutOptions()}}n([xt(".mdc-select")],ta.prototype,"mdcRoot",void 0),n([xt(".formElement")],ta.prototype,"formElement",void 0),n([xt("slot")],ta.prototype,"slotElement",void 0),n([xt("select")],ta.prototype,"nativeSelectElement",void 0),n([xt("input")],ta.prototype,"nativeInputElement",void 0),n([xt(".mdc-line-ripple")],ta.prototype,"lineRippleElement",void 0),n([xt(".mdc-floating-label")],ta.prototype,"labelElement",void 0),n([xt("mwc-notched-outline")],ta.prototype,"outlineElement",void 0),n([xt(".mdc-menu")],ta.prototype,"menuElement",void 0),n([xt(".mdc-select__anchor")],ta.prototype,"anchorElement",void 0),n([_t({type:Boolean,attribute:"disabled",reflect:!0}),ir((function(t){this.mdcFoundation&&this.mdcFoundation.setDisabled(t)}))],ta.prototype,"disabled",void 0),n([_t({type:Boolean}),ir((function(t,e){void 0!==e&&this.outlined!==e&&this.layout(!1)}))],ta.prototype,"outlined",void 0),n([_t({type:String}),ir((function(t,e){void 0!==e&&this.label!==e&&this.layout(!1)}))],ta.prototype,"label",void 0),n([vt()],ta.prototype,"outlineOpen",void 0),n([vt()],ta.prototype,"outlineWidth",void 0),n([_t({type:String}),ir((function(t){if(this.mdcFoundation){const e=null===this.selected&&!!t,i=this.selected&&this.selected.value!==t;(e||i)&&this.selectByValue(t),this.reportValidity()}}))],ta.prototype,"value",void 0),n([_t()],ta.prototype,"name",void 0),n([vt()],ta.prototype,"selectedText",void 0),n([_t({type:String})],ta.prototype,"icon",void 0),n([vt()],ta.prototype,"menuOpen",void 0),n([_t({type:String})],ta.prototype,"helper",void 0),n([_t({type:Boolean})],ta.prototype,"validateOnInitialRender",void 0),n([_t({type:String})],ta.prototype,"validationMessage",void 0),n([_t({type:Boolean})],ta.prototype,"required",void 0),n([_t({type:Boolean})],ta.prototype,"naturalMenuWidth",void 0),n([vt()],ta.prototype,"isUiValid",void 0),n([_t({type:Boolean})],ta.prototype,"fixedMenuPosition",void 0),n([yt({capture:!0})],ta.prototype,"handleTypeahead",null); /** * @license * Copyright 2021 Google LLC * SPDX-LIcense-Identifier: Apache-2.0 */ -const ta=h`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}.mdc-select{display:inline-flex;position:relative}.mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87)}.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-select.mdc-select--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#6200ee;fill:var(--mdc-theme-primary, #6200ee)}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled)+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__icon{color:rgba(0, 0, 0, 0.54)}.mdc-select.mdc-select--disabled .mdc-select__icon{color:rgba(0, 0, 0, 0.38)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:red}.mdc-select.mdc-select--disabled .mdc-floating-label{color:GrayText}.mdc-select.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}.mdc-select.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select.mdc-select--disabled .mdc-notched-outline__trailing{border-color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__icon{color:GrayText}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:GrayText}}.mdc-select .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-select .mdc-select__anchor{padding-left:16px;padding-right:0}[dir=rtl] .mdc-select .mdc-select__anchor,.mdc-select .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:16px}.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor{padding-left:0;padding-right:0}[dir=rtl] .mdc-select.mdc-select--with-leading-icon .mdc-select__anchor,.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:0}.mdc-select .mdc-select__icon{width:24px;height:24px;font-size:24px}.mdc-select .mdc-select__dropdown-icon{width:24px;height:24px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item,.mdc-select .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic{margin-left:0;margin-right:12px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic,.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:12px;margin-right:0}.mdc-select__dropdown-icon{margin-left:12px;margin-right:12px;display:inline-flex;position:relative;align-self:center;align-items:center;justify-content:center;flex-shrink:0;pointer-events:none}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active,.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{position:absolute;top:0;left:0}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-graphic{width:41.6666666667%;height:20.8333333333%}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:1;transition:opacity 75ms linear 75ms}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:0;transition:opacity 75ms linear}[dir=rtl] .mdc-select__dropdown-icon,.mdc-select__dropdown-icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:0;transition:opacity 49.5ms linear}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:1;transition:opacity 100.5ms linear 49.5ms}.mdc-select__anchor{width:200px;min-width:0;flex:1 1 auto;position:relative;box-sizing:border-box;overflow:hidden;outline:none;cursor:pointer}.mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-select__selected-text-container{display:flex;appearance:none;pointer-events:none;box-sizing:border-box;width:auto;min-width:0;flex-grow:1;height:28px;border:none;outline:none;padding:0;background-color:transparent;color:inherit}.mdc-select__selected-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);line-height:1.75rem;line-height:var(--mdc-typography-subtitle1-line-height, 1.75rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;width:100%;text-align:left}[dir=rtl] .mdc-select__selected-text,.mdc-select__selected-text[dir=rtl]{text-align:right}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--invalid+.mdc-select-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--disabled{cursor:default;pointer-events:none}.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item{padding-left:12px;padding-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item,.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:12px;padding-right:12px}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select__menu::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid transparent;border-radius:inherit;content:"";pointer-events:none}}@media screen and (forced-colors: active)and (forced-colors: active),screen and (-ms-high-contrast: active)and (forced-colors: active){.mdc-select__menu::before{border-color:CanvasText}}.mdc-select__menu .mdc-deprecated-list .mdc-select__icon,.mdc-select__menu .mdc-list .mdc-select__icon{margin-left:0;margin-right:0}[dir=rtl] .mdc-select__menu .mdc-deprecated-list .mdc-select__icon,[dir=rtl] .mdc-select__menu .mdc-list .mdc-select__icon,.mdc-select__menu .mdc-deprecated-list .mdc-select__icon[dir=rtl],.mdc-select__menu .mdc-list .mdc-select__icon[dir=rtl]{margin-left:0;margin-right:0}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-list-item__start{display:inline-flex;align-items:center}.mdc-select__option{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select__option,.mdc-select__option[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select__one-line-option.mdc-list-item--with-one-line{height:48px}.mdc-select__two-line-option.mdc-list-item--with-two-lines{height:64px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__start{margin-top:20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before{display:inline-block;width:0;height:28px;content:"";vertical-align:0}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end{display:block;margin-top:0;line-height:normal}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{display:inline-block;width:0;height:36px;content:"";vertical-align:0}.mdc-select__option-with-leading-content{padding-left:0;padding-right:12px}.mdc-select__option-with-leading-content.mdc-list-item{padding-left:0;padding-right:auto}[dir=rtl] .mdc-select__option-with-leading-content.mdc-list-item,.mdc-select__option-with-leading-content.mdc-list-item[dir=rtl]{padding-left:auto;padding-right:0}.mdc-select__option-with-leading-content .mdc-list-item__start{margin-left:12px;margin-right:0}[dir=rtl] .mdc-select__option-with-leading-content .mdc-list-item__start,.mdc-select__option-with-leading-content .mdc-list-item__start[dir=rtl]{margin-left:0;margin-right:12px}.mdc-select__option-with-leading-content .mdc-list-item__start{width:36px;height:24px}[dir=rtl] .mdc-select__option-with-leading-content,.mdc-select__option-with-leading-content[dir=rtl]{padding-left:12px;padding-right:0}.mdc-select__option-with-meta.mdc-list-item{padding-left:auto;padding-right:0}[dir=rtl] .mdc-select__option-with-meta.mdc-list-item,.mdc-select__option-with-meta.mdc-list-item[dir=rtl]{padding-left:0;padding-right:auto}.mdc-select__option-with-meta .mdc-list-item__end{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select__option-with-meta .mdc-list-item__end,.mdc-select__option-with-meta .mdc-list-item__end[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--filled .mdc-select__anchor{height:56px;display:flex;align-items:baseline}.mdc-select--filled .mdc-select__anchor::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor::before{display:none}.mdc-select--filled .mdc-select__anchor{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0}.mdc-select--filled:not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke}.mdc-select--filled.mdc-select--disabled .mdc-select__anchor{background-color:#fafafa}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-select--filled:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--filled.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-select--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-select--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-select--filled .mdc-menu-surface--is-open-below{border-top-left-radius:0px;border-top-right-radius:0px}.mdc-select--filled.mdc-select--focused.mdc-line-ripple::after{transform:scale(1, 2);opacity:1}.mdc-select--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-select--filled .mdc-floating-label,.mdc-select--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{left:48px;right:initial}[dir=rtl] .mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined{border:none}.mdc-select--outlined .mdc-select__anchor{height:56px}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-56px{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-select--outlined .mdc-select__anchor{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-select--outlined+.mdc-select-helper-text{margin-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-select__anchor{background-color:transparent}.mdc-select--outlined.mdc-select--disabled .mdc-select__anchor{background-color:transparent}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}.mdc-select--outlined .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-select--outlined .mdc-select__anchor{display:flex;align-items:baseline;overflow:visible}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined 250ms 1}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--outlined .mdc-select__anchor::before{display:none}.mdc-select--outlined .mdc-select__selected-text-container{display:flex;border:none;z-index:1;background-color:transparent}.mdc-select--outlined .mdc-select__icon{z-index:2}.mdc-select--outlined .mdc-floating-label{line-height:1.15rem;left:4px;right:initial}[dir=rtl] .mdc-select--outlined .mdc-floating-label,.mdc-select--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-select--outlined.mdc-select--focused .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake,.mdc-select--outlined.mdc-select--with-leading-icon[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 96px)}.mdc-select--outlined .mdc-menu-surface{margin-bottom:8px}.mdc-select--outlined.mdc-select--no-label .mdc-menu-surface,.mdc-select--outlined .mdc-menu-surface--is-open-below{margin-bottom:0}.mdc-select__anchor{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-select__anchor .mdc-select__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-select__anchor .mdc-select__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-select__anchor.mdc-ripple-upgraded--unbounded .mdc-select__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-select__anchor.mdc-ripple-upgraded--foreground-activation .mdc-select__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-select__anchor.mdc-ripple-upgraded--foreground-deactivation .mdc-select__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-select__anchor:hover .mdc-select__ripple::before,.mdc-select__anchor.mdc-ripple-surface--hover .mdc-select__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__anchor.mdc-ripple-upgraded--background-focused .mdc-select__ripple::before,.mdc-select__anchor:not(.mdc-ripple-upgraded):focus .mdc-select__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__anchor .mdc-select__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-deprecated-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-deprecated-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-deprecated-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-deprecated-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select-helper-text{margin:0;margin-left:16px;margin-right:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal}[dir=rtl] .mdc-select-helper-text,.mdc-select-helper-text[dir=rtl]{margin-left:16px;margin-right:16px}.mdc-select-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-select-helper-text--validation-msg{opacity:0;transition:opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-select--invalid+.mdc-select-helper-text--validation-msg,.mdc-select-helper-text--validation-msg-persistent{opacity:1}.mdc-select--with-leading-icon .mdc-select__icon{display:inline-block;box-sizing:border-box;border:none;text-decoration:none;cursor:pointer;user-select:none;flex-shrink:0;align-self:center;background-color:transparent;fill:currentColor}.mdc-select--with-leading-icon .mdc-select__icon{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__icon,.mdc-select--with-leading-icon .mdc-select__icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select__icon:not([tabindex]),.mdc-select__icon[tabindex="-1"]{cursor:default;pointer-events:none}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-block;vertical-align:top;outline:none}.mdc-select{width:100%}[hidden]{display:none}.mdc-select__icon{z-index:2}.mdc-select--with-leading-icon{--mdc-list-item-graphic-margin: calc( 48px - var(--mdc-list-item-graphic-size, 24px) - var(--mdc-list-side-padding, 16px) )}.mdc-select .mdc-select__anchor .mdc-select__selected-text{overflow:hidden}.mdc-select .mdc-select__anchor *{display:inline-flex}.mdc-select .mdc-select__anchor .mdc-floating-label{display:inline-block}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-idle-border-color, rgba(0, 0, 0, 0.38) );--mdc-notched-outline-notch-offset: 1px}:host(:not([disabled]):hover) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87);color:var(--mdc-select-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-select-idle-line-color, rgba(0, 0, 0, 0.42))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-select-hover-line-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--outlined):not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke;background-color:var(--mdc-select-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-select__dropdown-icon{fill:var(--mdc-select-error-dropdown-icon-color, var(--mdc-select-error-color, var(--mdc-theme-error, #b00020)))}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label,:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label::after{color:var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select.mdc-select--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}.mdc-select__menu--invalid{--mdc-theme-primary: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.6);color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54);fill:var(--mdc-select-dropdown-icon-color, rgba(0, 0, 0, 0.54))}:host(:not([disabled])) .mdc-select.mdc-select--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px;--mdc-notched-outline-notch-offset: 2px}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-select__dropdown-icon{fill:rgba(98,0,238,.87);fill:var(--mdc-select-focused-dropdown-icon-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)))}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label::after{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select-helper-text:not(.mdc-select-helper-text--validation-msg){color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]){pointer-events:none}:host([disabled]) .mdc-select:not(.mdc-select--outlined).mdc-select--disabled .mdc-select__anchor{background-color:#fafafa;background-color:var(--mdc-select-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-select.mdc-select--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-select .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38);fill:var(--mdc-select-disabled-dropdown-icon-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select-helper-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}`;let ea=class extends Qr{constructor(){super(...arguments),this._translationsUpdated=Te((async()=>{await ze(),this.layoutOptions()}),500)}renderLeadingIcon(){return this.icon?Y``:K}connectedCallback(){super.connectedCallback(),window.addEventListener("translations-updated",this._translationsUpdated)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("translations-updated",this._translationsUpdated)}};ea.styles=[ta,h` - .mdc-select__anchor { - height: var(--select-height, 56px) !important; - } - `],n([_t({type:Boolean})],ea.prototype,"icon",void 0),ea=n([pt("mushroom-select")],ea);const ia=["default","start","center","end","justify"],oa={default:"mdi:format-align-left",start:"mdi:format-align-left",center:"mdi:format-align-center",end:"mdi:format-align-right",justify:"mdi:format-align-justify"};let na=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){const t=Oo(this.hass),e=this.value||"default";return Y` - t.stopPropagation()} - .value=${this.value||"default"} - fixedMenuPosition - naturalMenuWidth - > - - ${ia.map((e=>Y` - - ${t(`editor.form.alignment_picker.values.${e}`)} - - - `))} - - `}static get styles(){return h` - mushroom-select { - width: 100%; - } - `}};n([_t()],na.prototype,"label",void 0),n([_t()],na.prototype,"value",void 0),n([_t()],na.prototype,"configValue",void 0),n([_t()],na.prototype,"hass",void 0),na=n([pt("mushroom-alignment-picker")],na);let ra=class extends ht{render(){return Y` - - `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],ra.prototype,"hass",void 0),n([_t()],ra.prototype,"selector",void 0),n([_t()],ra.prototype,"value",void 0),n([_t()],ra.prototype,"label",void 0),ra=n([pt("ha-selector-mush_alignment")],ra); +const ea=h`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}.mdc-select{display:inline-flex;position:relative}.mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87)}.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-select.mdc-select--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#6200ee;fill:var(--mdc-theme-primary, #6200ee)}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled)+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__icon{color:rgba(0, 0, 0, 0.54)}.mdc-select.mdc-select--disabled .mdc-select__icon{color:rgba(0, 0, 0, 0.38)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:red}.mdc-select.mdc-select--disabled .mdc-floating-label{color:GrayText}.mdc-select.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}.mdc-select.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select.mdc-select--disabled .mdc-notched-outline__trailing{border-color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__icon{color:GrayText}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:GrayText}}.mdc-select .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-select .mdc-select__anchor{padding-left:16px;padding-right:0}[dir=rtl] .mdc-select .mdc-select__anchor,.mdc-select .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:16px}.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor{padding-left:0;padding-right:0}[dir=rtl] .mdc-select.mdc-select--with-leading-icon .mdc-select__anchor,.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:0}.mdc-select .mdc-select__icon{width:24px;height:24px;font-size:24px}.mdc-select .mdc-select__dropdown-icon{width:24px;height:24px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item,.mdc-select .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic{margin-left:0;margin-right:12px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic,.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:12px;margin-right:0}.mdc-select__dropdown-icon{margin-left:12px;margin-right:12px;display:inline-flex;position:relative;align-self:center;align-items:center;justify-content:center;flex-shrink:0;pointer-events:none}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active,.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{position:absolute;top:0;left:0}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-graphic{width:41.6666666667%;height:20.8333333333%}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:1;transition:opacity 75ms linear 75ms}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:0;transition:opacity 75ms linear}[dir=rtl] .mdc-select__dropdown-icon,.mdc-select__dropdown-icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:0;transition:opacity 49.5ms linear}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:1;transition:opacity 100.5ms linear 49.5ms}.mdc-select__anchor{width:200px;min-width:0;flex:1 1 auto;position:relative;box-sizing:border-box;overflow:hidden;outline:none;cursor:pointer}.mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-select__selected-text-container{display:flex;appearance:none;pointer-events:none;box-sizing:border-box;width:auto;min-width:0;flex-grow:1;height:28px;border:none;outline:none;padding:0;background-color:transparent;color:inherit}.mdc-select__selected-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);line-height:1.75rem;line-height:var(--mdc-typography-subtitle1-line-height, 1.75rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;width:100%;text-align:left}[dir=rtl] .mdc-select__selected-text,.mdc-select__selected-text[dir=rtl]{text-align:right}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--invalid+.mdc-select-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--disabled{cursor:default;pointer-events:none}.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item{padding-left:12px;padding-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item,.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:12px;padding-right:12px}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select__menu::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid transparent;border-radius:inherit;content:"";pointer-events:none}}@media screen and (forced-colors: active)and (forced-colors: active),screen and (-ms-high-contrast: active)and (forced-colors: active){.mdc-select__menu::before{border-color:CanvasText}}.mdc-select__menu .mdc-deprecated-list .mdc-select__icon,.mdc-select__menu .mdc-list .mdc-select__icon{margin-left:0;margin-right:0}[dir=rtl] .mdc-select__menu .mdc-deprecated-list .mdc-select__icon,[dir=rtl] .mdc-select__menu .mdc-list .mdc-select__icon,.mdc-select__menu .mdc-deprecated-list .mdc-select__icon[dir=rtl],.mdc-select__menu .mdc-list .mdc-select__icon[dir=rtl]{margin-left:0;margin-right:0}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-list-item__start{display:inline-flex;align-items:center}.mdc-select__option{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select__option,.mdc-select__option[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select__one-line-option.mdc-list-item--with-one-line{height:48px}.mdc-select__two-line-option.mdc-list-item--with-two-lines{height:64px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__start{margin-top:20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before{display:inline-block;width:0;height:28px;content:"";vertical-align:0}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end{display:block;margin-top:0;line-height:normal}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{display:inline-block;width:0;height:36px;content:"";vertical-align:0}.mdc-select__option-with-leading-content{padding-left:0;padding-right:12px}.mdc-select__option-with-leading-content.mdc-list-item{padding-left:0;padding-right:auto}[dir=rtl] .mdc-select__option-with-leading-content.mdc-list-item,.mdc-select__option-with-leading-content.mdc-list-item[dir=rtl]{padding-left:auto;padding-right:0}.mdc-select__option-with-leading-content .mdc-list-item__start{margin-left:12px;margin-right:0}[dir=rtl] .mdc-select__option-with-leading-content .mdc-list-item__start,.mdc-select__option-with-leading-content .mdc-list-item__start[dir=rtl]{margin-left:0;margin-right:12px}.mdc-select__option-with-leading-content .mdc-list-item__start{width:36px;height:24px}[dir=rtl] .mdc-select__option-with-leading-content,.mdc-select__option-with-leading-content[dir=rtl]{padding-left:12px;padding-right:0}.mdc-select__option-with-meta.mdc-list-item{padding-left:auto;padding-right:0}[dir=rtl] .mdc-select__option-with-meta.mdc-list-item,.mdc-select__option-with-meta.mdc-list-item[dir=rtl]{padding-left:0;padding-right:auto}.mdc-select__option-with-meta .mdc-list-item__end{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select__option-with-meta .mdc-list-item__end,.mdc-select__option-with-meta .mdc-list-item__end[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--filled .mdc-select__anchor{height:56px;display:flex;align-items:baseline}.mdc-select--filled .mdc-select__anchor::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor::before{display:none}.mdc-select--filled .mdc-select__anchor{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0}.mdc-select--filled:not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke}.mdc-select--filled.mdc-select--disabled .mdc-select__anchor{background-color:#fafafa}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-select--filled:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--filled.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-select--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-select--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-select--filled .mdc-menu-surface--is-open-below{border-top-left-radius:0px;border-top-right-radius:0px}.mdc-select--filled.mdc-select--focused.mdc-line-ripple::after{transform:scale(1, 2);opacity:1}.mdc-select--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-select--filled .mdc-floating-label,.mdc-select--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{left:48px;right:initial}[dir=rtl] .mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined{border:none}.mdc-select--outlined .mdc-select__anchor{height:56px}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-56px{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-select--outlined .mdc-select__anchor{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-select--outlined+.mdc-select-helper-text{margin-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-select__anchor{background-color:transparent}.mdc-select--outlined.mdc-select--disabled .mdc-select__anchor{background-color:transparent}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}.mdc-select--outlined .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-select--outlined .mdc-select__anchor{display:flex;align-items:baseline;overflow:visible}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined 250ms 1}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--outlined .mdc-select__anchor::before{display:none}.mdc-select--outlined .mdc-select__selected-text-container{display:flex;border:none;z-index:1;background-color:transparent}.mdc-select--outlined .mdc-select__icon{z-index:2}.mdc-select--outlined .mdc-floating-label{line-height:1.15rem;left:4px;right:initial}[dir=rtl] .mdc-select--outlined .mdc-floating-label,.mdc-select--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-select--outlined.mdc-select--focused .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake,.mdc-select--outlined.mdc-select--with-leading-icon[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 96px)}.mdc-select--outlined .mdc-menu-surface{margin-bottom:8px}.mdc-select--outlined.mdc-select--no-label .mdc-menu-surface,.mdc-select--outlined .mdc-menu-surface--is-open-below{margin-bottom:0}.mdc-select__anchor{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-select__anchor .mdc-select__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-select__anchor .mdc-select__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-select__anchor.mdc-ripple-upgraded--unbounded .mdc-select__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-select__anchor.mdc-ripple-upgraded--foreground-activation .mdc-select__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-select__anchor.mdc-ripple-upgraded--foreground-deactivation .mdc-select__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-select__anchor:hover .mdc-select__ripple::before,.mdc-select__anchor.mdc-ripple-surface--hover .mdc-select__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__anchor.mdc-ripple-upgraded--background-focused .mdc-select__ripple::before,.mdc-select__anchor:not(.mdc-ripple-upgraded):focus .mdc-select__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__anchor .mdc-select__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-deprecated-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-deprecated-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-deprecated-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-deprecated-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select-helper-text{margin:0;margin-left:16px;margin-right:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal}[dir=rtl] .mdc-select-helper-text,.mdc-select-helper-text[dir=rtl]{margin-left:16px;margin-right:16px}.mdc-select-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-select-helper-text--validation-msg{opacity:0;transition:opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-select--invalid+.mdc-select-helper-text--validation-msg,.mdc-select-helper-text--validation-msg-persistent{opacity:1}.mdc-select--with-leading-icon .mdc-select__icon{display:inline-block;box-sizing:border-box;border:none;text-decoration:none;cursor:pointer;user-select:none;flex-shrink:0;align-self:center;background-color:transparent;fill:currentColor}.mdc-select--with-leading-icon .mdc-select__icon{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__icon,.mdc-select--with-leading-icon .mdc-select__icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select__icon:not([tabindex]),.mdc-select__icon[tabindex="-1"]{cursor:default;pointer-events:none}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-block;vertical-align:top;outline:none}.mdc-select{width:100%}[hidden]{display:none}.mdc-select__icon{z-index:2}.mdc-select--with-leading-icon{--mdc-list-item-graphic-margin: calc( 48px - var(--mdc-list-item-graphic-size, 24px) - var(--mdc-list-side-padding, 16px) )}.mdc-select .mdc-select__anchor .mdc-select__selected-text{overflow:hidden}.mdc-select .mdc-select__anchor *{display:inline-flex}.mdc-select .mdc-select__anchor .mdc-floating-label{display:inline-block}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-idle-border-color, rgba(0, 0, 0, 0.38) );--mdc-notched-outline-notch-offset: 1px}:host(:not([disabled]):hover) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87);color:var(--mdc-select-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-select-idle-line-color, rgba(0, 0, 0, 0.42))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-select-hover-line-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--outlined):not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke;background-color:var(--mdc-select-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-select__dropdown-icon{fill:var(--mdc-select-error-dropdown-icon-color, var(--mdc-select-error-color, var(--mdc-theme-error, #b00020)))}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label,:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label::after{color:var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select.mdc-select--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}.mdc-select__menu--invalid{--mdc-theme-primary: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.6);color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54);fill:var(--mdc-select-dropdown-icon-color, rgba(0, 0, 0, 0.54))}:host(:not([disabled])) .mdc-select.mdc-select--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px;--mdc-notched-outline-notch-offset: 2px}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-select__dropdown-icon{fill:rgba(98,0,238,.87);fill:var(--mdc-select-focused-dropdown-icon-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)))}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label::after{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select-helper-text:not(.mdc-select-helper-text--validation-msg){color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]){pointer-events:none}:host([disabled]) .mdc-select:not(.mdc-select--outlined).mdc-select--disabled .mdc-select__anchor{background-color:#fafafa;background-color:var(--mdc-select-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-select.mdc-select--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-select .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38);fill:var(--mdc-select-disabled-dropdown-icon-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select-helper-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}`;let ia=class extends ta{constructor(){super(...arguments),this._translationsUpdated=Te((async()=>{await Oe(),this.layoutOptions()}),500)}renderLeadingIcon(){return this.icon?Y``:K}connectedCallback(){super.connectedCallback(),window.addEventListener("translations-updated",this._translationsUpdated)}disconnectedCallback(){super.disconnectedCallback(),window.removeEventListener("translations-updated",this._translationsUpdated)}};ia.styles=[ea,h` + .mdc-select__anchor { + height: var(--select-height, 56px) !important; + } + `],n([_t({type:Boolean})],ia.prototype,"icon",void 0),ia=n([pt("mushroom-select")],ia);const oa=["default","start","center","end","justify"],na={default:"mdi:format-align-left",start:"mdi:format-align-left",center:"mdi:format-align-center",end:"mdi:format-align-right",justify:"mdi:format-align-justify"};let ra=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){const t=Oo(this.hass),e=this.value||"default";return Y` + t.stopPropagation()} + .value=${this.value||"default"} + fixedMenuPosition + naturalMenuWidth + > + + ${oa.map((e=>Y` + + ${t(`editor.form.alignment_picker.values.${e}`)} + + + `))} + + `}static get styles(){return h` + mushroom-select { + width: 100%; + } + `}};n([_t()],ra.prototype,"label",void 0),n([_t()],ra.prototype,"value",void 0),n([_t()],ra.prototype,"configValue",void 0),n([_t()],ra.prototype,"hass",void 0),ra=n([pt("mushroom-alignment-picker")],ra);let aa=class extends ht{render(){return Y` + + `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],aa.prototype,"hass",void 0),n([_t()],aa.prototype,"selector",void 0),n([_t()],aa.prototype,"value",void 0),n([_t()],aa.prototype,"label",void 0),aa=n([pt("ha-selector-mush_alignment")],aa); /** * @license * Copyright 2018 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -const aa="important",la=" !"+aa,sa=He(class extends Ye{constructor(t){var e;if(super(t),t.type!==Ve||"style"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce(((e,i)=>{const o=t[i];return null==o?e:e+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${o};`}),"")}update(t,[e]){const{style:i}=t.element;if(void 0===this.ht){this.ht=new Set;for(const t in e)this.ht.add(t);return this.render(e)}this.ht.forEach((t=>{null==e[t]&&(this.ht.delete(t),t.includes("-")?i.removeProperty(t):i[t]="")}));for(const t in e){const o=e[t];if(null!=o){this.ht.add(t);const e="string"==typeof o&&o.endsWith(la);t.includes("-")||e?i.setProperty(t,e?o.slice(0,-11):o,e?aa:""):i[t]=o}}return X}});var ca={exports:{}},da={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},ua={exports:{}},ha=function(t){return!(!t||"string"==typeof t)&&(t instanceof Array||Array.isArray(t)||t.length>=0&&(t.splice instanceof Function||Object.getOwnPropertyDescriptor(t,t.length-1)&&"String"!==t.constructor.name))},ma=Array.prototype.concat,pa=Array.prototype.slice,fa=ua.exports=function(t){for(var e=[],i=0,o=t.length;i=4&&1!==t[3]&&(e=", "+t[3]),"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+e+")"},wa.to.keyword=function(t){return ya[t.slice(0,3)]};var $a=ca.exports;const Ea=da,Aa={};for(const t of Object.keys(Ea))Aa[Ea[t]]=t;const Sa={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};var Ia=Sa;for(const t of Object.keys(Sa)){if(!("channels"in Sa[t]))throw new Error("missing channels property: "+t);if(!("labels"in Sa[t]))throw new Error("missing channel labels property: "+t);if(Sa[t].labels.length!==Sa[t].channels)throw new Error("channel and label counts mismatch: "+t);const{channels:e,labels:i}=Sa[t];delete Sa[t].channels,delete Sa[t].labels,Object.defineProperty(Sa[t],"channels",{value:e}),Object.defineProperty(Sa[t],"labels",{value:i})}function Ta(t,e){return(t[0]-e[0])**2+(t[1]-e[1])**2+(t[2]-e[2])**2}Sa.rgb.hsl=function(t){const e=t[0]/255,i=t[1]/255,o=t[2]/255,n=Math.min(e,i,o),r=Math.max(e,i,o),a=r-n;let l,s;r===n?l=0:e===r?l=(i-o)/a:i===r?l=2+(o-e)/a:o===r&&(l=4+(e-i)/a),l=Math.min(60*l,360),l<0&&(l+=360);const c=(n+r)/2;return s=r===n?0:c<=.5?a/(r+n):a/(2-r-n),[l,100*s,100*c]},Sa.rgb.hsv=function(t){let e,i,o,n,r;const a=t[0]/255,l=t[1]/255,s=t[2]/255,c=Math.max(a,l,s),d=c-Math.min(a,l,s),u=function(t){return(c-t)/6/d+.5};return 0===d?(n=0,r=0):(r=d/c,e=u(a),i=u(l),o=u(s),a===c?n=o-i:l===c?n=1/3+e-o:s===c&&(n=2/3+i-e),n<0?n+=1:n>1&&(n-=1)),[360*n,100*r,100*c]},Sa.rgb.hwb=function(t){const e=t[0],i=t[1];let o=t[2];const n=Sa.rgb.hsl(t)[0],r=1/255*Math.min(e,Math.min(i,o));return o=1-1/255*Math.max(e,Math.max(i,o)),[n,100*r,100*o]},Sa.rgb.cmyk=function(t){const e=t[0]/255,i=t[1]/255,o=t[2]/255,n=Math.min(1-e,1-i,1-o);return[100*((1-e-n)/(1-n)||0),100*((1-i-n)/(1-n)||0),100*((1-o-n)/(1-n)||0),100*n]},Sa.rgb.keyword=function(t){const e=Aa[t];if(e)return e;let i,o=1/0;for(const e of Object.keys(Ea)){const n=Ta(t,Ea[e]);n.04045?((e+.055)/1.055)**2.4:e/12.92,i=i>.04045?((i+.055)/1.055)**2.4:i/12.92,o=o>.04045?((o+.055)/1.055)**2.4:o/12.92;return[100*(.4124*e+.3576*i+.1805*o),100*(.2126*e+.7152*i+.0722*o),100*(.0193*e+.1192*i+.9505*o)]},Sa.rgb.lab=function(t){const e=Sa.rgb.xyz(t);let i=e[0],o=e[1],n=e[2];i/=95.047,o/=100,n/=108.883,i=i>.008856?i**(1/3):7.787*i+16/116,o=o>.008856?o**(1/3):7.787*o+16/116,n=n>.008856?n**(1/3):7.787*n+16/116;return[116*o-16,500*(i-o),200*(o-n)]},Sa.hsl.rgb=function(t){const e=t[0]/360,i=t[1]/100,o=t[2]/100;let n,r,a;if(0===i)return a=255*o,[a,a,a];n=o<.5?o*(1+i):o+i-o*i;const l=2*o-n,s=[0,0,0];for(let t=0;t<3;t++)r=e+1/3*-(t-1),r<0&&r++,r>1&&r--,a=6*r<1?l+6*(n-l)*r:2*r<1?n:3*r<2?l+(n-l)*(2/3-r)*6:l,s[t]=255*a;return s},Sa.hsl.hsv=function(t){const e=t[0];let i=t[1]/100,o=t[2]/100,n=i;const r=Math.max(o,.01);o*=2,i*=o<=1?o:2-o,n*=r<=1?r:2-r;return[e,100*(0===o?2*n/(r+n):2*i/(o+i)),100*((o+i)/2)]},Sa.hsv.rgb=function(t){const e=t[0]/60,i=t[1]/100;let o=t[2]/100;const n=Math.floor(e)%6,r=e-Math.floor(e),a=255*o*(1-i),l=255*o*(1-i*r),s=255*o*(1-i*(1-r));switch(o*=255,n){case 0:return[o,s,a];case 1:return[l,o,a];case 2:return[a,o,s];case 3:return[a,l,o];case 4:return[s,a,o];case 5:return[o,a,l]}},Sa.hsv.hsl=function(t){const e=t[0],i=t[1]/100,o=t[2]/100,n=Math.max(o,.01);let r,a;a=(2-i)*o;const l=(2-i)*n;return r=i*n,r/=l<=1?l:2-l,r=r||0,a/=2,[e,100*r,100*a]},Sa.hwb.rgb=function(t){const e=t[0]/360;let i=t[1]/100,o=t[2]/100;const n=i+o;let r;n>1&&(i/=n,o/=n);const a=Math.floor(6*e),l=1-o;r=6*e-a,0!=(1&a)&&(r=1-r);const s=i+r*(l-i);let c,d,u;switch(a){default:case 6:case 0:c=l,d=s,u=i;break;case 1:c=s,d=l,u=i;break;case 2:c=i,d=l,u=s;break;case 3:c=i,d=s,u=l;break;case 4:c=s,d=i,u=l;break;case 5:c=l,d=i,u=s}return[255*c,255*d,255*u]},Sa.cmyk.rgb=function(t){const e=t[0]/100,i=t[1]/100,o=t[2]/100,n=t[3]/100;return[255*(1-Math.min(1,e*(1-n)+n)),255*(1-Math.min(1,i*(1-n)+n)),255*(1-Math.min(1,o*(1-n)+n))]},Sa.xyz.rgb=function(t){const e=t[0]/100,i=t[1]/100,o=t[2]/100;let n,r,a;return n=3.2406*e+-1.5372*i+-.4986*o,r=-.9689*e+1.8758*i+.0415*o,a=.0557*e+-.204*i+1.057*o,n=n>.0031308?1.055*n**(1/2.4)-.055:12.92*n,r=r>.0031308?1.055*r**(1/2.4)-.055:12.92*r,a=a>.0031308?1.055*a**(1/2.4)-.055:12.92*a,n=Math.min(Math.max(0,n),1),r=Math.min(Math.max(0,r),1),a=Math.min(Math.max(0,a),1),[255*n,255*r,255*a]},Sa.xyz.lab=function(t){let e=t[0],i=t[1],o=t[2];e/=95.047,i/=100,o/=108.883,e=e>.008856?e**(1/3):7.787*e+16/116,i=i>.008856?i**(1/3):7.787*i+16/116,o=o>.008856?o**(1/3):7.787*o+16/116;return[116*i-16,500*(e-i),200*(i-o)]},Sa.lab.xyz=function(t){let e,i,o;i=(t[0]+16)/116,e=t[1]/500+i,o=i-t[2]/200;const n=i**3,r=e**3,a=o**3;return i=n>.008856?n:(i-16/116)/7.787,e=r>.008856?r:(e-16/116)/7.787,o=a>.008856?a:(o-16/116)/7.787,e*=95.047,i*=100,o*=108.883,[e,i,o]},Sa.lab.lch=function(t){const e=t[0],i=t[1],o=t[2];let n;n=360*Math.atan2(o,i)/2/Math.PI,n<0&&(n+=360);return[e,Math.sqrt(i*i+o*o),n]},Sa.lch.lab=function(t){const e=t[0],i=t[1],o=t[2]/360*2*Math.PI;return[e,i*Math.cos(o),i*Math.sin(o)]},Sa.rgb.ansi16=function(t,e=null){const[i,o,n]=t;let r=null===e?Sa.rgb.hsv(t)[2]:e;if(r=Math.round(r/50),0===r)return 30;let a=30+(Math.round(n/255)<<2|Math.round(o/255)<<1|Math.round(i/255));return 2===r&&(a+=60),a},Sa.hsv.ansi16=function(t){return Sa.rgb.ansi16(Sa.hsv.rgb(t),t[2])},Sa.rgb.ansi256=function(t){const e=t[0],i=t[1],o=t[2];if(e===i&&i===o)return e<8?16:e>248?231:Math.round((e-8)/247*24)+232;return 16+36*Math.round(e/255*5)+6*Math.round(i/255*5)+Math.round(o/255*5)},Sa.ansi16.rgb=function(t){let e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),e=e/10.5*255,[e,e,e];const i=.5*(1+~~(t>50));return[(1&e)*i*255,(e>>1&1)*i*255,(e>>2&1)*i*255]},Sa.ansi256.rgb=function(t){if(t>=232){const e=10*(t-232)+8;return[e,e,e]}let e;t-=16;return[Math.floor(t/36)/5*255,Math.floor((e=t%36)/6)/5*255,e%6/5*255]},Sa.rgb.hex=function(t){const e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},Sa.hex.rgb=function(t){const e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];let i=e[0];3===e[0].length&&(i=i.split("").map((t=>t+t)).join(""));const o=parseInt(i,16);return[o>>16&255,o>>8&255,255&o]},Sa.rgb.hcg=function(t){const e=t[0]/255,i=t[1]/255,o=t[2]/255,n=Math.max(Math.max(e,i),o),r=Math.min(Math.min(e,i),o),a=n-r;let l,s;return l=a<1?r/(1-a):0,s=a<=0?0:n===e?(i-o)/a%6:n===i?2+(o-e)/a:4+(e-i)/a,s/=6,s%=1,[360*s,100*a,100*l]},Sa.hsl.hcg=function(t){const e=t[1]/100,i=t[2]/100,o=i<.5?2*e*i:2*e*(1-i);let n=0;return o<1&&(n=(i-.5*o)/(1-o)),[t[0],100*o,100*n]},Sa.hsv.hcg=function(t){const e=t[1]/100,i=t[2]/100,o=e*i;let n=0;return o<1&&(n=(i-o)/(1-o)),[t[0],100*o,100*n]},Sa.hcg.rgb=function(t){const e=t[0]/360,i=t[1]/100,o=t[2]/100;if(0===i)return[255*o,255*o,255*o];const n=[0,0,0],r=e%1*6,a=r%1,l=1-a;let s=0;switch(Math.floor(r)){case 0:n[0]=1,n[1]=a,n[2]=0;break;case 1:n[0]=l,n[1]=1,n[2]=0;break;case 2:n[0]=0,n[1]=1,n[2]=a;break;case 3:n[0]=0,n[1]=l,n[2]=1;break;case 4:n[0]=a,n[1]=0,n[2]=1;break;default:n[0]=1,n[1]=0,n[2]=l}return s=(1-i)*o,[255*(i*n[0]+s),255*(i*n[1]+s),255*(i*n[2]+s)]},Sa.hcg.hsv=function(t){const e=t[1]/100,i=e+t[2]/100*(1-e);let o=0;return i>0&&(o=e/i),[t[0],100*o,100*i]},Sa.hcg.hsl=function(t){const e=t[1]/100,i=t[2]/100*(1-e)+.5*e;let o=0;return i>0&&i<.5?o=e/(2*i):i>=.5&&i<1&&(o=e/(2*(1-i))),[t[0],100*o,100*i]},Sa.hcg.hwb=function(t){const e=t[1]/100,i=e+t[2]/100*(1-e);return[t[0],100*(i-e),100*(1-i)]},Sa.hwb.hcg=function(t){const e=t[1]/100,i=1-t[2]/100,o=i-e;let n=0;return o<1&&(n=(i-o)/(1-o)),[t[0],100*o,100*n]},Sa.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},Sa.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},Sa.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},Sa.gray.hsl=function(t){return[0,0,t[0]]},Sa.gray.hsv=Sa.gray.hsl,Sa.gray.hwb=function(t){return[0,100,t[0]]},Sa.gray.cmyk=function(t){return[0,0,0,t[0]]},Sa.gray.lab=function(t){return[t[0],0,0]},Sa.gray.hex=function(t){const e=255&Math.round(t[0]/100*255),i=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(i.length)+i},Sa.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]};const Oa=Ia;function za(t){const e=function(){const t={},e=Object.keys(Oa);for(let i=e.length,o=0;o{Pa[t]={},Object.defineProperty(Pa[t],"channels",{value:Da[t].channels}),Object.defineProperty(Pa[t],"labels",{value:Da[t].labels});const e=La(t);Object.keys(e).forEach((i=>{const o=e[i];Pa[t][i]=function(t){const e=function(...e){const i=e[0];if(null==i)return i;i.length>1&&(e=i);const o=t(e);if("object"==typeof o)for(let t=o.length,e=0;e1&&(e=i),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(o)}))}));const Na=$a,Ra=Pa,Fa=["keyword","gray","hex"],Va={};for(const t of Object.keys(Ra))Va[[...Ra[t].labels].sort().join("")]=t;const Ba={};function Ua(t,e){if(!(this instanceof Ua))return new Ua(t,e);if(e&&e in Fa&&(e=null),e&&!(e in Ra))throw new Error("Unknown model: "+e);let i,o;if(null==t)this.model="rgb",this.color=[0,0,0],this.valpha=1;else if(t instanceof Ua)this.model=t.model,this.color=[...t.color],this.valpha=t.valpha;else if("string"==typeof t){const e=Na.get(t);if(null===e)throw new Error("Unable to parse color from string: "+t);this.model=e.model,o=Ra[this.model].channels,this.color=e.value.slice(0,o),this.valpha="number"==typeof e.value[o]?e.value[o]:1}else if(t.length>0){this.model=e||"rgb",o=Ra[this.model].channels;const i=Array.prototype.slice.call(t,0,o);this.color=Xa(i,o),this.valpha="number"==typeof t[o]?t[o]:1}else if("number"==typeof t)this.model="rgb",this.color=[t>>16&255,t>>8&255,255&t],this.valpha=1;else{this.valpha=1;const e=Object.keys(t);"alpha"in t&&(e.splice(e.indexOf("alpha"),1),this.valpha="number"==typeof t.alpha?t.alpha:0);const o=e.sort().join("");if(!(o in Va))throw new Error("Unable to parse color from object: "+JSON.stringify(t));this.model=Va[o];const{labels:n}=Ra[this.model],r=[];for(i=0;i(t%360+360)%360)),saturationl:Ya("hsl",1,Wa(100)),lightness:Ya("hsl",2,Wa(100)),saturationv:Ya("hsv",1,Wa(100)),value:Ya("hsv",2,Wa(100)),chroma:Ya("hcg",1,Wa(100)),gray:Ya("hcg",2,Wa(100)),white:Ya("hwb",1,Wa(100)),wblack:Ya("hwb",2,Wa(100)),cyan:Ya("cmyk",0,Wa(100)),magenta:Ya("cmyk",1,Wa(100)),yellow:Ya("cmyk",2,Wa(100)),black:Ya("cmyk",3,Wa(100)),x:Ya("xyz",0,Wa(95.047)),y:Ya("xyz",1,Wa(100)),z:Ya("xyz",2,Wa(108.833)),l:Ya("lab",0,Wa(100)),a:Ya("lab",1),b:Ya("lab",2),keyword(t){return void 0!==t?new Ua(t):Ra[this.model].keyword(this.color)},hex(t){return void 0!==t?new Ua(t):Na.to.hex(this.rgb().round().color)},hexa(t){if(void 0!==t)return new Ua(t);const e=this.rgb().round().color;let i=Math.round(255*this.valpha).toString(16).toUpperCase();return 1===i.length&&(i="0"+i),Na.to.hex(e)+i},rgbNumber(){const t=this.rgb().color;return(255&t[0])<<16|(255&t[1])<<8|255&t[2]},luminosity(){const t=this.rgb().color,e=[];for(const[i,o]of t.entries()){const t=o/255;e[i]=t<=.04045?t/12.92:((t+.055)/1.055)**2.4}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast(t){const e=this.luminosity(),i=t.luminosity();return e>i?(e+.05)/(i+.05):(i+.05)/(e+.05)},level(t){const e=this.contrast(t);return e>=7?"AAA":e>=4.5?"AA":""},isDark(){const t=this.rgb().color;return(2126*t[0]+7152*t[1]+722*t[2])/1e4<128},isLight(){return!this.isDark()},negate(){const t=this.rgb();for(let e=0;e<3;e++)t.color[e]=255-t.color[e];return t},lighten(t){const e=this.hsl();return e.color[2]+=e.color[2]*t,e},darken(t){const e=this.hsl();return e.color[2]-=e.color[2]*t,e},saturate(t){const e=this.hsl();return e.color[1]+=e.color[1]*t,e},desaturate(t){const e=this.hsl();return e.color[1]-=e.color[1]*t,e},whiten(t){const e=this.hwb();return e.color[1]+=e.color[1]*t,e},blacken(t){const e=this.hwb();return e.color[2]+=e.color[2]*t,e},grayscale(){const t=this.rgb().color,e=.3*t[0]+.59*t[1]+.11*t[2];return Ua.rgb(e,e,e)},fade(t){return this.alpha(this.valpha-this.valpha*t)},opaquer(t){return this.alpha(this.valpha+this.valpha*t)},rotate(t){const e=this.hsl();let i=e.color[0];return i=(i+t)%360,i=i<0?360+i:i,e.color[0]=i,e},mix(t,e){if(!t||!t.rgb)throw new Error('Argument to "mix" was not a Color instance, but rather an instance of '+typeof t);const i=t.rgb(),o=this.rgb(),n=void 0===e?.5:e,r=2*n-1,a=i.alpha()-o.alpha(),l=((r*a==-1?r:(r+a)/(1+r*a))+1)/2,s=1-l;return Ua.rgb(l*i.red()+s*o.red(),l*i.green()+s*o.green(),l*i.blue()+s*o.blue(),i.alpha()*n+o.alpha()*(1-n))}};for(const t of Object.keys(Ra)){if(Fa.includes(t))continue;const{channels:e}=Ra[t];Ua.prototype[t]=function(...e){return this.model===t?new Ua(this):e.length>0?new Ua(e,t):new Ua([...(i=Ra[this.model][t].raw(this.color),Array.isArray(i)?i:[i]),this.valpha],t);var i},Ua[t]=function(...i){let o=i[0];return"number"==typeof o&&(o=Xa(i,e)),new Ua(o,t)}}function Ha(t){return function(e){return function(t,e){return Number(t.toFixed(e))}(e,t)}}function Ya(t,e,i){t=Array.isArray(t)?t:[t];for(const o of t)(Ba[o]||(Ba[o]=[]))[e]=i;return t=t[0],function(o){let n;return void 0!==o?(i&&(o=i(o)),n=this[t](),n.color[e]=o,n):(n=this[t]().color[e],i&&(n=i(n)),n)}}function Wa(t){return function(e){return Math.max(0,Math.min(t,e))}}function Xa(t,e){for(let i=0;it.stopPropagation()} - .value=${this.value||"default"} - fixedMenuPosition - naturalMenuWidth - > - ${this.renderColorCircle(this.value||"grey")} - - ${t("editor.form.color_picker.values.default")} - - ${Ga.map((t=>Y` - - ${function(t){return t.split("-").map((t=>function(t){return t.charAt(0).toUpperCase()+t.slice(1)}(t))).join(" ")}(t)} - ${this.renderColorCircle(t)} - - `))} - - `}renderColorCircle(t){return Y` - - `}static get styles(){return h` - mushroom-select { - width: 100%; - } - .circle-color { - display: block; - background-color: rgb(var(--main-color)); - border-radius: 10px; - width: 20px; - height: 20px; - } - `}};n([_t()],Qa.prototype,"label",void 0),n([_t()],Qa.prototype,"value",void 0),n([_t()],Qa.prototype,"configValue",void 0),n([_t()],Qa.prototype,"hass",void 0),Qa=n([pt("mushroom-color-picker")],Qa);let tl=class extends ht{render(){return Y` - - `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],tl.prototype,"hass",void 0),n([_t()],tl.prototype,"selector",void 0),n([_t()],tl.prototype,"value",void 0),n([_t()],tl.prototype,"label",void 0),tl=n([pt("ha-selector-mush_color")],tl);const el=["button","input_button","scene"],il=["name","state","last-changed","last-updated","none"],ol=["icon","entity-picture","none"];function nl(t,e,i,o,n){switch(t){case"name":return e;case"state":const t=o.entity_id.split(".")[0];return"timestamp"!==o.attributes.device_class&&!el.includes(t)||!Ut(o)||function(t){return t.state===Rt}(o)?i:Y` - - `;case"last-changed":return Y` - - `;case"last-updated":return Y` - - `;case"none":return}}function rl(t,e){return"entity-picture"===e?Yt(t):void 0}let al=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){const t=Oo(this.hass);return Y` - t.stopPropagation()} - .value=${this.value||"default"} - fixedMenuPosition - naturalMenuWidth - > - - ${t("editor.form.icon_type_picker.values.default")} - - ${ol.map((e=>Y` - - ${t(`editor.form.icon_type_picker.values.${e}`)||function(t){return t.charAt(0).toUpperCase()+t.slice(1)}(e)} - - `))} - - `}static get styles(){return h` - mushroom-select { - width: 100%; - } - `}};n([_t()],al.prototype,"label",void 0),n([_t()],al.prototype,"value",void 0),n([_t()],al.prototype,"configValue",void 0),n([_t()],al.prototype,"hass",void 0),al=n([pt("mushroom-icon-type-picker")],al);let ll=class extends ht{render(){return Y` - - `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],ll.prototype,"hass",void 0),n([_t()],ll.prototype,"selector",void 0),n([_t()],ll.prototype,"value",void 0),n([_t()],ll.prototype,"label",void 0),ll=n([pt("ha-selector-mush_icon_type")],ll);let sl=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){var t;const e=Oo(this.hass);return Y` - t.stopPropagation()} - .value=${this.value||"default"} - fixedMenuPosition - naturalMenuWidth - > - - ${e("editor.form.info_picker.values.default")} - - ${(null!==(t=this.infos)&&void 0!==t?t:il).map((t=>Y` - - ${e(`editor.form.info_picker.values.${t}`)||function(t){return t.charAt(0).toUpperCase()+t.slice(1)}(t)} - - `))} - - `}static get styles(){return h` - mushroom-select { - width: 100%; - } - `}};n([_t()],sl.prototype,"label",void 0),n([_t()],sl.prototype,"value",void 0),n([_t()],sl.prototype,"configValue",void 0),n([_t()],sl.prototype,"infos",void 0),n([_t()],sl.prototype,"hass",void 0),sl=n([pt("mushroom-info-picker")],sl);let cl=class extends ht{render(){return Y` - - `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],cl.prototype,"hass",void 0),n([_t()],cl.prototype,"selector",void 0),n([_t()],cl.prototype,"value",void 0),n([_t()],cl.prototype,"label",void 0),cl=n([pt("ha-selector-mush_info")],cl);const dl=["default","horizontal","vertical"],ul={default:"mdi:card-text-outline",vertical:"mdi:focus-field-vertical",horizontal:"mdi:focus-field-horizontal"};let hl=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){const t=Oo(this.hass),e=this.value||"default";return Y` - t.stopPropagation()} - .value=${e} - fixedMenuPosition - naturalMenuWidth - > - - ${dl.map((e=>Y` - - ${t(`editor.form.layout_picker.values.${e}`)} - - - `))} - - `}static get styles(){return h` - mushroom-select { - width: 100%; - } - `}};n([_t()],hl.prototype,"label",void 0),n([_t()],hl.prototype,"value",void 0),n([_t()],hl.prototype,"configValue",void 0),n([_t()],hl.prototype,"hass",void 0),hl=n([pt("mushroom-layout-picker")],hl);let ml=class extends ht{render(){return Y` - - `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],ml.prototype,"hass",void 0),n([_t()],ml.prototype,"selector",void 0),n([_t()],ml.prototype,"value",void 0),n([_t()],ml.prototype,"label",void 0),ml=n([pt("ha-selector-mush_layout")],ml);Tt((t=>{const e={};for(const i of t)e[i.entity_id]=i;return e})),Tt((t=>{const e={};for(const i of t)e[i.id]=i;return e}));const pl={armed_home:{feature:1,service:"alarm_arm_home",icon:"mdi:home"},armed_away:{feature:2,service:"alarm_arm_away",icon:"mdi:lock"},armed_night:{feature:4,service:"alarm_arm_night",icon:"mdi:moon-waning-crescent"},armed_vacation:{feature:32,service:"alarm_arm_vacation",icon:"mdi:air-plane"},armed_custom_bypass:{feature:16,service:"alarm_arm_custom_bypass",icon:"mdi:shield"},disarmed:{service:"alarm_disarm",icon:"mdi:shield-off"}};let fl=class extends ht{constructor(){super(...arguments),this.icon=""}render(){return Y` -
    - -
    - `}static get styles(){return h` - :host { - --main-color: rgb(var(--rgb-grey)); - --icon-color: rgb(var(--rgb-white)); - } - .badge { - display: flex; - align-items: center; - justify-content: center; - line-height: 0; - width: var(--badge-size); - height: var(--badge-size); - font-size: var(--badge-size); - border-radius: var(--badge-border-radius); - background-color: var(--main-color); - transition: background-color 280ms ease-in-out; - } - .badge ha-icon { - --mdc-icon-size: var(--badge-icon-size); - color: var(--icon-color); - } - `}};n([_t()],fl.prototype,"icon",void 0),fl=n([pt("mushroom-badge-icon")],fl);let gl=class extends ht{constructor(){super(...arguments),this.title="",this.disabled=!1}render(){return Y` - - `}static get styles(){return h` - :host { - --icon-color: var(--primary-text-color); - --icon-color-disabled: rgb(var(--rgb-disabled)); - --bg-color: rgba(var(--rgb-primary-text-color), 0.05); - --bg-color-disabled: rgba(var(--rgb-disabled), 0.2); - height: var(--control-height); - width: calc(var(--control-height) * var(--control-button-ratio)); - flex: none; - } - .button { - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - width: 100%; - height: 100%; - border-radius: var(--control-border-radius); - border: none; - background-color: var(--bg-color); - transition: background-color 280ms ease-in-out; - font-size: var(--control-height); - margin: 0; - padding: 0; - box-sizing: border-box; - line-height: 0; - } - .button:disabled { - cursor: not-allowed; - background-color: var(--bg-color-disabled); - } - .button ::slotted(*) { - --mdc-icon-size: var(--control-icon-size); - color: var(--icon-color); - pointer-events: none; - } - .button:disabled ::slotted(*) { - color: var(--icon-color-disabled); - } - `}};n([_t()],gl.prototype,"title",void 0),n([_t({type:Boolean})],gl.prototype,"disabled",void 0),gl=n([pt("mushroom-button")],gl);let _l=class extends ht{constructor(){super(...arguments),this.fill=!1,this.rtl=!1}render(){return Y` -
    - -
    - `}static get styles(){return h` - :host { - display: flex; - flex-direction: row; - width: 100%; - } - .container { - width: 100%; - display: flex; - flex-direction: row; - justify-content: flex-end; - } - .container ::slotted(*:not(:last-child)) { - margin-right: var(--spacing); - } - :host([rtl]) .container ::slotted(*:not(:last-child)) { - margin-right: initial; - margin-left: var(--spacing); - } - .container > ::slotted(mushroom-button) { - width: 0; - flex-grow: 0; - flex-shrink: 1; - flex-basis: calc(var(--control-height) * var(--control-button-ratio)); - } - .container > ::slotted(mushroom-input-number) { - width: 0; - flex-grow: 0; - flex-shrink: 1; - flex-basis: calc(var(--control-height) * var(--control-button-ratio) * 3); - } - .container.fill > ::slotted(mushroom-button), - .container.fill > ::slotted(mushroom-input-number) { - flex-grow: 1; - } - `}};n([_t()],_l.prototype,"fill",void 0),n([_t()],_l.prototype,"rtl",void 0),_l=n([pt("mushroom-button-group")],_l);let vl=class extends ht{render(){var t,e,i,o;return Y` -
    - -
    - `}static get styles(){return h` - .container { - display: flex; - flex-direction: column; - flex-shrink: 0; - flex-grow: 0; - box-sizing: border-box; - justify-content: space-between; - height: 100%; - } - .container > ::slotted(*:not(:last-child)) { - margin-bottom: var(--spacing); - } - .container.horizontal { - flex-direction: row; - } - .container.horizontal > ::slotted(*) { - flex: 1; - min-width: 0; - } - .container.no-info > ::slotted(mushroom-state-item) { - flex: none; - } - .container.no-info.no-icon > ::slotted(mushroom-state-item) { - margin-right: 0; - margin-left: 0; - margin-bottom: 0; - } - .container.horizontal > ::slotted(*:not(:last-child)) { - margin-right: var(--spacing); - margin-bottom: 0; - } - :host([rtl]) .container.horizontal > ::slotted(*:not(:last-child)) { - margin-right: initial; - margin-left: var(--spacing); - margin-bottom: 0; - } - `}};n([_t()],vl.prototype,"appearance",void 0),vl=n([pt("mushroom-card")],vl);const bl={pulse:"@keyframes pulse {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n }",spin:"@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }",cleaning:"@keyframes cleaning {\n 0% {\n transform: rotate(0) translate(0);\n }\n 5% {\n transform: rotate(0) translate(0, -3px);\n }\n 10% {\n transform: rotate(0) translate(0, 1px);\n }\n 15% {\n transform: rotate(0) translate(0);\n }\n\n 20% {\n transform: rotate(30deg) translate(0);\n }\n 25% {\n transform: rotate(30deg) translate(0, -3px);\n }\n 30% {\n transform: rotate(30deg) translate(0, 1px);\n }\n 35% {\n transform: rotate(30deg) translate(0);\n }\n 40% {\n transform: rotate(0) translate(0);\n }\n\n 45% {\n transform: rotate(-30deg) translate(0);\n }\n 50% {\n transform: rotate(-30deg) translate(0, -3px);\n }\n 55% {\n transform: rotate(-30deg) translate(0, 1px);\n }\n 60% {\n transform: rotate(-30deg) translate(0);\n }\n 70% {\n transform: rotate(0deg) translate(0);\n }\n 100% {\n transform: rotate(0deg);\n }\n }",returning:"@keyframes returning {\n 0% {\n transform: rotate(0);\n }\n 25% {\n transform: rotate(20deg);\n }\n 50% {\n transform: rotate(0);\n }\n 75% {\n transform: rotate(-20deg);\n }\n 100% {\n transform: rotate(0);\n }\n }"},yl=h` - ${u(bl.pulse)} - `,xl=(h` - ${u(bl.spin)} - `,h` - ${u(bl.cleaning)} - `,h` - ${u(bl.returning)} - `,h` - ${u(Object.values(bl).join("\n"))} -`);let wl=class extends ht{render(){return Y` -
    - -
    - `}static get styles(){return[xl,h` - :host { - --icon-color: var(--primary-text-color); - --icon-color-disabled: rgb(var(--rgb-disabled)); - --shape-color: rgba(var(--rgb-primary-text-color), 0.05); - --shape-color-disabled: rgba(var(--rgb-disabled), 0.2); - --shape-animation: none; - --shape-outline-color: transparent; - flex: none; - } - .shape { - position: relative; - width: var(--icon-size); - height: var(--icon-size); - font-size: var(--icon-size); - border-radius: var(--icon-border-radius); - display: flex; - align-items: center; - justify-content: center; - background-color: var(--shape-color); - transition-property: background-color, box-shadow; - transition-duration: 280ms; - transition-timing-function: ease-out; - animation: var(--shape-animation); - box-shadow: 0 0 0 1px var(--shape-outline-color); - } - - .shape ::slotted(*) { - display: flex; - color: var(--icon-color); - transition: color 280ms ease-in-out; - } - ::slotted(ha-icon), - ::slotted(ha-state-icon) { - display: flex; - line-height: 0; - --mdc-icon-size: var(--icon-symbol-size); - } - .shape.disabled { - background-color: var(--shape-color-disabled); - } - .shape.disabled ::slotted(*) { - color: var(--icon-color-disabled); - } - `]}};n([_t({type:Boolean})],wl.prototype,"disabled",void 0),wl=n([pt("mushroom-shape-icon")],wl);let kl=class extends ht{constructor(){super(...arguments),this.multiline_secondary=!1}render(){var t;return Y` -
    - ${null!==(t=this.primary)&&void 0!==t?t:""} - ${this.secondary?Y`${this.secondary}`:K} -
    - `}static get styles(){return h` - .container { - min-width: 0; - flex: 1; - display: flex; - flex-direction: column; - } - .primary { - font-weight: var(--card-primary-font-weight); - font-size: var(--card-primary-font-size); - line-height: var(--card-primary-line-height); - color: var(--card-primary-color); - letter-spacing: var(--card-primary-letter-spacing); - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - .secondary { - font-weight: var(--card-secondary-font-weight); - font-size: var(--card-secondary-font-size); - line-height: var(--card-secondary-line-height); - color: var(--card-secondary-color); - letter-spacing: var(--card-secondary-letter-spacing); - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - } - .multiline_secondary { - white-space: pre-wrap; - } - `}};n([_t({attribute:!1})],kl.prototype,"primary",void 0),n([_t({attribute:!1})],kl.prototype,"secondary",void 0),n([_t({type:Boolean})],kl.prototype,"multiline_secondary",void 0),kl=n([pt("mushroom-state-info")],kl);let Cl=class extends ht{render(){var t,e,i,o;return Y` -
    - ${"none"!==(null===(e=this.appearance)||void 0===e?void 0:e.icon_type)?Y` -
    - - -
    - `:K} - ${"none"!==(null===(i=this.appearance)||void 0===i?void 0:i.primary_info)||"none"!==(null===(o=this.appearance)||void 0===o?void 0:o.secondary_info)?Y` -
    - -
    - `:K} -
    - `}static get styles(){return h` - .container { - display: flex; - flex-direction: row; - align-items: center; - justify-content: flex-start; - } - .container > *:not(:last-child) { - margin-right: var(--spacing); - } - :host([rtl]) .container > *:not(:last-child) { - margin-right: initial; - margin-left: var(--spacing); - } - .icon { - position: relative; - } - .icon ::slotted(*[slot="badge"]) { - position: absolute; - top: -3px; - right: -3px; - } - :host([rtl]) .icon ::slotted(*[slot="badge"]) { - right: initial; - left: -3px; - } - .info { - min-width: 0; - width: 100%; - display: flex; - flex-direction: column; - } - .container.vertical { - flex-direction: column; - } - .container.vertical > *:not(:last-child) { - margin-bottom: var(--spacing); - margin-right: 0; - margin-left: 0; - } - :host([rtl]) .container.vertical > *:not(:last-child) { - margin-right: initial; - margin-left: initial; - } - .container.vertical .info { - text-align: center; - } - `}};function $l(t){var e,i,o,n,r;return{layout:null!==(e=t.layout)&&void 0!==e?e:El(t),fill_container:null!==(i=t.fill_container)&&void 0!==i&&i,primary_info:null!==(o=t.primary_info)&&void 0!==o?o:Sl(t),secondary_info:null!==(n=t.secondary_info)&&void 0!==n?n:Il(t),icon_type:null!==(r=t.icon_type)&&void 0!==r?r:Al(t)}}function El(t){return t.vertical?"vertical":"default"}function Al(t){return t.hide_icon?"none":t.use_entity_picture||t.use_media_artwork?"entity-picture":"icon"}function Sl(t){return t.hide_name?"none":"name"}function Il(t){return t.hide_state?"none":"state"}n([_t()],Cl.prototype,"appearance",void 0),Cl=n([pt("mushroom-state-item")],Cl);let Tl=class extends ht{constructor(){super(...arguments),this.picture_url=""}render(){return Y` -
    - -
    - `}static get styles(){return h` - :host { - --main-color: var(--primary-text-color); - --icon-color-disabled: rgb(var(--rgb-disabled)); - --shape-color: rgba(var(--rgb-primary-text-color), 0.05); - --shape-color-disabled: rgba(var(--rgb-disabled), 0.2); - flex: none; - } - .container { - position: relative; - width: var(--icon-size); - height: var(--icon-size); - flex: none; - display: flex; - align-items: center; - justify-content: center; - } - .picture { - width: 100%; - height: 100%; - border-radius: var(--icon-border-radius); - } - `}};n([_t()],Tl.prototype,"picture_url",void 0),Tl=n([pt("mushroom-shape-avatar")],Tl);const Ol=h` - --spacing: var(--mush-spacing, 12px); - - /* Title */ - --title-padding: var(--mush-title-padding, 24px 12px 8px); - --title-spacing: var(--mush-title-spacing, 8px); - --title-font-size: var(--mush-title-font-size, 24px); - --title-font-weight: var(--mush-title-font-weight, normal); - --title-line-height: var(--mush-title-line-height, 32px); - --title-color: var(--mush-title-color, var(--primary-text-color)); - --title-letter-spacing: var(--mush-title-letter-spacing, -0.288px); - --subtitle-font-size: var(--mush-subtitle-font-size, 16px); - --subtitle-font-weight: var(--mush-subtitle-font-weight, normal); - --subtitle-line-height: var(--mush-subtitle-line-height, 24px); - --subtitle-color: var(--mush-subtitle-color, var(--secondary-text-color)); - --subtitle-letter-spacing: var(--mush-subtitle-letter-spacing, 0px); - - /* Card */ - --card-primary-font-size: var(--mush-card-primary-font-size, 14px); - --card-secondary-font-size: var(--mush-card-secondary-font-size, 12px); - --card-primary-font-weight: var(--mush-card-primary-font-weight, 500); - --card-secondary-font-weight: var(--mush-card-secondary-font-weight, 400); - --card-primary-line-height: var(--mush-card-primary-line-height, 20px); - --card-secondary-line-height: var(--mush-card-secondary-line-height, 16px); - --card-primary-color: var(--mush-card-primary-color, var(--primary-text-color)); - --card-secondary-color: var(--mush-card-secondary-color, var(--primary-text-color)); - --card-primary-letter-spacing: var(--mush-card-primary-letter-spacing, 0.1px); - --card-secondary-letter-spacing: var(--mush-card-secondary-letter-spacing, 0.4px); - - /* Chips */ - --chip-spacing: var(--mush-chip-spacing, 8px); - --chip-padding: var(--mush-chip-padding, 0 0.25em); - --chip-height: var(--mush-chip-height, 36px); - --chip-border-radius: var(--mush-chip-border-radius, 19px); - --chip-border-width: var(--mush-chip-border-width, var(--ha-card-border-width, 1px)); - --chip-border-color: var( - --mush-chip-border-color, - var(--ha-card-border-color, var(--divider-color)) - ); - --chip-box-shadow: var(--mush-chip-box-shadow, var(--ha-card-box-shadow, "none")); - --chip-font-size: var(--mush-chip-font-size, 0.3em); - --chip-font-weight: var(--mush-chip-font-weight, bold); - --chip-icon-size: var(--mush-chip-icon-size, 0.5em); - --chip-avatar-padding: var(--mush-chip-avatar-padding, 0.1em); - --chip-avatar-border-radius: var(--mush-chip-avatar-border-radius, 50%); - --chip-background: var( - --mush-chip-background, - var(--ha-card-background, var(--card-background-color, white)) - ); - /* Controls */ - --control-border-radius: var(--mush-control-border-radius, 12px); - --control-height: var(--mush-control-height, 40px); - --control-button-ratio: var(--mush-control-button-ratio, 1); - --control-icon-size: var(--mush-control-icon-size, 0.5em); - - /* Slider */ - --slider-threshold: var(--mush-slider-threshold); - - /* Input Number */ - --input-number-debounce: var(--mush-input-number-debounce); - - /* Layout */ - --layout-align: var(--mush-layout-align, center); - - /* Badge */ - --badge-size: var(--mush-badge-size, 16px); - --badge-icon-size: var(--mush-badge-icon-size, 0.75em); - --badge-border-radius: var(--mush-badge-border-radius, 50%); - - /* Icon */ - --icon-border-radius: var(--mush-icon-border-radius, 50%); - --icon-size: var(--mush-icon-size, 40px); - --icon-symbol-size: var(--mush-icon-symbol-size, 0.6em); -`,zl=h` - /* RGB */ - /* Standard colors */ - --rgb-red: var(--mush-rgb-red, var(--default-red)); - --rgb-pink: var(--mush-rgb-pink, var(--default-pink)); - --rgb-purple: var(--mush-rgb-purple, var(--default-purple)); - --rgb-deep-purple: var(--mush-rgb-deep-purple, var(--default-deep-purple)); - --rgb-indigo: var(--mush-rgb-indigo, var(--default-indigo)); - --rgb-blue: var(--mush-rgb-blue, var(--default-blue)); - --rgb-light-blue: var(--mush-rgb-light-blue, var(--default-light-blue)); - --rgb-cyan: var(--mush-rgb-cyan, var(--default-cyan)); - --rgb-teal: var(--mush-rgb-teal, var(--default-teal)); - --rgb-green: var(--mush-rgb-green, var(--default-green)); - --rgb-light-green: var(--mush-rgb-light-green, var(--default-light-green)); - --rgb-lime: var(--mush-rgb-lime, var(--default-lime)); - --rgb-yellow: var(--mush-rgb-yellow, var(--default-yellow)); - --rgb-amber: var(--mush-rgb-amber, var(--default-amber)); - --rgb-orange: var(--mush-rgb-orange, var(--default-orange)); - --rgb-deep-orange: var(--mush-rgb-deep-orange, var(--default-deep-orange)); - --rgb-brown: var(--mush-rgb-brown, var(--default-brown)); - --rgb-light-grey: var(--mush-rgb-light-grey, var(--default-light-grey)); - --rgb-grey: var(--mush-rgb-grey, var(--default-grey)); - --rgb-dark-grey: var(--mush-rgb-dark-grey, var(--default-dark-grey)); - --rgb-blue-grey: var(--mush-rgb-blue-grey, var(--default-blue-grey)); - --rgb-black: var(--mush-rgb-black, var(--default-black)); - --rgb-white: var(--mush-rgb-white, var(--default-white)); - --rgb-disabled: var(--mush-rgb-disabled, var(--default-disabled)); - - /* Action colors */ - --rgb-info: var(--mush-rgb-info, var(--rgb-blue)); - --rgb-success: var(--mush-rgb-success, var(--rgb-green)); - --rgb-warning: var(--mush-rgb-warning, var(--rgb-orange)); - --rgb-danger: var(--mush-rgb-danger, var(--rgb-red)); - - /* State colors */ - --rgb-state-vacuum: var(--mush-rgb-state-vacuum, var(--rgb-teal)); - --rgb-state-fan: var(--mush-rgb-state-fan, var(--rgb-green)); - --rgb-state-light: var(--mush-rgb-state-light, var(--rgb-orange)); - --rgb-state-entity: var(--mush-rgb-state-entity, var(--rgb-blue)); - --rgb-state-media-player: var(--mush-rgb-state-media-player, var(--rgb-indigo)); - --rgb-state-lock: var(--mush-rgb-state-lock, var(--rgb-blue)); - --rgb-state-number: var(--mush-rgb-state-number, var(--rgb-blue)); - --rgb-state-humidifier: var(--mush-rgb-state-humidifier, var(--rgb-purple)); - - /* State alarm colors */ - --rgb-state-alarm-disarmed: var(--mush-rgb-state-alarm-disarmed, var(--rgb-info)); - --rgb-state-alarm-armed: var(--mush-rgb-state-alarm-armed, var(--rgb-success)); - --rgb-state-alarm-triggered: var(--mush-rgb-state-alarm-triggered, var(--rgb-danger)); - - /* State person colors */ - --rgb-state-person-home: var(--mush-rgb-state-person-home, var(--rgb-success)); - --rgb-state-person-not-home: var(--mush-rgb-state-person-not-home, var(--rgb-danger)); - --rgb-state-person-zone: var(--mush-rgb-state-person-zone, var(--rgb-info)); - --rgb-state-person-unknown: var(--mush-rgb-state-person-unknown, var(--rgb-grey)); - - /* State update colors */ - --rgb-state-update-on: var(--mush-rgb-state-update-on, var(--rgb-orange)); - --rgb-state-update-off: var(--mush-rgb-update-off, var(--rgb-green)); - --rgb-state-update-installing: var(--mush-rgb-update-installing, var(--rgb-blue)); - - /* State lock colors */ - --rgb-state-lock-locked: var(--mush-rgb-state-lock-locked, var(--rgb-green)); - --rgb-state-lock-unlocked: var(--mush-rgb-state-lock-unlocked, var(--rgb-red)); - --rgb-state-lock-pending: var(--mush-rgb-state-lock-pending, var(--rgb-orange)); - - /* State cover colors */ - --rgb-state-cover-open: var(--mush-rgb-state-cover-open, var(--rgb-blue)); - --rgb-state-cover-closed: var(--mush-rgb-state-cover-closed, var(--rgb-disabled)); - - /* State climate colors */ - --rgb-state-climate-auto: var(--mush-rgb-state-climate-auto, var(--rgb-green)); - --rgb-state-climate-cool: var(--mush-rgb-state-climate-cool, var(--rgb-blue)); - --rgb-state-climate-dry: var(--mush-rgb-state-climate-dry, var(--rgb-orange)); - --rgb-state-climate-fan-only: var(--mush-rgb-state-climate-fan-only, var(--rgb-teal)); - --rgb-state-climate-heat: var(--mush-rgb-state-climate-heat, var(--rgb-deep-orange)); - --rgb-state-climate-heat-cool: var(--mush-rgb-state-climate-heat-cool, var(--rgb-green)); - --rgb-state-climate-idle: var(--mush-rgb-state-climate-idle, var(--rgb-disabled)); - --rgb-state-climate-off: var(--mush-rgb-state-climate-off, var(--rgb-disabled)); -`;function Ml(t){return!!t&&t.themes.darkMode}class jl extends ht{updated(t){if(super.updated(t),t.has("hass")&&this.hass){const e=Ml(t.get("hass")),i=Ml(this.hass);e!==i&&this.toggleAttribute("dark-mode",i)}}static get styles(){return[xl,h` - :host { - ${Za} - } - :host([dark-mode]) { - ${Ja} - } - :host { - ${zl} - ${Ol} - } - `]}}n([_t({attribute:!1})],jl.prototype,"hass",void 0);class Dl extends jl{constructor(){super(...arguments),this._inGrid=!1}get _stateObj(){if(!this._config||!this.hass||!this._config.entity)return;const t=this._config.entity;return this.hass.states[t]}get hasControls(){return!1}setConfig(t){this._config=Object.assign({tap_action:{action:"more-info"},hold_action:{action:"more-info"}},t)}getGridSize(){const{grid_columns:t,grid_rows:e}=this.getLayoutOptions();return[t,e]}getCardSize(){var t;let e=1;if(!this._config)return e;const i=$l(this._config);return"vertical"===i.layout&&(e+=1),"horizontal"===(null==i?void 0:i.layout)||!this.hasControls||"collapsible_controls"in this._config&&(null===(t=this._config)||void 0===t?void 0:t.collapsible_controls)||(e+=1),e}getLayoutOptions(){this._inGrid=!0;const t={grid_columns:2,grid_rows:1};if(!this._config)return t;const e=$l(this._config);return"vertical"===e.layout&&(t.grid_rows+=1),"horizontal"===e.layout&&(t.grid_columns=4),"horizontal"!==(null==e?void 0:e.layout)&&this.hasControls&&(t.grid_rows+=1),t}renderPicture(t){return Y` - - `}renderNotFound(t){const e=$l(t),i=Ie(this.hass),o=Oo(this.hass);return Y` - - - - - - - - - - - - `}renderIcon(t,e){const i=Bt(t);return Y` - - - `}renderBadge(t){return!Ut(t)?Y` - - `:K}renderStateInfo(t,e,i,o){const n=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities),r=null!=o?o:n,a=nl(e.primary_info,i,r,t,this.hass),l=nl(e.secondary_info,i,r,t,this.hass);return Y` - - `}}n([vt()],Dl.prototype,"_config",void 0),n([_t({attribute:"in-grid",reflect:!0,type:Boolean})],Dl.prototype,"_inGrid",void 0);const Ll=h` - ha-card { - box-sizing: border-box; - padding: var(--spacing); +const sa="important",la=" !"+sa,ca=He(class extends Ye{constructor(t){var e;if(super(t),t.type!==Ve||"style"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce(((e,i)=>{const o=t[i];return null==o?e:e+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${o};`}),"")}update(t,[e]){const{style:i}=t.element;if(void 0===this.ht){this.ht=new Set;for(const t in e)this.ht.add(t);return this.render(e)}this.ht.forEach((t=>{null==e[t]&&(this.ht.delete(t),t.includes("-")?i.removeProperty(t):i[t]="")}));for(const t in e){const o=e[t];if(null!=o){this.ht.add(t);const e="string"==typeof o&&o.endsWith(la);t.includes("-")||e?i.setProperty(t,e?o.slice(0,-11):o,e?sa:""):i[t]=o}}return X}});var da={exports:{}},ua={aliceblue:[240,248,255],antiquewhite:[250,235,215],aqua:[0,255,255],aquamarine:[127,255,212],azure:[240,255,255],beige:[245,245,220],bisque:[255,228,196],black:[0,0,0],blanchedalmond:[255,235,205],blue:[0,0,255],blueviolet:[138,43,226],brown:[165,42,42],burlywood:[222,184,135],cadetblue:[95,158,160],chartreuse:[127,255,0],chocolate:[210,105,30],coral:[255,127,80],cornflowerblue:[100,149,237],cornsilk:[255,248,220],crimson:[220,20,60],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgoldenrod:[184,134,11],darkgray:[169,169,169],darkgreen:[0,100,0],darkgrey:[169,169,169],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkseagreen:[143,188,143],darkslateblue:[72,61,139],darkslategray:[47,79,79],darkslategrey:[47,79,79],darkturquoise:[0,206,209],darkviolet:[148,0,211],deeppink:[255,20,147],deepskyblue:[0,191,255],dimgray:[105,105,105],dimgrey:[105,105,105],dodgerblue:[30,144,255],firebrick:[178,34,34],floralwhite:[255,250,240],forestgreen:[34,139,34],fuchsia:[255,0,255],gainsboro:[220,220,220],ghostwhite:[248,248,255],gold:[255,215,0],goldenrod:[218,165,32],gray:[128,128,128],green:[0,128,0],greenyellow:[173,255,47],grey:[128,128,128],honeydew:[240,255,240],hotpink:[255,105,180],indianred:[205,92,92],indigo:[75,0,130],ivory:[255,255,240],khaki:[240,230,140],lavender:[230,230,250],lavenderblush:[255,240,245],lawngreen:[124,252,0],lemonchiffon:[255,250,205],lightblue:[173,216,230],lightcoral:[240,128,128],lightcyan:[224,255,255],lightgoldenrodyellow:[250,250,210],lightgray:[211,211,211],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightsalmon:[255,160,122],lightseagreen:[32,178,170],lightskyblue:[135,206,250],lightslategray:[119,136,153],lightslategrey:[119,136,153],lightsteelblue:[176,196,222],lightyellow:[255,255,224],lime:[0,255,0],limegreen:[50,205,50],linen:[250,240,230],magenta:[255,0,255],maroon:[128,0,0],mediumaquamarine:[102,205,170],mediumblue:[0,0,205],mediumorchid:[186,85,211],mediumpurple:[147,112,219],mediumseagreen:[60,179,113],mediumslateblue:[123,104,238],mediumspringgreen:[0,250,154],mediumturquoise:[72,209,204],mediumvioletred:[199,21,133],midnightblue:[25,25,112],mintcream:[245,255,250],mistyrose:[255,228,225],moccasin:[255,228,181],navajowhite:[255,222,173],navy:[0,0,128],oldlace:[253,245,230],olive:[128,128,0],olivedrab:[107,142,35],orange:[255,165,0],orangered:[255,69,0],orchid:[218,112,214],palegoldenrod:[238,232,170],palegreen:[152,251,152],paleturquoise:[175,238,238],palevioletred:[219,112,147],papayawhip:[255,239,213],peachpuff:[255,218,185],peru:[205,133,63],pink:[255,192,203],plum:[221,160,221],powderblue:[176,224,230],purple:[128,0,128],rebeccapurple:[102,51,153],red:[255,0,0],rosybrown:[188,143,143],royalblue:[65,105,225],saddlebrown:[139,69,19],salmon:[250,128,114],sandybrown:[244,164,96],seagreen:[46,139,87],seashell:[255,245,238],sienna:[160,82,45],silver:[192,192,192],skyblue:[135,206,235],slateblue:[106,90,205],slategray:[112,128,144],slategrey:[112,128,144],snow:[255,250,250],springgreen:[0,255,127],steelblue:[70,130,180],tan:[210,180,140],teal:[0,128,128],thistle:[216,191,216],tomato:[255,99,71],turquoise:[64,224,208],violet:[238,130,238],wheat:[245,222,179],white:[255,255,255],whitesmoke:[245,245,245],yellow:[255,255,0],yellowgreen:[154,205,50]},ha={exports:{}},ma=function(t){return!(!t||"string"==typeof t)&&(t instanceof Array||Array.isArray(t)||t.length>=0&&(t.splice instanceof Function||Object.getOwnPropertyDescriptor(t,t.length-1)&&"String"!==t.constructor.name))},pa=Array.prototype.concat,fa=Array.prototype.slice,ga=ha.exports=function(t){for(var e=[],i=0,o=t.length;i=4&&1!==t[3]&&(e=", "+t[3]),"hwb("+t[0]+", "+t[1]+"%, "+t[2]+"%"+e+")"},ka.to.keyword=function(t){return xa[t.slice(0,3)]};var Ea=da.exports;const Aa=ua,Sa={};for(const t of Object.keys(Aa))Sa[Aa[t]]=t;const Ia={rgb:{channels:3,labels:"rgb"},hsl:{channels:3,labels:"hsl"},hsv:{channels:3,labels:"hsv"},hwb:{channels:3,labels:"hwb"},cmyk:{channels:4,labels:"cmyk"},xyz:{channels:3,labels:"xyz"},lab:{channels:3,labels:"lab"},lch:{channels:3,labels:"lch"},hex:{channels:1,labels:["hex"]},keyword:{channels:1,labels:["keyword"]},ansi16:{channels:1,labels:["ansi16"]},ansi256:{channels:1,labels:["ansi256"]},hcg:{channels:3,labels:["h","c","g"]},apple:{channels:3,labels:["r16","g16","b16"]},gray:{channels:1,labels:["gray"]}};var Ta=Ia;for(const t of Object.keys(Ia)){if(!("channels"in Ia[t]))throw new Error("missing channels property: "+t);if(!("labels"in Ia[t]))throw new Error("missing channel labels property: "+t);if(Ia[t].labels.length!==Ia[t].channels)throw new Error("channel and label counts mismatch: "+t);const{channels:e,labels:i}=Ia[t];delete Ia[t].channels,delete Ia[t].labels,Object.defineProperty(Ia[t],"channels",{value:e}),Object.defineProperty(Ia[t],"labels",{value:i})}function za(t,e){return(t[0]-e[0])**2+(t[1]-e[1])**2+(t[2]-e[2])**2}Ia.rgb.hsl=function(t){const e=t[0]/255,i=t[1]/255,o=t[2]/255,n=Math.min(e,i,o),r=Math.max(e,i,o),a=r-n;let s,l;r===n?s=0:e===r?s=(i-o)/a:i===r?s=2+(o-e)/a:o===r&&(s=4+(e-i)/a),s=Math.min(60*s,360),s<0&&(s+=360);const c=(n+r)/2;return l=r===n?0:c<=.5?a/(r+n):a/(2-r-n),[s,100*l,100*c]},Ia.rgb.hsv=function(t){let e,i,o,n,r;const a=t[0]/255,s=t[1]/255,l=t[2]/255,c=Math.max(a,s,l),d=c-Math.min(a,s,l),u=function(t){return(c-t)/6/d+.5};return 0===d?(n=0,r=0):(r=d/c,e=u(a),i=u(s),o=u(l),a===c?n=o-i:s===c?n=1/3+e-o:l===c&&(n=2/3+i-e),n<0?n+=1:n>1&&(n-=1)),[360*n,100*r,100*c]},Ia.rgb.hwb=function(t){const e=t[0],i=t[1];let o=t[2];const n=Ia.rgb.hsl(t)[0],r=1/255*Math.min(e,Math.min(i,o));return o=1-1/255*Math.max(e,Math.max(i,o)),[n,100*r,100*o]},Ia.rgb.cmyk=function(t){const e=t[0]/255,i=t[1]/255,o=t[2]/255,n=Math.min(1-e,1-i,1-o);return[100*((1-e-n)/(1-n)||0),100*((1-i-n)/(1-n)||0),100*((1-o-n)/(1-n)||0),100*n]},Ia.rgb.keyword=function(t){const e=Sa[t];if(e)return e;let i,o=1/0;for(const e of Object.keys(Aa)){const n=za(t,Aa[e]);n.04045?((e+.055)/1.055)**2.4:e/12.92,i=i>.04045?((i+.055)/1.055)**2.4:i/12.92,o=o>.04045?((o+.055)/1.055)**2.4:o/12.92;return[100*(.4124*e+.3576*i+.1805*o),100*(.2126*e+.7152*i+.0722*o),100*(.0193*e+.1192*i+.9505*o)]},Ia.rgb.lab=function(t){const e=Ia.rgb.xyz(t);let i=e[0],o=e[1],n=e[2];i/=95.047,o/=100,n/=108.883,i=i>.008856?i**(1/3):7.787*i+16/116,o=o>.008856?o**(1/3):7.787*o+16/116,n=n>.008856?n**(1/3):7.787*n+16/116;return[116*o-16,500*(i-o),200*(o-n)]},Ia.hsl.rgb=function(t){const e=t[0]/360,i=t[1]/100,o=t[2]/100;let n,r,a;if(0===i)return a=255*o,[a,a,a];n=o<.5?o*(1+i):o+i-o*i;const s=2*o-n,l=[0,0,0];for(let t=0;t<3;t++)r=e+1/3*-(t-1),r<0&&r++,r>1&&r--,a=6*r<1?s+6*(n-s)*r:2*r<1?n:3*r<2?s+(n-s)*(2/3-r)*6:s,l[t]=255*a;return l},Ia.hsl.hsv=function(t){const e=t[0];let i=t[1]/100,o=t[2]/100,n=i;const r=Math.max(o,.01);o*=2,i*=o<=1?o:2-o,n*=r<=1?r:2-r;return[e,100*(0===o?2*n/(r+n):2*i/(o+i)),100*((o+i)/2)]},Ia.hsv.rgb=function(t){const e=t[0]/60,i=t[1]/100;let o=t[2]/100;const n=Math.floor(e)%6,r=e-Math.floor(e),a=255*o*(1-i),s=255*o*(1-i*r),l=255*o*(1-i*(1-r));switch(o*=255,n){case 0:return[o,l,a];case 1:return[s,o,a];case 2:return[a,o,l];case 3:return[a,s,o];case 4:return[l,a,o];case 5:return[o,a,s]}},Ia.hsv.hsl=function(t){const e=t[0],i=t[1]/100,o=t[2]/100,n=Math.max(o,.01);let r,a;a=(2-i)*o;const s=(2-i)*n;return r=i*n,r/=s<=1?s:2-s,r=r||0,a/=2,[e,100*r,100*a]},Ia.hwb.rgb=function(t){const e=t[0]/360;let i=t[1]/100,o=t[2]/100;const n=i+o;let r;n>1&&(i/=n,o/=n);const a=Math.floor(6*e),s=1-o;r=6*e-a,0!=(1&a)&&(r=1-r);const l=i+r*(s-i);let c,d,u;switch(a){default:case 6:case 0:c=s,d=l,u=i;break;case 1:c=l,d=s,u=i;break;case 2:c=i,d=s,u=l;break;case 3:c=i,d=l,u=s;break;case 4:c=l,d=i,u=s;break;case 5:c=s,d=i,u=l}return[255*c,255*d,255*u]},Ia.cmyk.rgb=function(t){const e=t[0]/100,i=t[1]/100,o=t[2]/100,n=t[3]/100;return[255*(1-Math.min(1,e*(1-n)+n)),255*(1-Math.min(1,i*(1-n)+n)),255*(1-Math.min(1,o*(1-n)+n))]},Ia.xyz.rgb=function(t){const e=t[0]/100,i=t[1]/100,o=t[2]/100;let n,r,a;return n=3.2406*e+-1.5372*i+-.4986*o,r=-.9689*e+1.8758*i+.0415*o,a=.0557*e+-.204*i+1.057*o,n=n>.0031308?1.055*n**(1/2.4)-.055:12.92*n,r=r>.0031308?1.055*r**(1/2.4)-.055:12.92*r,a=a>.0031308?1.055*a**(1/2.4)-.055:12.92*a,n=Math.min(Math.max(0,n),1),r=Math.min(Math.max(0,r),1),a=Math.min(Math.max(0,a),1),[255*n,255*r,255*a]},Ia.xyz.lab=function(t){let e=t[0],i=t[1],o=t[2];e/=95.047,i/=100,o/=108.883,e=e>.008856?e**(1/3):7.787*e+16/116,i=i>.008856?i**(1/3):7.787*i+16/116,o=o>.008856?o**(1/3):7.787*o+16/116;return[116*i-16,500*(e-i),200*(i-o)]},Ia.lab.xyz=function(t){let e,i,o;i=(t[0]+16)/116,e=t[1]/500+i,o=i-t[2]/200;const n=i**3,r=e**3,a=o**3;return i=n>.008856?n:(i-16/116)/7.787,e=r>.008856?r:(e-16/116)/7.787,o=a>.008856?a:(o-16/116)/7.787,e*=95.047,i*=100,o*=108.883,[e,i,o]},Ia.lab.lch=function(t){const e=t[0],i=t[1],o=t[2];let n;n=360*Math.atan2(o,i)/2/Math.PI,n<0&&(n+=360);return[e,Math.sqrt(i*i+o*o),n]},Ia.lch.lab=function(t){const e=t[0],i=t[1],o=t[2]/360*2*Math.PI;return[e,i*Math.cos(o),i*Math.sin(o)]},Ia.rgb.ansi16=function(t,e=null){const[i,o,n]=t;let r=null===e?Ia.rgb.hsv(t)[2]:e;if(r=Math.round(r/50),0===r)return 30;let a=30+(Math.round(n/255)<<2|Math.round(o/255)<<1|Math.round(i/255));return 2===r&&(a+=60),a},Ia.hsv.ansi16=function(t){return Ia.rgb.ansi16(Ia.hsv.rgb(t),t[2])},Ia.rgb.ansi256=function(t){const e=t[0],i=t[1],o=t[2];if(e===i&&i===o)return e<8?16:e>248?231:Math.round((e-8)/247*24)+232;return 16+36*Math.round(e/255*5)+6*Math.round(i/255*5)+Math.round(o/255*5)},Ia.ansi16.rgb=function(t){let e=t%10;if(0===e||7===e)return t>50&&(e+=3.5),e=e/10.5*255,[e,e,e];const i=.5*(1+~~(t>50));return[(1&e)*i*255,(e>>1&1)*i*255,(e>>2&1)*i*255]},Ia.ansi256.rgb=function(t){if(t>=232){const e=10*(t-232)+8;return[e,e,e]}let e;t-=16;return[Math.floor(t/36)/5*255,Math.floor((e=t%36)/6)/5*255,e%6/5*255]},Ia.rgb.hex=function(t){const e=(((255&Math.round(t[0]))<<16)+((255&Math.round(t[1]))<<8)+(255&Math.round(t[2]))).toString(16).toUpperCase();return"000000".substring(e.length)+e},Ia.hex.rgb=function(t){const e=t.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);if(!e)return[0,0,0];let i=e[0];3===e[0].length&&(i=i.split("").map((t=>t+t)).join(""));const o=parseInt(i,16);return[o>>16&255,o>>8&255,255&o]},Ia.rgb.hcg=function(t){const e=t[0]/255,i=t[1]/255,o=t[2]/255,n=Math.max(Math.max(e,i),o),r=Math.min(Math.min(e,i),o),a=n-r;let s,l;return s=a<1?r/(1-a):0,l=a<=0?0:n===e?(i-o)/a%6:n===i?2+(o-e)/a:4+(e-i)/a,l/=6,l%=1,[360*l,100*a,100*s]},Ia.hsl.hcg=function(t){const e=t[1]/100,i=t[2]/100,o=i<.5?2*e*i:2*e*(1-i);let n=0;return o<1&&(n=(i-.5*o)/(1-o)),[t[0],100*o,100*n]},Ia.hsv.hcg=function(t){const e=t[1]/100,i=t[2]/100,o=e*i;let n=0;return o<1&&(n=(i-o)/(1-o)),[t[0],100*o,100*n]},Ia.hcg.rgb=function(t){const e=t[0]/360,i=t[1]/100,o=t[2]/100;if(0===i)return[255*o,255*o,255*o];const n=[0,0,0],r=e%1*6,a=r%1,s=1-a;let l=0;switch(Math.floor(r)){case 0:n[0]=1,n[1]=a,n[2]=0;break;case 1:n[0]=s,n[1]=1,n[2]=0;break;case 2:n[0]=0,n[1]=1,n[2]=a;break;case 3:n[0]=0,n[1]=s,n[2]=1;break;case 4:n[0]=a,n[1]=0,n[2]=1;break;default:n[0]=1,n[1]=0,n[2]=s}return l=(1-i)*o,[255*(i*n[0]+l),255*(i*n[1]+l),255*(i*n[2]+l)]},Ia.hcg.hsv=function(t){const e=t[1]/100,i=e+t[2]/100*(1-e);let o=0;return i>0&&(o=e/i),[t[0],100*o,100*i]},Ia.hcg.hsl=function(t){const e=t[1]/100,i=t[2]/100*(1-e)+.5*e;let o=0;return i>0&&i<.5?o=e/(2*i):i>=.5&&i<1&&(o=e/(2*(1-i))),[t[0],100*o,100*i]},Ia.hcg.hwb=function(t){const e=t[1]/100,i=e+t[2]/100*(1-e);return[t[0],100*(i-e),100*(1-i)]},Ia.hwb.hcg=function(t){const e=t[1]/100,i=1-t[2]/100,o=i-e;let n=0;return o<1&&(n=(i-o)/(1-o)),[t[0],100*o,100*n]},Ia.apple.rgb=function(t){return[t[0]/65535*255,t[1]/65535*255,t[2]/65535*255]},Ia.rgb.apple=function(t){return[t[0]/255*65535,t[1]/255*65535,t[2]/255*65535]},Ia.gray.rgb=function(t){return[t[0]/100*255,t[0]/100*255,t[0]/100*255]},Ia.gray.hsl=function(t){return[0,0,t[0]]},Ia.gray.hsv=Ia.gray.hsl,Ia.gray.hwb=function(t){return[0,100,t[0]]},Ia.gray.cmyk=function(t){return[0,0,0,t[0]]},Ia.gray.lab=function(t){return[t[0],0,0]},Ia.gray.hex=function(t){const e=255&Math.round(t[0]/100*255),i=((e<<16)+(e<<8)+e).toString(16).toUpperCase();return"000000".substring(i.length)+i},Ia.rgb.gray=function(t){return[(t[0]+t[1]+t[2])/3/255*100]};const Oa=Ta;function Ma(t){const e=function(){const t={},e=Object.keys(Oa);for(let i=e.length,o=0;o{Na[t]={},Object.defineProperty(Na[t],"channels",{value:La[t].channels}),Object.defineProperty(Na[t],"labels",{value:La[t].labels});const e=Pa(t);Object.keys(e).forEach((i=>{const o=e[i];Na[t][i]=function(t){const e=function(...e){const i=e[0];if(null==i)return i;i.length>1&&(e=i);const o=t(e);if("object"==typeof o)for(let t=o.length,e=0;e1&&(e=i),t(e))};return"conversion"in t&&(e.conversion=t.conversion),e}(o)}))}));const Ra=Ea,Fa=Na,Va=["keyword","gray","hex"],Ba={};for(const t of Object.keys(Fa))Ba[[...Fa[t].labels].sort().join("")]=t;const Ua={};function Ha(t,e){if(!(this instanceof Ha))return new Ha(t,e);if(e&&e in Va&&(e=null),e&&!(e in Fa))throw new Error("Unknown model: "+e);let i,o;if(null==t)this.model="rgb",this.color=[0,0,0],this.valpha=1;else if(t instanceof Ha)this.model=t.model,this.color=[...t.color],this.valpha=t.valpha;else if("string"==typeof t){const e=Ra.get(t);if(null===e)throw new Error("Unable to parse color from string: "+t);this.model=e.model,o=Fa[this.model].channels,this.color=e.value.slice(0,o),this.valpha="number"==typeof e.value[o]?e.value[o]:1}else if(t.length>0){this.model=e||"rgb",o=Fa[this.model].channels;const i=Array.prototype.slice.call(t,0,o);this.color=Ka(i,o),this.valpha="number"==typeof t[o]?t[o]:1}else if("number"==typeof t)this.model="rgb",this.color=[t>>16&255,t>>8&255,255&t],this.valpha=1;else{this.valpha=1;const e=Object.keys(t);"alpha"in t&&(e.splice(e.indexOf("alpha"),1),this.valpha="number"==typeof t.alpha?t.alpha:0);const o=e.sort().join("");if(!(o in Ba))throw new Error("Unable to parse color from object: "+JSON.stringify(t));this.model=Ba[o];const{labels:n}=Fa[this.model],r=[];for(i=0;i(t%360+360)%360)),saturationl:Wa("hsl",1,Xa(100)),lightness:Wa("hsl",2,Xa(100)),saturationv:Wa("hsv",1,Xa(100)),value:Wa("hsv",2,Xa(100)),chroma:Wa("hcg",1,Xa(100)),gray:Wa("hcg",2,Xa(100)),white:Wa("hwb",1,Xa(100)),wblack:Wa("hwb",2,Xa(100)),cyan:Wa("cmyk",0,Xa(100)),magenta:Wa("cmyk",1,Xa(100)),yellow:Wa("cmyk",2,Xa(100)),black:Wa("cmyk",3,Xa(100)),x:Wa("xyz",0,Xa(95.047)),y:Wa("xyz",1,Xa(100)),z:Wa("xyz",2,Xa(108.833)),l:Wa("lab",0,Xa(100)),a:Wa("lab",1),b:Wa("lab",2),keyword(t){return void 0!==t?new Ha(t):Fa[this.model].keyword(this.color)},hex(t){return void 0!==t?new Ha(t):Ra.to.hex(this.rgb().round().color)},hexa(t){if(void 0!==t)return new Ha(t);const e=this.rgb().round().color;let i=Math.round(255*this.valpha).toString(16).toUpperCase();return 1===i.length&&(i="0"+i),Ra.to.hex(e)+i},rgbNumber(){const t=this.rgb().color;return(255&t[0])<<16|(255&t[1])<<8|255&t[2]},luminosity(){const t=this.rgb().color,e=[];for(const[i,o]of t.entries()){const t=o/255;e[i]=t<=.04045?t/12.92:((t+.055)/1.055)**2.4}return.2126*e[0]+.7152*e[1]+.0722*e[2]},contrast(t){const e=this.luminosity(),i=t.luminosity();return e>i?(e+.05)/(i+.05):(i+.05)/(e+.05)},level(t){const e=this.contrast(t);return e>=7?"AAA":e>=4.5?"AA":""},isDark(){const t=this.rgb().color;return(2126*t[0]+7152*t[1]+722*t[2])/1e4<128},isLight(){return!this.isDark()},negate(){const t=this.rgb();for(let e=0;e<3;e++)t.color[e]=255-t.color[e];return t},lighten(t){const e=this.hsl();return e.color[2]+=e.color[2]*t,e},darken(t){const e=this.hsl();return e.color[2]-=e.color[2]*t,e},saturate(t){const e=this.hsl();return e.color[1]+=e.color[1]*t,e},desaturate(t){const e=this.hsl();return e.color[1]-=e.color[1]*t,e},whiten(t){const e=this.hwb();return e.color[1]+=e.color[1]*t,e},blacken(t){const e=this.hwb();return e.color[2]+=e.color[2]*t,e},grayscale(){const t=this.rgb().color,e=.3*t[0]+.59*t[1]+.11*t[2];return Ha.rgb(e,e,e)},fade(t){return this.alpha(this.valpha-this.valpha*t)},opaquer(t){return this.alpha(this.valpha+this.valpha*t)},rotate(t){const e=this.hsl();let i=e.color[0];return i=(i+t)%360,i=i<0?360+i:i,e.color[0]=i,e},mix(t,e){if(!t||!t.rgb)throw new Error('Argument to "mix" was not a Color instance, but rather an instance of '+typeof t);const i=t.rgb(),o=this.rgb(),n=void 0===e?.5:e,r=2*n-1,a=i.alpha()-o.alpha(),s=((r*a==-1?r:(r+a)/(1+r*a))+1)/2,l=1-s;return Ha.rgb(s*i.red()+l*o.red(),s*i.green()+l*o.green(),s*i.blue()+l*o.blue(),i.alpha()*n+o.alpha()*(1-n))}};for(const t of Object.keys(Fa)){if(Va.includes(t))continue;const{channels:e}=Fa[t];Ha.prototype[t]=function(...e){return this.model===t?new Ha(this):e.length>0?new Ha(e,t):new Ha([...(i=Fa[this.model][t].raw(this.color),Array.isArray(i)?i:[i]),this.valpha],t);var i},Ha[t]=function(...i){let o=i[0];return"number"==typeof o&&(o=Ka(i,e)),new Ha(o,t)}}function Ya(t){return function(e){return function(t,e){return Number(t.toFixed(e))}(e,t)}}function Wa(t,e,i){t=Array.isArray(t)?t:[t];for(const o of t)(Ua[o]||(Ua[o]=[]))[e]=i;return t=t[0],function(o){let n;return void 0!==o?(i&&(o=i(o)),n=this[t](),n.color[e]=o,n):(n=this[t]().color[e],i&&(n=i(n)),n)}}function Xa(t){return function(e){return Math.max(0,Math.min(t,e))}}function Ka(t,e){for(let i=0;it.stopPropagation()} + .value=${this.value||"default"} + fixedMenuPosition + naturalMenuWidth + > + ${this.renderColorCircle(this.value||"grey")} + + ${t("editor.form.color_picker.values.default")} + + ${qa.map((t=>Y` + + ${function(t){return t.split("-").map((t=>function(t){return t.charAt(0).toUpperCase()+t.slice(1)}(t))).join(" ")}(t)} + ${this.renderColorCircle(t)} + + `))} + + `}renderColorCircle(t){return Y` + + `}static get styles(){return h` + mushroom-select { + width: 100%; + } + .circle-color { + display: block; + background-color: rgb(var(--main-color)); + border-radius: 10px; + width: 20px; + height: 20px; + } + `}};n([_t()],ts.prototype,"label",void 0),n([_t()],ts.prototype,"value",void 0),n([_t()],ts.prototype,"configValue",void 0),n([_t()],ts.prototype,"hass",void 0),ts=n([pt("mushroom-color-picker")],ts);let es=class extends ht{render(){return Y` + + `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],es.prototype,"hass",void 0),n([_t()],es.prototype,"selector",void 0),n([_t()],es.prototype,"value",void 0),n([_t()],es.prototype,"label",void 0),es=n([pt("ha-selector-mush_color")],es);const is=["button","input_button","scene"],os=["name","state","last-changed","last-updated","none"],ns=["icon","entity-picture","none"];function rs(t,e,i,o,n){switch(t){case"name":return e;case"state":const t=o.entity_id.split(".")[0];return"timestamp"!==o.attributes.device_class&&!is.includes(t)||!Ut(o)||function(t){return t.state===Rt}(o)?i:Y` + + `;case"last-changed":return Y` + + `;case"last-updated":return Y` + + `;case"none":return}}function as(t,e){return"entity-picture"===e?Yt(t):void 0}let ss=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){const t=Oo(this.hass);return Y` + t.stopPropagation()} + .value=${this.value||"default"} + fixedMenuPosition + naturalMenuWidth + > + + ${t("editor.form.icon_type_picker.values.default")} + + ${ns.map((e=>Y` + + ${t(`editor.form.icon_type_picker.values.${e}`)||function(t){return t.charAt(0).toUpperCase()+t.slice(1)}(e)} + + `))} + + `}static get styles(){return h` + mushroom-select { + width: 100%; + } + `}};n([_t()],ss.prototype,"label",void 0),n([_t()],ss.prototype,"value",void 0),n([_t()],ss.prototype,"configValue",void 0),n([_t()],ss.prototype,"hass",void 0),ss=n([pt("mushroom-icon-type-picker")],ss);let ls=class extends ht{render(){return Y` + + `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],ls.prototype,"hass",void 0),n([_t()],ls.prototype,"selector",void 0),n([_t()],ls.prototype,"value",void 0),n([_t()],ls.prototype,"label",void 0),ls=n([pt("ha-selector-mush_icon_type")],ls);let cs=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){var t;const e=Oo(this.hass);return Y` + t.stopPropagation()} + .value=${this.value||"default"} + fixedMenuPosition + naturalMenuWidth + > + + ${e("editor.form.info_picker.values.default")} + + ${(null!==(t=this.infos)&&void 0!==t?t:os).map((t=>Y` + + ${e(`editor.form.info_picker.values.${t}`)||function(t){return t.charAt(0).toUpperCase()+t.slice(1)}(t)} + + `))} + + `}static get styles(){return h` + mushroom-select { + width: 100%; + } + `}};n([_t()],cs.prototype,"label",void 0),n([_t()],cs.prototype,"value",void 0),n([_t()],cs.prototype,"configValue",void 0),n([_t()],cs.prototype,"infos",void 0),n([_t()],cs.prototype,"hass",void 0),cs=n([pt("mushroom-info-picker")],cs);let ds=class extends ht{render(){return Y` + + `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],ds.prototype,"hass",void 0),n([_t()],ds.prototype,"selector",void 0),n([_t()],ds.prototype,"value",void 0),n([_t()],ds.prototype,"label",void 0),ds=n([pt("ha-selector-mush_info")],ds);const us=["default","horizontal","vertical"],hs={default:"mdi:card-text-outline",vertical:"mdi:focus-field-vertical",horizontal:"mdi:focus-field-horizontal"};let ms=class extends ht{constructor(){super(...arguments),this.label="",this.configValue=""}_selectChanged(t){const e=t.target.value;e&&this.dispatchEvent(new CustomEvent("value-changed",{detail:{value:"default"!==e?e:""}}))}render(){const t=Oo(this.hass),e=this.value||"default";return Y` + t.stopPropagation()} + .value=${e} + fixedMenuPosition + naturalMenuWidth + > + + ${us.map((e=>Y` + + ${t(`editor.form.layout_picker.values.${e}`)} + + + `))} + + `}static get styles(){return h` + mushroom-select { + width: 100%; + } + `}};n([_t()],ms.prototype,"label",void 0),n([_t()],ms.prototype,"value",void 0),n([_t()],ms.prototype,"configValue",void 0),n([_t()],ms.prototype,"hass",void 0),ms=n([pt("mushroom-layout-picker")],ms);let ps=class extends ht{render(){return Y` + + `}_valueChanged(t){Lt(this,"value-changed",{value:t.detail.value||void 0})}};n([_t()],ps.prototype,"hass",void 0),n([_t()],ps.prototype,"selector",void 0),n([_t()],ps.prototype,"value",void 0),n([_t()],ps.prototype,"label",void 0),ps=n([pt("ha-selector-mush_layout")],ps);Tt((t=>{const e={};for(const i of t)e[i.entity_id]=i;return e})),Tt((t=>{const e={};for(const i of t)e[i.id]=i;return e}));const fs={armed_home:{feature:1,service:"alarm_arm_home",icon:"mdi:home"},armed_away:{feature:2,service:"alarm_arm_away",icon:"mdi:lock"},armed_night:{feature:4,service:"alarm_arm_night",icon:"mdi:moon-waning-crescent"},armed_vacation:{feature:32,service:"alarm_arm_vacation",icon:"mdi:airplane"},armed_custom_bypass:{feature:16,service:"alarm_arm_custom_bypass",icon:"mdi:shield"},disarmed:{service:"alarm_disarm",icon:"mdi:shield-off"}};let gs=class extends ht{constructor(){super(...arguments),this.icon=""}render(){return Y` +
    + +
    + `}static get styles(){return h` + :host { + --main-color: rgb(var(--rgb-grey)); + --icon-color: rgb(var(--rgb-white)); + } + .badge { display: flex; - flex-direction: column; - justify-content: var(--layout-align); - height: auto; - } - ha-card.fill-container { + align-items: center; + justify-content: center; + line-height: 0; + width: var(--badge-size); + height: var(--badge-size); + font-size: var(--badge-size); + border-radius: var(--badge-border-radius); + background-color: var(--main-color); + transition: background-color 280ms ease-in-out; + } + .badge ha-icon { + --mdc-icon-size: var(--badge-icon-size); + color: var(--icon-color); + } + `}};n([_t()],gs.prototype,"icon",void 0),gs=n([pt("mushroom-badge-icon")],gs);let _s=class extends ht{constructor(){super(...arguments),this.title="",this.disabled=!1}render(){return Y` + + `}static get styles(){return h` + :host { + --icon-color: var(--primary-text-color); + --icon-color-disabled: rgb(var(--rgb-disabled)); + --bg-color: rgba(var(--rgb-primary-text-color), 0.05); + --bg-color-disabled: rgba(var(--rgb-disabled), 0.2); + height: var(--control-height); + width: calc(var(--control-height) * var(--control-button-ratio)); + flex: none; + } + .button { + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 100%; height: 100%; - } - :host([in-grid]) ha-card { - height: 100%; - } - :host([in-grid]) ha-card mushroom-card { - height: 100%; - } - .actions { + border-radius: var(--control-border-radius); + border: none; + background-color: var(--bg-color); + transition: background-color 280ms ease-in-out; + font-size: var(--control-height); + margin: 0; + padding: 0; + box-sizing: border-box; + line-height: 0; + } + .button:disabled { + cursor: not-allowed; + background-color: var(--bg-color-disabled); + } + .button ::slotted(*) { + --mdc-icon-size: var(--control-icon-size); + color: var(--icon-color); + pointer-events: none; + } + .button:disabled ::slotted(*) { + color: var(--icon-color-disabled); + } + `}};n([_t()],_s.prototype,"title",void 0),n([_t({type:Boolean})],_s.prototype,"disabled",void 0),_s=n([pt("mushroom-button")],_s);let vs=class extends ht{constructor(){super(...arguments),this.fill=!1,this.rtl=!1}render(){return Y` +
    + +
    + `}static get styles(){return h` + :host { display: flex; flex-direction: row; - align-items: flex-start; - justify-content: flex-start; - overflow-x: auto; - overflow-y: hidden; - scrollbar-width: none; /* Firefox */ - -ms-overflow-style: none; /* IE 10+ */ - } - .actions::-webkit-scrollbar { - background: transparent; /* Chrome/Safari/Webkit */ - height: 0px; - } - .actions *:not(:last-child) { + width: 100%; + } + .container { + width: 100%; + display: flex; + flex-direction: row; + justify-content: flex-end; + } + .container ::slotted(*:not(:last-child)) { margin-right: var(--spacing); - } - .actions[rtl] *:not(:last-child) { + } + :host([rtl]) .container ::slotted(*:not(:last-child)) { margin-right: initial; margin-left: var(--spacing); - } - .unavailable { - --main-color: rgb(var(--rgb-warning)); - } - .not-found { - --main-color: rgb(var(--rgb-danger)); - } - mushroom-state-item[disabled] { - cursor: initial; - } -`;function Pl(e){const i=window;i.customCards=i.customCards||[];const o=e.type.replace("-card","").replace("mushroom-","");i.customCards.push(Object.assign(Object.assign({},e),{preview:!0,documentationURL:`${t}/blob/main/docs/cards/${o}.md`}))}const Nl="mushroom",Rl=`${Nl}-alarm-control-panel-card`,Fl=`${Rl}-editor`,Vl=["alarm_control_panel"],Bl={disarmed:"var(--rgb-state-alarm-disarmed)",armed:"var(--rgb-state-alarm-armed)",triggered:"var(--rgb-state-alarm-triggered)",unavailable:"var(--rgb-warning)"};function Ul(t){var e;return null!==(e=Bl[t.split("_")[0]])&&void 0!==e?e:"var(--rgb-grey)"}function Hl(t){return["arming","triggered","pending",Nt].indexOf(t)>=0}Pl({type:Rl,name:"Mushroom Alarm Control Panel Card",description:"Card for alarm control panel"});let Yl=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return Wd})),document.createElement(Fl)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Vl.includes(t.split(".")[0])));return{type:`custom:${Rl}`,entity:e[0],states:["armed_home","armed_away"]}}get hasControls(){var t,e;return Boolean(null===(e=null===(t=this._config)||void 0===t?void 0:t.states)||void 0===e?void 0:e.length)}_onTap(t,e){t.stopPropagation(),(async(t,e,i,o)=>{var n,r;const{service:a}=pl[o];let l;if("disarmed"!==o&&i.attributes.code_arm_required||"disarmed"===o&&i.attributes.code_format){const a=await((t,e)=>t.callWS({type:"config/entity_registry/get",entity_id:e}))(e,i.entity_id).catch((()=>{}));if(!(null===(r=null===(n=null==a?void 0:a.options)||void 0===n?void 0:n.alarm_control_panel)||void 0===r?void 0:r.default_code)){const n="disarmed"===o,r=await window.loadCardHelpers(),a=await r.showEnterCodeDialog(t,{codeFormat:i.attributes.code_format,title:e.localize("ui.card.alarm_control_panel."+(n?"disarm":"arm")),submitText:e.localize("ui.card.alarm_control_panel."+(n?"disarm":"arm"))});if(null==a)throw new Error("Code dialog closed");l=a}}await e.callService("alarm_control_panel",a,{entity_id:i.entity_id,code:l})})(this,this.hass,this._stateObj,e)}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type),r=this._config.states&&this._config.states.length>0?function(t){return"disarmed"===t.state}(t)?this._config.states.map((t=>({mode:t}))):[{mode:"disarmed"}]:[],a=function(t){return Nt!==t.state}(t),l=Ie(this.hass);return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e)}; - - ${r.length>0?Y` - - ${r.map((t=>Y` - this._onTap(e,t.mode)} - .disabled=${!a} - > - - - - `))} - - `:K} - - - `}renderIcon(t,e){const i=Ul(t.state),o=Hl(t.state);return Y` - - + } + .container > ::slotted(mushroom-button) { + width: 0; + flex-grow: 0; + flex-shrink: 1; + flex-basis: calc(var(--control-height) * var(--control-button-ratio)); + } + .container > ::slotted(mushroom-input-number) { + width: 0; + flex-grow: 0; + flex-shrink: 1; + flex-basis: calc( + var(--control-height) * var(--control-button-ratio) * 3 + ); + } + .container.fill > ::slotted(mushroom-button), + .container.fill > ::slotted(mushroom-input-number) { + flex-grow: 1; + } + `}};n([_t()],vs.prototype,"fill",void 0),n([_t()],vs.prototype,"rtl",void 0),vs=n([pt("mushroom-button-group")],vs);let bs=class extends ht{render(){var t,e,i;return Y` +
    + +
    + `}static get styles(){return h` + :host { + flex: 1; + display: flex; + flex-direction: column; + margin: calc(-1 * var(--ha-card-border-width, 1px)); + } + .container { + display: flex; + flex-direction: column; + flex-shrink: 0; + flex-grow: 0; + box-sizing: border-box; + justify-content: space-between; + height: 100%; + } + .container.horizontal { + flex-direction: row; + } + .container.horizontal > ::slotted(*) { + flex: 1; + min-width: 0; + } + .container.horizontal > ::slotted(*.actions) { + padding-top: 0 !important; + padding-bottom: 0 !important; + padding-left: 0 !important; + --control-spacing: var(--spacing); + --control-height: var(--icon-size); + } + .container > ::slotted(mushroom-state-item) { + flex: 1; + } + .container.no-info > ::slotted(mushroom-state-item) { + flex: none; + } + `}};n([_t()],bs.prototype,"appearance",void 0),bs=n([pt("mushroom-card")],bs);const ys={pulse:"@keyframes pulse {\n 0% {\n opacity: 1;\n }\n 50% {\n opacity: 0;\n }\n 100% {\n opacity: 1;\n }\n }",spin:"@keyframes spin {\n from {\n transform: rotate(0deg);\n }\n to {\n transform: rotate(360deg);\n }\n }",cleaning:"@keyframes cleaning {\n 0% {\n transform: rotate(0) translate(0);\n }\n 5% {\n transform: rotate(0) translate(0, -3px);\n }\n 10% {\n transform: rotate(0) translate(0, 1px);\n }\n 15% {\n transform: rotate(0) translate(0);\n }\n\n 20% {\n transform: rotate(30deg) translate(0);\n }\n 25% {\n transform: rotate(30deg) translate(0, -3px);\n }\n 30% {\n transform: rotate(30deg) translate(0, 1px);\n }\n 35% {\n transform: rotate(30deg) translate(0);\n }\n 40% {\n transform: rotate(0) translate(0);\n }\n\n 45% {\n transform: rotate(-30deg) translate(0);\n }\n 50% {\n transform: rotate(-30deg) translate(0, -3px);\n }\n 55% {\n transform: rotate(-30deg) translate(0, 1px);\n }\n 60% {\n transform: rotate(-30deg) translate(0);\n }\n 70% {\n transform: rotate(0deg) translate(0);\n }\n 100% {\n transform: rotate(0deg);\n }\n }",returning:"@keyframes returning {\n 0% {\n transform: rotate(0);\n }\n 25% {\n transform: rotate(20deg);\n }\n 50% {\n transform: rotate(0);\n }\n 75% {\n transform: rotate(-20deg);\n }\n 100% {\n transform: rotate(0);\n }\n }"},xs=h` + ${u(ys.pulse)} + `,ws=(h` + ${u(ys.spin)} + `,h` + ${u(ys.cleaning)} + `,h` + ${u(ys.returning)} + `,h` + ${u(Object.values(ys).join("\n"))} +`);let ks=class extends ht{render(){return Y` +
    + +
    + `}static get styles(){return[ws,h` + :host { + --icon-color: var(--primary-text-color); + --icon-color-disabled: rgb(var(--rgb-disabled)); + --shape-color: rgba(var(--rgb-primary-text-color), 0.05); + --shape-color-disabled: rgba(var(--rgb-disabled), 0.2); + --shape-animation: none; + --shape-outline-color: transparent; + flex: none; + } + .shape { + position: relative; + width: var(--icon-size); + height: var(--icon-size); + font-size: var(--icon-size); + border-radius: var(--icon-border-radius); + display: flex; + align-items: center; + justify-content: center; + background-color: var(--shape-color); + transition-property: background-color, box-shadow; + transition-duration: 280ms; + transition-timing-function: ease-out; + animation: var(--shape-animation); + box-shadow: 0 0 0 1px var(--shape-outline-color); + } + + .shape ::slotted(*) { + display: flex; + color: var(--icon-color); + transition: color 280ms ease-in-out; + } + ::slotted(ha-icon), + ::slotted(ha-state-icon) { + display: flex; + line-height: 0; + --mdc-icon-size: var(--icon-symbol-size); + } + .shape.disabled { + background-color: var(--shape-color-disabled); + } + .shape.disabled ::slotted(*) { + color: var(--icon-color-disabled); + } + `]}};n([_t({type:Boolean})],ks.prototype,"disabled",void 0),ks=n([pt("mushroom-shape-icon")],ks);let Cs=class extends ht{constructor(){super(...arguments),this.multiline_secondary=!1}render(){var t;return Y` +
    + ${null!==(t=this.primary)&&void 0!==t?t:""} + ${this.secondary?Y`${this.secondary}`:K} +
    + `}static get styles(){return h` + .container { + min-width: 0; + flex: 1; + display: flex; + flex-direction: column; + } + .primary { + font-weight: var(--card-primary-font-weight); + font-size: var(--card-primary-font-size); + line-height: var(--card-primary-line-height); + color: var(--card-primary-color); + letter-spacing: var(--card-primary-letter-spacing); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + .secondary { + font-weight: var(--card-secondary-font-weight); + font-size: var(--card-secondary-font-size); + line-height: var(--card-secondary-line-height); + color: var(--card-secondary-color); + letter-spacing: var(--card-secondary-letter-spacing); + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + } + .multiline_secondary { + white-space: pre-wrap; + } + `}};n([_t({attribute:!1})],Cs.prototype,"primary",void 0),n([_t({attribute:!1})],Cs.prototype,"secondary",void 0),n([_t({type:Boolean})],Cs.prototype,"multiline_secondary",void 0),Cs=n([pt("mushroom-state-info")],Cs);let $s=class extends ht{render(){var t,e,i,o;return Y` +
    + ${"none"!==(null===(e=this.appearance)||void 0===e?void 0:e.icon_type)?Y` +
    + + +
    + `:K} + ${"none"!==(null===(i=this.appearance)||void 0===i?void 0:i.primary_info)||"none"!==(null===(o=this.appearance)||void 0===o?void 0:o.secondary_info)?Y` +
    + +
    + `:K} +
    + `}static get styles(){return h` + :host { + display: block; + height: 100%; + } + .container { + height: 100%; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + box-sizing: border-box; + padding: var(--spacing); + gap: var(--spacing); + } + .icon { + position: relative; + } + .icon ::slotted(*[slot="badge"]) { + position: absolute; + top: -3px; + right: -3px; + } + :host([rtl]) .icon ::slotted(*[slot="badge"]) { + right: initial; + left: -3px; + } + .info { + min-width: 0; + width: 100%; + display: flex; + flex-direction: column; + } + .container.vertical { + flex-direction: column; + } + .container.vertical .info { + text-align: center; + } + `}};function Es(t){var e,i,o,n,r;return{layout:null!==(e=t.layout)&&void 0!==e?e:As(t),fill_container:null!==(i=t.fill_container)&&void 0!==i&&i,primary_info:null!==(o=t.primary_info)&&void 0!==o?o:Is(t),secondary_info:null!==(n=t.secondary_info)&&void 0!==n?n:Ts(t),icon_type:null!==(r=t.icon_type)&&void 0!==r?r:Ss(t)}}function As(t){return t.vertical?"vertical":"default"}function Ss(t){return t.hide_icon?"none":t.use_entity_picture||t.use_media_artwork?"entity-picture":"icon"}function Is(t){return t.hide_name?"none":"name"}function Ts(t){return t.hide_state?"none":"state"}n([_t()],$s.prototype,"appearance",void 0),$s=n([pt("mushroom-state-item")],$s);let zs=class extends ht{constructor(){super(...arguments),this.picture_url=""}render(){return Y` +
    + +
    + `}static get styles(){return h` + :host { + --main-color: var(--primary-text-color); + --icon-color-disabled: rgb(var(--rgb-disabled)); + --shape-color: rgba(var(--rgb-primary-text-color), 0.05); + --shape-color-disabled: rgba(var(--rgb-disabled), 0.2); + flex: none; + } + .container { + position: relative; + width: var(--icon-size); + height: var(--icon-size); + flex: none; + display: flex; + align-items: center; + justify-content: center; + } + .picture { + width: 100%; + height: 100%; + border-radius: var(--icon-border-radius); + } + `}};n([_t()],zs.prototype,"picture_url",void 0),zs=n([pt("mushroom-shape-avatar")],zs);const Os=h` + --spacing: var(--mush-spacing, 10px); + + /* Title */ + --title-padding: var(--mush-title-padding, 24px 12px 8px); + --title-spacing: var(--mush-title-spacing, 8px); + --title-font-size: var(--mush-title-font-size, 24px); + --title-font-weight: var(--mush-title-font-weight, normal); + --title-line-height: var(--mush-title-line-height, 32px); + --title-color: var(--mush-title-color, var(--primary-text-color)); + --title-letter-spacing: var(--mush-title-letter-spacing, -0.288px); + --subtitle-font-size: var(--mush-subtitle-font-size, 16px); + --subtitle-font-weight: var(--mush-subtitle-font-weight, normal); + --subtitle-line-height: var(--mush-subtitle-line-height, 24px); + --subtitle-color: var(--mush-subtitle-color, var(--secondary-text-color)); + --subtitle-letter-spacing: var(--mush-subtitle-letter-spacing, 0px); + + /* Card */ + --card-primary-font-size: var(--mush-card-primary-font-size, 14px); + --card-secondary-font-size: var(--mush-card-secondary-font-size, 12px); + --card-primary-font-weight: var(--mush-card-primary-font-weight, 500); + --card-secondary-font-weight: var(--mush-card-secondary-font-weight, 400); + --card-primary-line-height: var(--mush-card-primary-line-height, 20px); + --card-secondary-line-height: var(--mush-card-secondary-line-height, 16px); + --card-primary-color: var( + --mush-card-primary-color, + var(--primary-text-color) + ); + --card-secondary-color: var( + --mush-card-secondary-color, + var(--primary-text-color) + ); + --card-primary-letter-spacing: var(--mush-card-primary-letter-spacing, 0.1px); + --card-secondary-letter-spacing: var( + --mush-card-secondary-letter-spacing, + 0.4px + ); + + /* Chips */ + --chip-spacing: var(--mush-chip-spacing, 8px); + --chip-padding: var(--mush-chip-padding, 0 0.25em); + --chip-height: var(--mush-chip-height, 36px); + --chip-border-radius: var(--mush-chip-border-radius, 19px); + --chip-border-width: var( + --mush-chip-border-width, + var(--ha-card-border-width, 1px) + ); + --chip-border-color: var( + --mush-chip-border-color, + var(--ha-card-border-color, var(--divider-color)) + ); + --chip-box-shadow: var( + --mush-chip-box-shadow, + var(--ha-card-box-shadow, "none") + ); + --chip-font-size: var(--mush-chip-font-size, 0.3em); + --chip-font-weight: var(--mush-chip-font-weight, bold); + --chip-icon-size: var(--mush-chip-icon-size, 0.5em); + --chip-avatar-padding: var(--mush-chip-avatar-padding, 0.1em); + --chip-avatar-border-radius: var(--mush-chip-avatar-border-radius, 50%); + --chip-background: var( + --mush-chip-background, + var(--ha-card-background, var(--card-background-color, white)) + ); + /* Controls */ + --control-border-radius: var(--mush-control-border-radius, 12px); + --control-height: var(--mush-control-height, 42px); + --control-button-ratio: var(--mush-control-button-ratio, 1); + --control-icon-size: var(--mush-control-icon-size, 0.5em); + --control-spacing: var(--mush-control-spacing, 12px); + + /* Slider */ + --slider-threshold: var(--mush-slider-threshold); + + /* Input Number */ + --input-number-debounce: var(--mush-input-number-debounce); + + /* Layout */ + --layout-align: var(--mush-layout-align, center); + + /* Badge */ + --badge-size: var(--mush-badge-size, 16px); + --badge-icon-size: var(--mush-badge-icon-size, 0.75em); + --badge-border-radius: var(--mush-badge-border-radius, 50%); + + /* Icon */ + --icon-border-radius: var(--mush-icon-border-radius, 50%); + --icon-size: var(--mush-icon-size, 36px); + --icon-symbol-size: var(--mush-icon-symbol-size, 0.6em); +`,Ms=h` + /* RGB */ + /* Standard colors */ + --rgb-red: var(--mush-rgb-red, var(--default-red)); + --rgb-pink: var(--mush-rgb-pink, var(--default-pink)); + --rgb-purple: var(--mush-rgb-purple, var(--default-purple)); + --rgb-deep-purple: var(--mush-rgb-deep-purple, var(--default-deep-purple)); + --rgb-indigo: var(--mush-rgb-indigo, var(--default-indigo)); + --rgb-blue: var(--mush-rgb-blue, var(--default-blue)); + --rgb-light-blue: var(--mush-rgb-light-blue, var(--default-light-blue)); + --rgb-cyan: var(--mush-rgb-cyan, var(--default-cyan)); + --rgb-teal: var(--mush-rgb-teal, var(--default-teal)); + --rgb-green: var(--mush-rgb-green, var(--default-green)); + --rgb-light-green: var(--mush-rgb-light-green, var(--default-light-green)); + --rgb-lime: var(--mush-rgb-lime, var(--default-lime)); + --rgb-yellow: var(--mush-rgb-yellow, var(--default-yellow)); + --rgb-amber: var(--mush-rgb-amber, var(--default-amber)); + --rgb-orange: var(--mush-rgb-orange, var(--default-orange)); + --rgb-deep-orange: var(--mush-rgb-deep-orange, var(--default-deep-orange)); + --rgb-brown: var(--mush-rgb-brown, var(--default-brown)); + --rgb-light-grey: var(--mush-rgb-light-grey, var(--default-light-grey)); + --rgb-grey: var(--mush-rgb-grey, var(--default-grey)); + --rgb-dark-grey: var(--mush-rgb-dark-grey, var(--default-dark-grey)); + --rgb-blue-grey: var(--mush-rgb-blue-grey, var(--default-blue-grey)); + --rgb-black: var(--mush-rgb-black, var(--default-black)); + --rgb-white: var(--mush-rgb-white, var(--default-white)); + --rgb-disabled: var(--mush-rgb-disabled, var(--default-disabled)); + + /* Action colors */ + --rgb-info: var(--mush-rgb-info, var(--rgb-blue)); + --rgb-success: var(--mush-rgb-success, var(--rgb-green)); + --rgb-warning: var(--mush-rgb-warning, var(--rgb-orange)); + --rgb-danger: var(--mush-rgb-danger, var(--rgb-red)); + + /* State colors */ + --rgb-state-vacuum: var(--mush-rgb-state-vacuum, var(--rgb-teal)); + --rgb-state-fan: var(--mush-rgb-state-fan, var(--rgb-green)); + --rgb-state-light: var(--mush-rgb-state-light, var(--rgb-orange)); + --rgb-state-entity: var(--mush-rgb-state-entity, var(--rgb-blue)); + --rgb-state-media-player: var( + --mush-rgb-state-media-player, + var(--rgb-indigo) + ); + --rgb-state-lock: var(--mush-rgb-state-lock, var(--rgb-blue)); + --rgb-state-number: var(--mush-rgb-state-number, var(--rgb-blue)); + --rgb-state-humidifier: var(--mush-rgb-state-humidifier, var(--rgb-purple)); + + /* State alarm colors */ + --rgb-state-alarm-disarmed: var( + --mush-rgb-state-alarm-disarmed, + var(--rgb-info) + ); + --rgb-state-alarm-armed: var( + --mush-rgb-state-alarm-armed, + var(--rgb-success) + ); + --rgb-state-alarm-triggered: var( + --mush-rgb-state-alarm-triggered, + var(--rgb-danger) + ); + + /* State person colors */ + --rgb-state-person-home: var( + --mush-rgb-state-person-home, + var(--rgb-success) + ); + --rgb-state-person-not-home: var( + --mush-rgb-state-person-not-home, + var(--rgb-danger) + ); + --rgb-state-person-zone: var(--mush-rgb-state-person-zone, var(--rgb-info)); + --rgb-state-person-unknown: var( + --mush-rgb-state-person-unknown, + var(--rgb-grey) + ); + + /* State update colors */ + --rgb-state-update-on: var(--mush-rgb-state-update-on, var(--rgb-orange)); + --rgb-state-update-off: var(--mush-rgb-update-off, var(--rgb-green)); + --rgb-state-update-installing: var( + --mush-rgb-update-installing, + var(--rgb-blue) + ); + + /* State lock colors */ + --rgb-state-lock-locked: var(--mush-rgb-state-lock-locked, var(--rgb-green)); + --rgb-state-lock-unlocked: var( + --mush-rgb-state-lock-unlocked, + var(--rgb-red) + ); + --rgb-state-lock-pending: var( + --mush-rgb-state-lock-pending, + var(--rgb-orange) + ); + + /* State cover colors */ + --rgb-state-cover-open: var(--mush-rgb-state-cover-open, var(--rgb-blue)); + --rgb-state-cover-closed: var( + --mush-rgb-state-cover-closed, + var(--rgb-disabled) + ); + + /* State climate colors */ + --rgb-state-climate-auto: var( + --mush-rgb-state-climate-auto, + var(--rgb-green) + ); + --rgb-state-climate-cool: var(--mush-rgb-state-climate-cool, var(--rgb-blue)); + --rgb-state-climate-dry: var(--mush-rgb-state-climate-dry, var(--rgb-orange)); + --rgb-state-climate-fan-only: var( + --mush-rgb-state-climate-fan-only, + var(--rgb-teal) + ); + --rgb-state-climate-heat: var( + --mush-rgb-state-climate-heat, + var(--rgb-deep-orange) + ); + --rgb-state-climate-heat-cool: var( + --mush-rgb-state-climate-heat-cool, + var(--rgb-green) + ); + --rgb-state-climate-idle: var( + --mush-rgb-state-climate-idle, + var(--rgb-disabled) + ); + --rgb-state-climate-off: var( + --mush-rgb-state-climate-off, + var(--rgb-disabled) + ); +`;function js(t){return!!t&&t.themes.darkMode}class Ds extends ht{firstUpdated(t){this.toggleAttribute("pre-2024-8",!ai(this.hass.config.version,2024,8))}updated(t){if(super.updated(t),t.has("hass")&&this.hass){const e=js(t.get("hass")),i=js(this.hass);e!==i&&this.toggleAttribute("dark-mode",i)}}static get styles(){return[ws,h` + :host { + ${Ja} + } + :host([dark-mode]) { + ${Qa} + } + :host { + ${Ms} + ${Os} + } + :host([pre-2024-8]) { + --spacing: var(--mush-spacing, 12px); + --control-height: var(--mush-control-height, 40px); + --control-spacing: var(--mush-spacing, 12px); + --icon-size: var(--mush-icon-size, 40px); + } + `]}}n([_t({attribute:!1})],Ds.prototype,"hass",void 0);class Ls extends Ds{get _stateObj(){if(!this._config||!this.hass||!this._config.entity)return;const t=this._config.entity;return this.hass.states[t]}get hasControls(){return!1}setConfig(t){this._config=Object.assign({tap_action:{action:"more-info"},hold_action:{action:"more-info"}},t)}getCardSize(){var t;let e=1;if(!this._config)return e;const i=Es(this._config);return"vertical"===i.layout&&(e+=1),"horizontal"===(null==i?void 0:i.layout)||!this.hasControls||"collapsible_controls"in this._config&&(null===(t=this._config)||void 0===t?void 0:t.collapsible_controls)||(e+=1),e}getLayoutOptions(){const t={grid_columns:2,grid_rows:1};if(!this._config)return t;const e=Es(this._config);return"vertical"===e.layout&&(t.grid_rows+=1),"horizontal"===e.layout&&(t.grid_columns=4),"horizontal"!==(null==e?void 0:e.layout)&&this.hasControls&&(t.grid_rows+=1),t}renderPicture(t){return Y` + + `}renderNotFound(t){const e=Es(t),i=Ie(this.hass),o=Oo(this.hass);return Y` + + + + + - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon.pulse { - --shape-animation: 1s ease 0s infinite normal none running pulse; - } - `]}};Yl=n([pt(Rl)],Yl);let Wl=class extends ht{constructor(){super(...arguments),this.icon="",this.label="",this.avatar="",this.avatarOnly=!1}render(){return Y` - - ${this.avatar?Y` `:K} - ${this.avatarOnly?K:Y` -
    - -
    - `} -
    - `}static get styles(){return[xl,h` - :host { - --icon-color: var(--primary-text-color); - --text-color: var(--primary-text-color); - } - ha-card { - box-sizing: border-box; - height: var(--chip-height); - min-width: var(--chip-height); - font-size: var(--chip-height); - width: auto; - border-radius: var(--chip-border-radius); - display: flex; - flex-direction: row; - align-items: center; - background: var(--chip-background); - border-width: var(--chip-border-width); - border-color: var(--chip-border-color); - box-shadow: var(--chip-box-shadow); - box-sizing: content-box; - } - .avatar { - --avatar-size: calc(var(--chip-height) - 2 * var(--chip-avatar-padding)); - border-radius: var(--chip-avatar-border-radius); - height: var(--avatar-size); - width: var(--avatar-size); - margin-left: var(--chip-avatar-padding); - box-sizing: border-box; - object-fit: cover; - } - :host([rtl]) .avatar { - margin-left: initial; - margin-right: var(--chip-avatar-padding); - } - .content { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - height: 100%; - padding: var(--chip-padding); - line-height: 0; - } - ::slotted(ha-icon), - ::slotted(ha-state-icon) { - display: flex; - line-height: 0; - --mdc-icon-size: var(--chip-icon-size); - color: var(--icon-color); - } - ::slotted(svg) { - width: var(--chip-icon-size); - height: var(--chip-icon-size); - display: flex; - } - ::slotted(span) { - font-weight: var(--chip-font-weight); - font-size: var(--chip-font-size); - line-height: 1; - color: var(--text-color); - } - ::slotted(*:not(:last-child)) { - margin-right: 0.15em; - } - :host([rtl]) ::slotted(*:not(:last-child)) { - margin-right: initial; - margin-left: 0.15em; - } - `]}};n([_t()],Wl.prototype,"icon",void 0),n([_t()],Wl.prototype,"label",void 0),n([_t()],Wl.prototype,"avatar",void 0),n([_t()],Wl.prototype,"avatarOnly",void 0),Wl=n([pt("mushroom-chip")],Wl);const Xl=t=>{try{const e=Kl(t.type);if(customElements.get(e)){const i=document.createElement(e,t);return i.setConfig(t),i}const i=document.createElement(e);return customElements.whenDefined(e).then((()=>{try{customElements.upgrade(i),i.setConfig(t)}catch(t){}})),i}catch(t){return void console.error(t)}};function Kl(t){return`${Nl}-${t}-chip`}function Gl(t){return`${Nl}-${t}-chip-editor`}let ql=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return Gd})),document.createElement(Gl("entity"))}static async getStubConfig(t){return{type:"entity",entity:Object.keys(t.states)[0]}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t;if(!this.hass||!this._config||!this._config.entity)return K;const e=this._config.entity,i=this.hass.states[e];if(!i)return K;const o=this._config.name||i.attributes.friendly_name||"",n=this._config.icon,r=this._config.icon_color,a=this._config.use_entity_picture?Yt(i):void 0,l=this.hass.formatEntityState?this.hass.formatEntityState(i):ne(this.hass.localize,i,this.hass.locale,this.hass.config,this.hass.entities),s=Bt(i),c=nl(null!==(t=this._config.content_info)&&void 0!==t?t:"state",o,l,i,this.hass),d=Ie(this.hass);return Y` - - ${a?K:this.renderIcon(i,n,r,s)} - ${c?Y`${c}`:K} - - `}renderIcon(t,e,i,o){const n={};if(i){const t=qa(i);n["--color"]=`rgb(${t})`}return Y` - - `}static get styles(){return h` - mushroom-chip { - cursor: pointer; - } - ha-state-icon.active { - color: var(--color); - } - `}};n([_t({attribute:!1})],ql.prototype,"hass",void 0),n([vt()],ql.prototype,"_config",void 0),ql=n([pt(Kl("entity"))],ql);const Zl=new Set(["partlycloudy","cloudy","fog","windy","windy-variant","hail","rainy","snowy","snowy-rainy","pouring","lightning","lightning-rainy"]),Jl=new Set(["hail","rainy","pouring"]),Ql=new Set(["windy","windy-variant"]),ts=new Set(["snowy","snowy-rainy"]),es=new Set(["lightning","lightning-rainy"]),is=h` - .rain { - fill: var(--weather-icon-rain-color, #30b3ff); - } - .sun { - fill: var(--weather-icon-sun-color, #fdd93c); - } - .moon { - fill: var(--weather-icon-moon-color, #fcf497); - } - .cloud-back { - fill: var(--weather-icon-cloud-back-color, #d4d4d4); - } - .cloud-front { - fill: var(--weather-icon-cloud-front-color, #f9f9f9); - } -`,os=(t,e)=>W` + + +
    +
    +
    + `}renderIcon(t,e){const i=Bt(t);return Y` + + + `}renderBadge(t){return!Ut(t)?Y` + + `:K}renderStateInfo(t,e,i,o){const n=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities),r=null!=o?o:n,a=rs(e.primary_info,i,r,t,this.hass),s=rs(e.secondary_info,i,r,t,this.hass);return Y` + + `}}n([vt()],Ls.prototype,"_config",void 0),n([_t({reflect:!0,type:String})],Ls.prototype,"layout",void 0);const Ps=h` + ha-card { + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: var(--layout-align); + height: auto; + display: flex; + flex-direction: column; + } + ha-card.fill-container { + height: 100%; + } + :host([layout="grid"]) ha-card { + height: 100%; + } + .actions { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + overflow-x: auto; + overflow-y: hidden; + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE 10+ */ + padding: var(--control-spacing); + padding-top: 0; + box-sizing: border-box; + gap: var(--control-spacing); + } + .actions::-webkit-scrollbar { + background: transparent; /* Chrome/Safari/Webkit */ + height: 0px; + } + .unavailable { + --main-color: rgb(var(--rgb-warning)); + } + .not-found { + --main-color: rgb(var(--rgb-danger)); + } + mushroom-state-item[disabled] { + cursor: initial; + } +`;function Ns(e){const i=window;i.customCards=i.customCards||[];const o=e.type.replace("-card","").replace("mushroom-","");i.customCards.push(Object.assign(Object.assign({},e),{preview:!0,documentationURL:`${t}/blob/main/docs/cards/${o}.md`}))}const Rs="mushroom",Fs=`${Rs}-alarm-control-panel-card`,Vs=`${Fs}-editor`,Bs=["alarm_control_panel"],Us={disarmed:"var(--rgb-state-alarm-disarmed)",armed:"var(--rgb-state-alarm-armed)",triggered:"var(--rgb-state-alarm-triggered)",unavailable:"var(--rgb-warning)"};function Hs(t){var e;return null!==(e=Us[t.split("_")[0]])&&void 0!==e?e:"var(--rgb-grey)"}function Ys(t){return["arming","triggered","pending",Nt].indexOf(t)>=0}Ns({type:Fs,name:"Mushroom Alarm Control Panel Card",description:"Card for alarm control panel"});let Ws=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Jd})),document.createElement(Vs)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Bs.includes(t.split(".")[0])));return{type:`custom:${Fs}`,entity:e[0],states:["armed_home","armed_away"]}}get hasControls(){var t,e;return Boolean(null===(e=null===(t=this._config)||void 0===t?void 0:t.states)||void 0===e?void 0:e.length)}_onTap(t,e){t.stopPropagation(),(async(t,e,i,o)=>{var n,r;const{service:a}=fs[o];let s;if("disarmed"!==o&&i.attributes.code_arm_required||"disarmed"===o&&i.attributes.code_format){const a=await((t,e)=>t.callWS({type:"config/entity_registry/get",entity_id:e}))(e,i.entity_id).catch((()=>{}));if(!(null===(r=null===(n=null==a?void 0:a.options)||void 0===n?void 0:n.alarm_control_panel)||void 0===r?void 0:r.default_code)){const n="disarmed"===o,r=await window.loadCardHelpers(),a=await r.showEnterCodeDialog(t,{codeFormat:i.attributes.code_format,title:e.localize("ui.card.alarm_control_panel."+(n?"disarm":"arm")),submitText:e.localize("ui.card.alarm_control_panel."+(n?"disarm":"arm"))});if(null==a)throw new Error("Code dialog closed");s=a}}await e.callService("alarm_control_panel",a,{entity_id:i.entity_id,code:s})})(this,this.hass,this._stateObj,e)}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type),r=this._config.states&&this._config.states.length>0?function(t){return"disarmed"===t.state}(t)?this._config.states.map((t=>({mode:t}))):[{mode:"disarmed"}]:[],a=function(t){return Nt!==t.state}(t),s=Ie(this.hass);return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e)}; + + ${r.length>0?Y` +
    + + ${r.map((t=>Y` + this._onTap(e,t.mode)} + .disabled=${!a} + > + + + + `))} + +
    + `:K} +
    +
    + `}renderIcon(t,e){const i=Hs(t.state),o=Ys(t.state);return Y` + + + + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon.pulse { + --shape-animation: 1s ease 0s infinite normal none running pulse; + } + `]}};Ws=n([pt(Fs)],Ws);let Xs=class extends ht{constructor(){super(...arguments),this.icon="",this.label="",this.avatar="",this.avatarOnly=!1}render(){return Y` + + ${this.avatar?Y` `:K} + ${this.avatarOnly?K:Y` +
    + +
    + `} +
    + `}static get styles(){return[ws,h` + :host { + --icon-color: var(--primary-text-color); + --text-color: var(--primary-text-color); + } + ha-card { + box-sizing: border-box; + height: var(--chip-height); + min-width: var(--chip-height); + font-size: var(--chip-height); + width: auto; + border-radius: var(--chip-border-radius); + display: flex; + flex-direction: row; + align-items: center; + background: var(--chip-background); + border-width: var(--chip-border-width); + border-color: var(--chip-border-color); + box-shadow: var(--chip-box-shadow); + box-sizing: content-box; + } + .avatar { + --avatar-size: calc( + var(--chip-height) - 2 * var(--chip-avatar-padding) + ); + border-radius: var(--chip-avatar-border-radius); + height: var(--avatar-size); + width: var(--avatar-size); + margin-left: var(--chip-avatar-padding); + box-sizing: border-box; + object-fit: cover; + } + :host([rtl]) .avatar { + margin-left: initial; + margin-right: var(--chip-avatar-padding); + } + .content { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + height: 100%; + padding: var(--chip-padding); + line-height: 0; + } + ::slotted(ha-icon), + ::slotted(ha-state-icon) { + display: flex; + line-height: 0; + --mdc-icon-size: var(--chip-icon-size); + color: var(--icon-color); + } + ::slotted(svg) { + width: var(--chip-icon-size); + height: var(--chip-icon-size); + display: flex; + } + ::slotted(span) { + font-weight: var(--chip-font-weight); + font-size: var(--chip-font-size); + line-height: 1; + color: var(--text-color); + } + ::slotted(*:not(:last-child)) { + margin-right: 0.15em; + } + :host([rtl]) ::slotted(*:not(:last-child)) { + margin-right: initial; + margin-left: 0.15em; + } + `]}};n([_t()],Xs.prototype,"icon",void 0),n([_t()],Xs.prototype,"label",void 0),n([_t()],Xs.prototype,"avatar",void 0),n([_t()],Xs.prototype,"avatarOnly",void 0),Xs=n([pt("mushroom-chip")],Xs);const Ks=t=>{try{const e=Gs(t.type);if(customElements.get(e)){const i=document.createElement(e,t);return i.setConfig(t),i}const i=document.createElement(e);return customElements.whenDefined(e).then((()=>{try{customElements.upgrade(i),i.setConfig(t)}catch(t){}})),i}catch(t){return void console.error(t)}};function Gs(t){return`${Rs}-${t}-chip`}function qs(t){return`${Rs}-${t}-chip-editor`}let Zs=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return eu})),document.createElement(qs("entity"))}static async getStubConfig(t){return{type:"entity",entity:Object.keys(t.states)[0]}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t;if(!this.hass||!this._config||!this._config.entity)return K;const e=this._config.entity,i=this.hass.states[e];if(!i)return K;const o=this._config.name||i.attributes.friendly_name||"",n=this._config.icon,r=this._config.icon_color,a=this._config.use_entity_picture?Yt(i):void 0,s=this.hass.formatEntityState?this.hass.formatEntityState(i):ne(this.hass.localize,i,this.hass.locale,this.hass.config,this.hass.entities),l=Bt(i),c=rs(null!==(t=this._config.content_info)&&void 0!==t?t:"state",o,s,i,this.hass),d=Ie(this.hass);return Y` + + ${a?K:this.renderIcon(i,n,r,l)} + ${c?Y`${c}`:K} + + `}renderIcon(t,e,i,o){const n={};if(i){const t=Za(i);n["--color"]=`rgb(${t})`}return Y` + + `}static get styles(){return h` + mushroom-chip { + cursor: pointer; + } + ha-state-icon.active { + color: var(--color); + } + `}};n([_t({attribute:!1})],Zs.prototype,"hass",void 0),n([vt()],Zs.prototype,"_config",void 0),Zs=n([pt(Gs("entity"))],Zs);const Js=new Set(["partlycloudy","cloudy","fog","windy","windy-variant","hail","rainy","snowy","snowy-rainy","pouring","lightning","lightning-rainy"]),Qs=new Set(["hail","rainy","pouring"]),tl=new Set(["windy","windy-variant"]),el=new Set(["snowy","snowy-rainy"]),il=new Set(["lightning","lightning-rainy"]),ol=h` + .rain { + fill: var(--weather-icon-rain-color, #30b3ff); + } + .sun { + fill: var(--weather-icon-sun-color, #fdd93c); + } + .moon { + fill: var(--weather-icon-moon-color, #fcf497); + } + .cloud-back { + fill: var(--weather-icon-cloud-back-color, #d4d4d4); + } + .cloud-front { + fill: var(--weather-icon-cloud-front-color, #f9f9f9); + } +`,nl=(t,e)=>W` `:""} - ${Zl.has(t)?W` + ${Js.has(t)?W` `:""} - ${Jl.has(t)?W` + ${Qs.has(t)?W` `:""} - ${Ql.has(t)?W` + ${tl.has(t)?W` `:""} - ${ts.has(t)?W` + ${el.has(t)?W` `:""} - ${es.has(t)?W` + ${il.has(t)?W` `:""} - `;let ns=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return Qd})),document.createElement(Gl("weather"))}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>"weather"===t.split(".")[0]));return{type:"weather",entity:e[0]}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._config.entity,e=this.hass.states[t];if(!e)return K;const i=os(e.state,!0),o=[];if(this._config.show_conditions){const t=this.hass.formatEntityState?this.hass.formatEntityState(e):ne(this.hass.localize,e,this.hass.locale,this.hass.config,this.hass.entities);o.push(t)}if(this._config.show_temperature){const t=`${te(e.attributes.temperature,this.hass.locale)} ${this.hass.config.unit_system.temperature}`;o.push(t)}const n=Ie(this.hass);return Y` - - ${i} - ${o.length>0?Y`${o.join(" / ")}`:K} - - `}static get styles(){return[is,h` - mushroom-chip { - cursor: pointer; - } - `]}};n([_t({attribute:!1})],ns.prototype,"hass",void 0),n([vt()],ns.prototype,"_config",void 0),ns=n([pt(Kl("weather"))],ns);const rs="mdi:arrow-left";let as=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return iu})),document.createElement(Gl("back"))}static async getStubConfig(t){return{type:"back"}}setConfig(t){this._config=t}_handleAction(){window.history.back()}render(){if(!this.hass||!this._config)return K;const t=this._config.icon||rs,e=Ie(this.hass);return Y` - - - - `}static get styles(){return h` - mushroom-chip { - cursor: pointer; - } - `}};n([_t({attribute:!1})],as.prototype,"hass",void 0),n([vt()],as.prototype,"_config",void 0),as=n([pt(Kl("back"))],as);const ls="mdi:flash";let ss=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return ru})),document.createElement(Gl("action"))}static async getStubConfig(t){return{type:"action"}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config)return K;const t=this._config.icon||ls,e=this._config.icon_color,i={};if(e){const t=qa(e);i["--color"]=`rgb(${t})`}const o=Ie(this.hass);return Y` - - - - `}static get styles(){return h` - mushroom-chip { - cursor: pointer; - } - ha-state-icon { - color: var(--color); - } - `}};n([_t({attribute:!1})],ss.prototype,"hass",void 0),n([vt()],ss.prototype,"_config",void 0),ss=n([pt(Kl("action"))],ss);const cs="mdi:menu";let ds=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return su})),document.createElement(Gl("menu"))}static async getStubConfig(t){return{type:"menu"}}setConfig(t){this._config=t}_handleAction(){Lt(this,"hass-toggle-menu")}render(){if(!this.hass||!this._config)return K;const t=this._config.icon||cs,e=Ie(this.hass);return Y` - - - - `}static get styles(){return h` - mushroom-chip { - cursor: pointer; - } - `}};n([_t({attribute:!1})],ds.prototype,"hass",void 0),n([vt()],ds.prototype,"_config",void 0),ds=n([pt(Kl("menu"))],ds);const us=new Set(["clear-night","cloudy","fog","lightning","lightning-rainy","partlycloudy","pouring","rainy","hail","snowy","snowy-rainy","sunny","windy","windy-variant"]),hs=t=>{if(!t||!t.startsWith("weather-"))return;const e=t.replace("weather-","");return us.has(e)?os(e,!0):void 0},ms=["content","icon","icon_color","picture"];let ps=class extends ht{constructor(){super(...arguments),this._templateResults={},this._unsubRenderTemplates=new Map}static async getConfigElement(){return await Promise.resolve().then((function(){return gu})),document.createElement(Gl("template"))}static async getStubConfig(t){return{type:"template"}}setConfig(t){ms.forEach((e=>{var i,o;(null===(i=this._config)||void 0===i?void 0:i[e])===t[e]&&(null===(o=this._config)||void 0===o?void 0:o.entity)==t.entity||this._tryDisconnectKey(e)})),this._config=Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)}connectedCallback(){super.connectedCallback(),this._tryConnect()}disconnectedCallback(){this._tryDisconnect()}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}isTemplate(t){var e;const i=null===(e=this._config)||void 0===e?void 0:e[t];return null==i?void 0:i.includes("{")}getValue(t){var e,i,o;return this.isTemplate(t)?null===(i=null===(e=this._templateResults[t])||void 0===e?void 0:e.result)||void 0===i?void 0:i.toString():null===(o=this._config)||void 0===o?void 0:o[t]}render(){if(!this.hass||!this._config)return K;const t=this.getValue("icon"),e=this.getValue("icon_color"),i=this.getValue("content"),o=this.getValue("picture"),n=Ie(this.hass),r=hs(t);return Y` - - ${o?K:r||(t?this.renderIcon(t,e):K)} - ${i?this.renderContent(i):K} - - `}renderIcon(t,e){const i={};if(e){const t=qa(e);i["--color"]=`rgb(${t})`}return Y``}renderContent(t){return Y`${t}`}updated(t){super.updated(t),this._config&&this.hass&&this._tryConnect()}async _tryConnect(){ms.forEach((t=>{this._tryConnectKey(t)}))}async _tryConnectKey(t){var e,i;if(void 0===this._unsubRenderTemplates.get(t)&&this.hass&&this._config&&this.isTemplate(t))try{const i=Fe(this.hass.connection,(e=>{this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:e})}),{template:null!==(e=this._config[t])&&void 0!==e?e:"",entity_ids:this._config.entity_id,variables:{config:this._config,user:this.hass.user.name,entity:this._config.entity},strict:!0});this._unsubRenderTemplates.set(t,i),await i}catch(e){const o={result:null!==(i=this._config[t])&&void 0!==i?i:"",listeners:{all:!1,domains:[],entities:[],time:!1}};this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:o}),this._unsubRenderTemplates.delete(t)}}async _tryDisconnect(){ms.forEach((t=>{this._tryDisconnectKey(t)}))}async _tryDisconnectKey(t){const e=this._unsubRenderTemplates.get(t);if(e)try{(await e)(),this._unsubRenderTemplates.delete(t)}catch(t){if("not_found"!==t.code&&"template_error"!==t.code)throw t}}static get styles(){return h` - mushroom-chip { - cursor: pointer; - } - ha-state-icon { - color: var(--color); - } - ${is} - `}};n([_t({attribute:!1})],ps.prototype,"hass",void 0),n([vt()],ps.prototype,"_config",void 0),n([vt()],ps.prototype,"_templateResults",void 0),n([vt()],ps.prototype,"_unsubRenderTemplates",void 0),ps=n([pt(Kl("template"))],ps);const fs=()=>{var t,e,i;customElements.get("ha-form")||null===(t=customElements.get("hui-button-card"))||void 0===t||t.getConfigElement(),customElements.get("ha-entity-picker")||null===(e=customElements.get("hui-entities-card"))||void 0===e||e.getConfigElement(),customElements.get("ha-card-conditions-editor")||null===(i=customElements.get("hui-conditional-card"))||void 0===i||i.getConfigElement()},gs=Kl("conditional"),_s=async()=>{if(customElements.get(gs))return;if(!customElements.get("hui-conditional-base")){(await window.loadCardHelpers()).createCardElement({type:"conditional",card:{type:"button"},conditions:[]})}const t=await(async t=>{let e=customElements.get(t);return e||(await customElements.whenDefined(t),customElements.get(t))})("hui-conditional-base");class e extends t{static async getConfigElement(){return await Promise.resolve().then((function(){return kp})),document.createElement(Gl("conditional"))}static async getStubConfig(){return{type:"conditional",conditions:[]}}setConfig(t){if(this.validateConfig(t),!t.chip)throw new Error("No chip configured");this._element=Xl(t.chip)}}customElements.get(gs)||customElements.define(gs,e)};function vs(t){return null!=t.attributes.brightness?Math.max(Math.round(100*t.attributes.brightness/255),1):void 0}function bs(t){return null!=t.attributes.rgb_color?t.attributes.rgb_color:void 0}function ys(t){return Ka.rgb(t).l()>96}function xs(t){return Ka.rgb(t).l()>97}function ws(t){return(t=>{var e;return(null===(e=t.attributes.supported_color_modes)||void 0===e?void 0:e.some((t=>De.includes(t))))||!1})(t)}function ks(t){return(t=>{var e;return(null===(e=t.attributes.supported_color_modes)||void 0===e?void 0:e.some((t=>Le.includes(t))))||!1})(t)}let Cs=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return Op})),document.createElement(Gl("light"))}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>"light"===t.split(".")[0]));return{type:"light",entity:e[0]}}setConfig(t){this._config=Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t,e;if(!this.hass||!this._config||!this._config.entity)return K;const i=this._config.entity,o=this.hass.states[i];if(!o)return K;const n=this._config.name||o.attributes.friendly_name||"",r=this._config.icon,a=this.hass.formatEntityState?this.hass.formatEntityState(o):ne(this.hass.localize,o,this.hass.locale,this.hass.config,this.hass.entities),l=Bt(o),s=bs(o),c={};if(s&&(null===(t=this._config)||void 0===t?void 0:t.use_light_color)){const t=s.join(",");c["--color"]=`rgb(${t})`,xs(s)&&(c["--color"]="rgba(var(--rgb-primary-text-color), 0.2)")}const d=nl(null!==(e=this._config.content_info)&&void 0!==e?e:"state",n,a,o,this.hass),u=Ie(this.hass);return Y` - - - ${d?Y`${d}`:K} - - `}static get styles(){return h` - :host { - --color: rgb(var(--rgb-state-light)); - } - mushroom-chip { - cursor: pointer; - } - ha-state-icon.active { - color: var(--color); - } - `}};n([_t({attribute:!1})],Cs.prototype,"hass",void 0),n([vt()],Cs.prototype,"_config",void 0),Cs=n([pt(Kl("light"))],Cs);let $s=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return jp})),document.createElement(Gl("alarm-control-panel"))}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Vl.includes(t.split(".")[0])));return{type:"alarm-control-panel",entity:e[0]}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t;if(!this.hass||!this._config||!this._config.entity)return K;const e=this._config.entity,i=this.hass.states[e];if(!i)return K;const o=this._config.name||i.attributes.friendly_name||"",n=this._config.icon,r=Ul(i.state),a=Hl(i.state),l=this.hass.formatEntityState?this.hass.formatEntityState(i):ne(this.hass.localize,i,this.hass.locale,this.hass.config,this.hass.entities),s={};if(r){const t=qa(r);s["--color"]=`rgb(${t})`}const c=nl(null!==(t=this._config.content_info)&&void 0!==t?t:"state",o,l,i,this.hass),d=Ie(this.hass);return Y` - - - ${c?Y`${c}`:K} - - `}static get styles(){return h` - mushroom-chip { - cursor: pointer; - } - ha-state-icon { - color: var(--color); - } - ha-state-icon.pulse { - animation: 1s ease 0s infinite normal none running pulse; - } - ${yl} - `}};n([_t({attribute:!1})],$s.prototype,"hass",void 0),n([vt()],$s.prototype,"_config",void 0),$s=n([pt(Kl("alarm-control-panel"))],$s);let Es=class extends ht{setConfig(){}static get styles(){return h` - :host { - flex-grow: 1; - } - `}};Es=n([pt(Kl("spacer"))],Es);const As=`${Nl}-chips-card`,Ss=`${As}-editor`;Pl({type:As,name:"Mushroom Chips Card",description:"Card with chips to display informations"});let Is=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return Qp})),document.createElement(Ss)}static async getStubConfig(t){const e=await Promise.all([ql.getStubConfig(t)]);return{type:`custom:${As}`,chips:e}}set hass(t){var e;const i=Ml(this._hass),o=Ml(t);i!==o&&this.toggleAttribute("dark-mode",o),this._hass=t,null===(e=this.shadowRoot)||void 0===e||e.querySelectorAll("div > *").forEach((e=>{e.hass=t}))}getCardSize(){return 1}setConfig(t){this._config=t}render(){if(!this._config||!this._hass)return K;let t="";this._config.alignment&&(t=`align-${this._config.alignment}`);const e=Ie(this._hass);return Y` - -
    - ${this._config.chips.map((t=>this.renderChip(t)))} + `;let rl=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return su})),document.createElement(qs("weather"))}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>"weather"===t.split(".")[0]));return{type:"weather",entity:e[0]}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._config.entity,e=this.hass.states[t];if(!e)return K;const i=nl(e.state,!0),o=[];if(this._config.show_conditions){const t=this.hass.formatEntityState?this.hass.formatEntityState(e):ne(this.hass.localize,e,this.hass.locale,this.hass.config,this.hass.entities);o.push(t)}if(this._config.show_temperature){const t=`${te(e.attributes.temperature,this.hass.locale)} ${this.hass.config.unit_system.temperature}`;o.push(t)}const n=Ie(this.hass);return Y` + + ${i} + ${o.length>0?Y`${o.join(" / ")}`:K} + + `}static get styles(){return[ol,h` + mushroom-chip { + cursor: pointer; + } + `]}};n([_t({attribute:!1})],rl.prototype,"hass",void 0),n([vt()],rl.prototype,"_config",void 0),rl=n([pt(Gs("weather"))],rl);const al="mdi:arrow-left";let sl=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return du})),document.createElement(qs("back"))}static async getStubConfig(t){return{type:"back"}}setConfig(t){this._config=t}_handleAction(){window.history.back()}render(){if(!this.hass||!this._config)return K;const t=this._config.icon||al,e=Ie(this.hass);return Y` + + + + `}static get styles(){return h` + mushroom-chip { + cursor: pointer; + } + `}};n([_t({attribute:!1})],sl.prototype,"hass",void 0),n([vt()],sl.prototype,"_config",void 0),sl=n([pt(Gs("back"))],sl);const ll="mdi:flash";let cl=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return pu})),document.createElement(qs("action"))}static async getStubConfig(t){return{type:"action"}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config)return K;const t=this._config.icon||ll,e=this._config.icon_color,i={};if(e){const t=Za(e);i["--color"]=`rgb(${t})`}const o=Ie(this.hass);return Y` + + + + `}static get styles(){return h` + mushroom-chip { + cursor: pointer; + } + ha-state-icon { + color: var(--color); + } + `}};n([_t({attribute:!1})],cl.prototype,"hass",void 0),n([vt()],cl.prototype,"_config",void 0),cl=n([pt(Gs("action"))],cl);const dl="mdi:menu";let ul=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return _u})),document.createElement(qs("menu"))}static async getStubConfig(t){return{type:"menu"}}setConfig(t){this._config=t}_handleAction(){Lt(this,"hass-toggle-menu")}render(){if(!this.hass||!this._config)return K;const t=this._config.icon||dl,e=Ie(this.hass);return Y` + + + + `}static get styles(){return h` + mushroom-chip { + cursor: pointer; + } + `}};n([_t({attribute:!1})],ul.prototype,"hass",void 0),n([vt()],ul.prototype,"_config",void 0),ul=n([pt(Gs("menu"))],ul);const hl=new Set(["clear-night","cloudy","fog","lightning","lightning-rainy","partlycloudy","pouring","rainy","hail","snowy","snowy-rainy","sunny","windy","windy-variant"]),ml=t=>{if(!t||!t.startsWith("weather-"))return;const e=t.replace("weather-","");return hl.has(e)?nl(e,!0):void 0},pl=["content","icon","icon_color","picture"];let fl=class extends ht{constructor(){super(...arguments),this._templateResults={},this._unsubRenderTemplates=new Map}static async getConfigElement(){return await Promise.resolve().then((function(){return $u})),document.createElement(qs("template"))}static async getStubConfig(t){return{type:"template"}}setConfig(t){pl.forEach((e=>{var i,o;(null===(i=this._config)||void 0===i?void 0:i[e])===t[e]&&(null===(o=this._config)||void 0===o?void 0:o.entity)==t.entity||this._tryDisconnectKey(e)})),this._config=Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)}connectedCallback(){super.connectedCallback(),this._tryConnect()}disconnectedCallback(){this._tryDisconnect()}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}isTemplate(t){var e;const i=null===(e=this._config)||void 0===e?void 0:e[t];return null==i?void 0:i.includes("{")}getValue(t){var e,i,o;return this.isTemplate(t)?null===(i=null===(e=this._templateResults[t])||void 0===e?void 0:e.result)||void 0===i?void 0:i.toString():null===(o=this._config)||void 0===o?void 0:o[t]}render(){if(!this.hass||!this._config)return K;const t=this.getValue("icon"),e=this.getValue("icon_color"),i=this.getValue("content"),o=this.getValue("picture"),n=Ie(this.hass),r=ml(t);return Y` + + ${o?K:r||(t?this.renderIcon(t,e):K)} + ${i?this.renderContent(i):K} + + `}renderIcon(t,e){const i={};if(e){const t=Za(e);i["--color"]=`rgb(${t})`}return Y``}renderContent(t){return Y`${t}`}updated(t){super.updated(t),this._config&&this.hass&&this._tryConnect()}async _tryConnect(){pl.forEach((t=>{this._tryConnectKey(t)}))}async _tryConnectKey(t){var e,i;if(void 0===this._unsubRenderTemplates.get(t)&&this.hass&&this._config&&this.isTemplate(t))try{const i=Fe(this.hass.connection,(e=>{this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:e})}),{template:null!==(e=this._config[t])&&void 0!==e?e:"",entity_ids:this._config.entity_id,variables:{config:this._config,user:this.hass.user.name,entity:this._config.entity},strict:!0});this._unsubRenderTemplates.set(t,i),await i}catch(e){const o={result:null!==(i=this._config[t])&&void 0!==i?i:"",listeners:{all:!1,domains:[],entities:[],time:!1}};this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:o}),this._unsubRenderTemplates.delete(t)}}async _tryDisconnect(){pl.forEach((t=>{this._tryDisconnectKey(t)}))}async _tryDisconnectKey(t){const e=this._unsubRenderTemplates.get(t);if(e)try{(await e)(),this._unsubRenderTemplates.delete(t)}catch(t){if("not_found"!==t.code&&"template_error"!==t.code)throw t}}static get styles(){return h` + mushroom-chip { + cursor: pointer; + } + ha-state-icon { + color: var(--color); + } + ${ol} + `}};n([_t({attribute:!1})],fl.prototype,"hass",void 0),n([vt()],fl.prototype,"_config",void 0),n([vt()],fl.prototype,"_templateResults",void 0),n([vt()],fl.prototype,"_unsubRenderTemplates",void 0),fl=n([pt(Gs("template"))],fl);const gl=()=>{var t,e,i;customElements.get("ha-form")||null===(t=customElements.get("hui-button-card"))||void 0===t||t.getConfigElement(),customElements.get("ha-entity-picker")||null===(e=customElements.get("hui-entities-card"))||void 0===e||e.getConfigElement(),customElements.get("ha-card-conditions-editor")||null===(i=customElements.get("hui-conditional-card"))||void 0===i||i.getConfigElement()},_l=Gs("conditional"),vl=async()=>{if(customElements.get(_l))return;if(!customElements.get("hui-conditional-base")){(await window.loadCardHelpers()).createCardElement({type:"conditional",card:{type:"button"},conditions:[]})}const t=await(async t=>{let e=customElements.get(t);return e||(await customElements.whenDefined(t),customElements.get(t))})("hui-conditional-base");class e extends t{static async getConfigElement(){return await Promise.resolve().then((function(){return Op})),document.createElement(qs("conditional"))}static async getStubConfig(){return{type:"conditional",conditions:[]}}setConfig(t){if(this.validateConfig(t),!t.chip)throw new Error("No chip configured");this._element=Ks(t.chip)}}customElements.get(_l)||customElements.define(_l,e)};function bl(t){return null!=t.attributes.brightness?Math.max(Math.round(100*t.attributes.brightness/255),1):void 0}function yl(t){return null!=t.attributes.rgb_color?t.attributes.rgb_color:void 0}function xl(t){return Ga.rgb(t).l()>96}function wl(t){return Ga.rgb(t).l()>97}function kl(t){return(t=>{var e;return(null===(e=t.attributes.supported_color_modes)||void 0===e?void 0:e.some((t=>De.includes(t))))||!1})(t)}function Cl(t){return(t=>{var e;return(null===(e=t.attributes.supported_color_modes)||void 0===e?void 0:e.some((t=>Le.includes(t))))||!1})(t)}let $l=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return Fp})),document.createElement(qs("light"))}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>"light"===t.split(".")[0]));return{type:"light",entity:e[0]}}setConfig(t){this._config=Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t,e;if(!this.hass||!this._config||!this._config.entity)return K;const i=this._config.entity,o=this.hass.states[i];if(!o)return K;const n=this._config.name||o.attributes.friendly_name||"",r=this._config.icon,a=this.hass.formatEntityState?this.hass.formatEntityState(o):ne(this.hass.localize,o,this.hass.locale,this.hass.config,this.hass.entities),s=Bt(o),l=yl(o),c={};if(l&&(null===(t=this._config)||void 0===t?void 0:t.use_light_color)){const t=l.join(",");c["--color"]=`rgb(${t})`,wl(l)&&(c["--color"]="rgba(var(--rgb-primary-text-color), 0.2)")}const d=rs(null!==(e=this._config.content_info)&&void 0!==e?e:"state",n,a,o,this.hass),u=Ie(this.hass);return Y` + + + ${d?Y`${d}`:K} + + `}static get styles(){return h` + :host { + --color: rgb(var(--rgb-state-light)); + } + mushroom-chip { + cursor: pointer; + } + ha-state-icon.active { + color: var(--color); + } + `}};n([_t({attribute:!1})],$l.prototype,"hass",void 0),n([vt()],$l.prototype,"_config",void 0),$l=n([pt(Gs("light"))],$l);let El=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return Hp})),document.createElement(qs("alarm-control-panel"))}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Bs.includes(t.split(".")[0])));return{type:"alarm-control-panel",entity:e[0]}}setConfig(t){this._config=t}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t;if(!this.hass||!this._config||!this._config.entity)return K;const e=this._config.entity,i=this.hass.states[e];if(!i)return K;const o=this._config.name||i.attributes.friendly_name||"",n=this._config.icon,r=Hs(i.state),a=Ys(i.state),s=this.hass.formatEntityState?this.hass.formatEntityState(i):ne(this.hass.localize,i,this.hass.locale,this.hass.config,this.hass.entities),l={};if(r){const t=Za(r);l["--color"]=`rgb(${t})`}const c=rs(null!==(t=this._config.content_info)&&void 0!==t?t:"state",o,s,i,this.hass),d=Ie(this.hass);return Y` + + + ${c?Y`${c}`:K} + + `}static get styles(){return h` + mushroom-chip { + cursor: pointer; + } + ha-state-icon { + color: var(--color); + } + ha-state-icon.pulse { + animation: 1s ease 0s infinite normal none running pulse; + } + ${xs} + `}};n([_t({attribute:!1})],El.prototype,"hass",void 0),n([vt()],El.prototype,"_config",void 0),El=n([pt(Gs("alarm-control-panel"))],El);let Al=class extends ht{setConfig(){}static get styles(){return h` + :host { + flex-grow: 1; + } + `}};Al=n([pt(Gs("spacer"))],Al);const Sl=`${Rs}-chips-card`,Il=`${Sl}-editor`;Ns({type:Sl,name:"Mushroom Chips Card",description:"Card with chips to display informations"});let Tl=class extends ht{static async getConfigElement(){return await Promise.resolve().then((function(){return df})),document.createElement(Il)}static async getStubConfig(t){const e=await Promise.all([Zs.getStubConfig(t)]);return{type:`custom:${Sl}`,chips:e}}set hass(t){var e;const i=js(this._hass),o=js(t);i!==o&&this.toggleAttribute("dark-mode",o),this._hass=t,null===(e=this.shadowRoot)||void 0===e||e.querySelectorAll("div > *").forEach((e=>{e.hass=t}))}getCardSize(){return 1}setConfig(t){this._config=t}render(){if(!this._config||!this._hass)return K;let t="";this._config.alignment&&(t=`align-${this._config.alignment}`);const e=Ie(this._hass);return Y` + +
    + ${this._config.chips.map((t=>this.renderChip(t)))} +
    +
    + `}renderChip(t){"conditional"===t.type&&vl();const e=Ks(t);return e?(this._hass&&(e.hass=this._hass),e.editMode=this.editMode||this.preview,e.preview=this.preview||this.editMode,Y`${e}`):K}static get styles(){return[Ds.styles,h` + ha-card { + background: none; + box-shadow: none; + border-radius: 0; + border: none; + } + .chip-container { + display: flex; + flex-direction: row; + align-items: flex-start; + justify-content: flex-start; + flex-wrap: wrap; + gap: var(--chip-spacing); + } + .chip-container.align-end { + justify-content: flex-end; + } + .chip-container.align-center { + justify-content: center; + } + .chip-container.align-justify { + justify-content: space-between; + } + `]}};n([_t()],Tl.prototype,"preview",void 0),n([_t()],Tl.prototype,"editMode",void 0),n([vt()],Tl.prototype,"_config",void 0),Tl=n([pt(Sl)],Tl);const zl=`${Rs}-climate-card`,Ol=`${zl}-editor`,Ml=["climate"],jl={auto:"var(--rgb-state-climate-auto)",cool:"var(--rgb-state-climate-cool)",dry:"var(--rgb-state-climate-dry)",fan_only:"var(--rgb-state-climate-fan-only)",heat:"var(--rgb-state-climate-heat)",heat_cool:"var(--rgb-state-climate-heat-cool)",off:"var(--rgb-state-climate-off)"},Dl={cooling:"var(--rgb-state-climate-cool)",drying:"var(--rgb-state-climate-dry)",heating:"var(--rgb-state-climate-heat)",idle:"var(--rgb-state-climate-idle)",off:"var(--rgb-state-climate-off)"},Ll={auto:"mdi:calendar-sync",cool:"mdi:snowflake",dry:"mdi:water-percent",fan_only:"mdi:fan",heat:"mdi:fire",heat_cool:"mdi:autorenew",off:"mdi:power"},Pl={cooling:"mdi:snowflake",drying:"mdi:water-percent",heating:"mdi:fire",idle:"mdi:clock-outline",off:"mdi:power"};function Nl(t){var e;return null!==(e=jl[t])&&void 0!==e?e:jl.off}let Rl=class extends ht{constructor(){super(...arguments),this.fill=!1}callService(t){t.stopPropagation();const e=t.target.mode;this.hass.callService("climate","set_hvac_mode",{entity_id:this.entity.entity_id,hvac_mode:e})}render(){const t=Ie(this.hass),e=this.entity.attributes.hvac_modes.filter((t=>{var e;return(null!==(e=this.modes)&&void 0!==e?e:[]).includes(t)})).sort(je);return Y` + + ${e.map((t=>this.renderModeButton(t)))} + + `}renderModeButton(t){const e={},i="off"===t?"var(--rgb-grey)":Nl(t);return t===this.entity.state&&(e["--icon-color"]=`rgb(${i})`,e["--bg-color"]=`rgba(${i}, 0.2)`),Y` + + + + `}};n([_t({attribute:!1})],Rl.prototype,"hass",void 0),n([_t({attribute:!1})],Rl.prototype,"entity",void 0),n([_t({attribute:!1})],Rl.prototype,"modes",void 0),n([_t()],Rl.prototype,"fill",void 0),Rl=n([pt("mushroom-climate-hvac-modes-control")],Rl);let Fl=class extends ht{constructor(){super(...arguments),this.disabled=!1,this.formatOptions={},this.pending=!1,this.dispatchValue=t=>{this.pending=!1,this.dispatchEvent(new CustomEvent("change",{detail:{value:t}}))},this.debounceDispatchValue=this.dispatchValue}get _precision(){return Math.ceil(Math.log10(1/this._step))}get _step(){var t;return null!==(t=this.step)&&void 0!==t?t:1}_incrementValue(t){if(t.stopPropagation(),null==this.value)return;const e=Qt(this.value+this._step,this._precision);this._processNewValue(e)}_decrementValue(t){if(t.stopPropagation(),null==this.value)return;const e=Qt(this.value-this._step,this._precision);this._processNewValue(e)}firstUpdated(t){super.firstUpdated(t);const e=(t=>{const e=window.getComputedStyle(t).getPropertyValue("--input-number-debounce"),i=parseFloat(e);return isNaN(i)?2e3:i})(this.container);e&&(this.debounceDispatchValue=Te(this.dispatchValue,e))}_processNewValue(t){const e=((t,e,i)=>{let o;return o=e?Math.max(t,e):t,o=i?Math.min(o,i):o,o})(t,this.min,this.max);this.value!==e&&(this.value=e,this.pending=!0),this.debounceDispatchValue(e)}render(){const t=null!=this.value?te(this.value,this.locale,this.formatOptions):"-";return Y` +
    + + + ${t} + + +
    + `}static get styles(){return h` + :host { + --text-color: var(--primary-text-color); + --text-color-disabled: rgb(var(--rgb-disabled)); + --icon-color: var(--primary-text-color); + --icon-color-disabled: rgb(var(--rgb-disabled)); + --bg-color: rgba(var(--rgb-primary-text-color), 0.05); + --bg-color-disabled: rgba(var(--rgb-disabled), 0.2); + height: var(--control-height); + width: calc(var(--control-height) * var(--control-button-ratio) * 3); + flex: none; + } + .container { + box-sizing: border-box; + width: 100%; + height: 100%; + padding: 6px; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + border-radius: var(--control-border-radius); + border: none; + background-color: var(--bg-color); + transition: background-color 280ms ease-in-out; + height: var(--control-height); + overflow: hidden; + } + .button { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding: 4px; + border: none; + background: none; + cursor: pointer; + border-radius: var(--control-border-radius); + line-height: 0; + height: 100%; + } + .minus { + padding-right: 0; + } + .plus { + padding-left: 0; + } + .button:disabled { + cursor: not-allowed; + } + .button ha-icon { + font-size: var(--control-height); + --mdc-icon-size: var(--control-icon-size); + color: var(--icon-color); + pointer-events: none; + } + .button:disabled ha-icon { + color: var(--icon-color-disabled); + } + .value { + text-align: center; + flex-grow: 1; + flex-shrink: 0; + flex-basis: 20px; + font-weight: bold; + color: var(--text-color); + } + .value.disabled { + color: var(--text-color-disabled); + } + .value.pending { + opacity: 0.5; + } + `}};n([_t({attribute:!1})],Fl.prototype,"locale",void 0),n([_t({type:Boolean})],Fl.prototype,"disabled",void 0),n([_t({attribute:!1,type:Number,reflect:!0})],Fl.prototype,"value",void 0),n([_t({type:Number})],Fl.prototype,"step",void 0),n([_t({type:Number})],Fl.prototype,"min",void 0),n([_t({type:Number})],Fl.prototype,"max",void 0),n([_t({attribute:"false"})],Fl.prototype,"formatOptions",void 0),n([vt()],Fl.prototype,"pending",void 0),n([xt("#container")],Fl.prototype,"container",void 0),Fl=n([pt("mushroom-input-number")],Fl);let Vl=class extends ht{constructor(){super(...arguments),this.fill=!1}get _stepSize(){return this.entity.attributes.target_temp_step?this.entity.attributes.target_temp_step:"°F"===this.hass.config.unit_system.temperature?1:.5}onValueChange(t){const e=t.detail.value;this.hass.callService("climate","set_temperature",{entity_id:this.entity.entity_id,temperature:e})}onLowValueChange(t){const e=t.detail.value;this.hass.callService("climate","set_temperature",{entity_id:this.entity.entity_id,target_temp_low:e,target_temp_high:this.entity.attributes.target_temp_high})}onHighValueChange(t){const e=t.detail.value;this.hass.callService("climate","set_temperature",{entity_id:this.entity.entity_id,target_temp_low:this.entity.attributes.target_temp_low,target_temp_high:e})}render(){const t=Ie(this.hass),e=Ut(this.entity),i=1===this._stepSize?{maximumFractionDigits:0}:{minimumFractionDigits:1,maximumFractionDigits:1},o=t=>({"--bg-color":`rgba(var(--rgb-state-climate-${t}), 0.05)`,"--icon-color":`rgb(var(--rgb-state-climate-${t}))`,"--text-color":`rgb(var(--rgb-state-climate-${t}))`});return Y` + + ${null!=this.entity.attributes.temperature?Y` + + `:K} + ${null!=this.entity.attributes.target_temp_low&&null!=this.entity.attributes.target_temp_high?Y` + + `:K} + + `}};n([_t({attribute:!1})],Vl.prototype,"hass",void 0),n([_t({attribute:!1})],Vl.prototype,"entity",void 0),n([_t()],Vl.prototype,"fill",void 0),Vl=n([pt("mushroom-climate-temperature-control")],Vl);const Bl={temperature_control:"mdi:thermometer",hvac_mode_control:"mdi:thermostat"};Ns({type:zl,name:"Mushroom Climate Card",description:"Card for climate entity"});let Ul=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return gf})),document.createElement(Ol)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Ml.includes(t.split(".")[0])));return{type:`custom:${zl}`,entity:e[0]}}get _controls(){if(!this._config||!this._stateObj)return[];const t=this._stateObj,e=[];var i;return(null!=(i=t).attributes.temperature||null!=i.attributes.target_temp_low&&null!=i.attributes.target_temp_high)&&this._config.show_temperature_control&&e.push("temperature_control"),((t,e)=>(t.attributes.hvac_modes||[]).some((t=>(null!=e?e:[]).includes(t))))(t,this._config.hvac_modes)&&e.push("hvac_mode_control"),e}get hasControls(){return this._controls.length>0}_onControlTap(t,e){e.stopPropagation(),this._activeControl=t}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updateActiveControl()}updated(t){super.updated(t),this.hass&&t.has("hass")&&this.updateActiveControl()}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);if(null!==t.attributes.current_temperature){r+=` - ${te(t.attributes.current_temperature,this.hass.locale)} ${this.hass.config.unit_system.temperature}`}const a=Ie(this.hass),s=(!this._config.collapsible_controls||Bt(t))&&this._controls.length;return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e,r)}; + + ${s?Y` +
    + ${this.renderActiveControl(t)} + ${this.renderOtherControls()}
    -
    - `}renderChip(t){"conditional"===t.type&&_s();const e=Xl(t);return e?(this._hass&&(e.hass=this._hass,e.editMode=this.editMode),Y`${e}`):K}static get styles(){return[jl.styles,h` - ha-card { - background: none; - box-shadow: none; - border-radius: 0; - border: none; - } - .chip-container { - display: flex; - flex-direction: row; - align-items: flex-start; - justify-content: flex-start; - flex-wrap: wrap; - margin-bottom: calc(-1 * var(--chip-spacing)); - } - .chip-container.align-end { - justify-content: flex-end; - } - .chip-container.align-center { - justify-content: center; - } - .chip-container.align-justify { - justify-content: space-between; - } - .chip-container * { - margin-bottom: var(--chip-spacing); - } - .chip-container *:not(:last-child) { - margin-right: var(--chip-spacing); - } - .chip-container[rtl] *:not(:last-child) { - margin-right: initial; - margin-left: var(--chip-spacing); - } - `]}};n([_t()],Is.prototype,"editMode",void 0),n([vt()],Is.prototype,"_config",void 0),Is=n([pt(As)],Is);const Ts=`${Nl}-climate-card`,Os=`${Ts}-editor`,zs=["climate"],Ms={auto:"var(--rgb-state-climate-auto)",cool:"var(--rgb-state-climate-cool)",dry:"var(--rgb-state-climate-dry)",fan_only:"var(--rgb-state-climate-fan-only)",heat:"var(--rgb-state-climate-heat)",heat_cool:"var(--rgb-state-climate-heat-cool)",off:"var(--rgb-state-climate-off)"},js={cooling:"var(--rgb-state-climate-cool)",drying:"var(--rgb-state-climate-dry)",heating:"var(--rgb-state-climate-heat)",idle:"var(--rgb-state-climate-idle)",off:"var(--rgb-state-climate-off)"},Ds={auto:"mdi:calendar-sync",cool:"mdi:snowflake",dry:"mdi:water-percent",fan_only:"mdi:fan",heat:"mdi:fire",heat_cool:"mdi:autorenew",off:"mdi:power"},Ls={cooling:"mdi:snowflake",drying:"mdi:water-percent",heating:"mdi:fire",idle:"mdi:clock-outline",off:"mdi:power"};function Ps(t){var e;return null!==(e=Ms[t])&&void 0!==e?e:Ms.off}let Ns=class extends ht{constructor(){super(...arguments),this.fill=!1}callService(t){t.stopPropagation();const e=t.target.mode;this.hass.callService("climate","set_hvac_mode",{entity_id:this.entity.entity_id,hvac_mode:e})}render(){const t=Ie(this.hass),e=this.entity.attributes.hvac_modes.filter((t=>{var e;return(null!==(e=this.modes)&&void 0!==e?e:[]).includes(t)})).sort(je);return Y` - - ${e.map((t=>this.renderModeButton(t)))} - - `}renderModeButton(t){const e={},i="off"===t?"var(--rgb-grey)":Ps(t);return t===this.entity.state&&(e["--icon-color"]=`rgb(${i})`,e["--bg-color"]=`rgba(${i}, 0.2)`),Y` - + + `}renderIcon(t,e){const i=Ut(t),o=Nl(t.state),n={};return n["--icon-color"]=`rgb(${o})`,n["--shape-color"]=`rgba(${o}, 0.2)`,Y` + + + + `}renderBadge(t){return!Ut(t)?super.renderBadge(t):this.renderActionBadge(t)}renderActionBadge(t){const e=t.attributes.hvac_action;if(!e||"off"==e)return K;const i=function(t){var e;return null!==(e=Dl[t])&&void 0!==e?e:Dl.off}(e),o=function(t){var e;return null!==(e=Pl[t])&&void 0!==e?e:""}(e);return o?Y` + + `:K}renderOtherControls(){const t=this._controls.filter((t=>t!=this._activeControl));return Y` + ${t.map((t=>Y` + this._onControlTap(t,e)}> + + + `))} + `}renderActiveControl(t){var e;const i=null!==(e=this._config.hvac_modes)&&void 0!==e?e:[],o=Es(this._config);switch(this._activeControl){case"temperature_control":return Y` + + `;case"hvac_mode_control":return Y` + + `;default:return K}}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-climate-temperature-control, + mushroom-climate-hvac-modes-control { + flex: 1; + } + `]}};n([vt()],Ul.prototype,"_activeControl",void 0),Ul=n([pt(zl)],Ul);const Hl=`${Rs}-cover-card`,Yl=`${Hl}-editor`,Wl=["cover"];let Xl=class extends ht{constructor(){super(...arguments),this.fill=!1}_onOpenTap(t){t.stopPropagation(),this.hass.callService("cover","open_cover",{entity_id:this.entity.entity_id})}_onCloseTap(t){t.stopPropagation(),this.hass.callService("cover","close_cover",{entity_id:this.entity.entity_id})}_onStopTap(t){t.stopPropagation(),this.hass.callService("cover","stop_cover",{entity_id:this.entity.entity_id})}get openDisabled(){const t=!0===this.entity.attributes.assumed_state;return((void 0!==(e=this.entity).attributes.current_position?100===e.attributes.current_position:"open"===e.state)||function(t){return"opening"===t.state}(this.entity))&&!t;var e}get closedDisabled(){const t=!0===this.entity.attributes.assumed_state;return((void 0!==(e=this.entity).attributes.current_position?0===e.attributes.current_position:"closed"===e.state)||function(t){return"closing"===t.state}(this.entity))&&!t;var e}render(){const t=Ie(this.hass);return Y` + + ${Wt(this.entity,1)?Y` + + {switch(t.attributes.device_class){case"awning":case"curtain":case"door":case"gate":return"mdi:arrow-expand-horizontal";default:return"mdi:arrow-up"}})(this.entity)}> + + `:void 0} + ${Wt(this.entity,8)?Y` + - - - `}};n([_t({attribute:!1})],Ns.prototype,"hass",void 0),n([_t({attribute:!1})],Ns.prototype,"entity",void 0),n([_t({attribute:!1})],Ns.prototype,"modes",void 0),n([_t()],Ns.prototype,"fill",void 0),Ns=n([pt("mushroom-climate-hvac-modes-control")],Ns);let Rs=class extends ht{constructor(){super(...arguments),this.disabled=!1,this.formatOptions={},this.pending=!1,this.dispatchValue=t=>{this.pending=!1,this.dispatchEvent(new CustomEvent("change",{detail:{value:t}}))},this.debounceDispatchValue=this.dispatchValue}get _precision(){return Math.ceil(Math.log10(1/this._step))}get _step(){var t;return null!==(t=this.step)&&void 0!==t?t:1}_incrementValue(t){if(t.stopPropagation(),null==this.value)return;const e=Qt(this.value+this._step,this._precision);this._processNewValue(e)}_decrementValue(t){if(t.stopPropagation(),null==this.value)return;const e=Qt(this.value-this._step,this._precision);this._processNewValue(e)}firstUpdated(t){super.firstUpdated(t);const e=(t=>{const e=window.getComputedStyle(t).getPropertyValue("--input-number-debounce"),i=parseFloat(e);return isNaN(i)?2e3:i})(this.container);e&&(this.debounceDispatchValue=Te(this.dispatchValue,e))}_processNewValue(t){const e=((t,e,i)=>{let o;return o=e?Math.max(t,e):t,o=i?Math.min(o,i):o,o})(t,this.min,this.max);this.value!==e&&(this.value=e,this.pending=!0),this.debounceDispatchValue(e)}render(){const t=null!=this.value?te(this.value,this.locale,this.formatOptions):"-";return Y` -
    - - - ${t} - - -
    - `}static get styles(){return h` - :host { - --text-color: var(--primary-text-color); - --text-color-disabled: rgb(var(--rgb-disabled)); - --icon-color: var(--primary-text-color); - --icon-color-disabled: rgb(var(--rgb-disabled)); - --bg-color: rgba(var(--rgb-primary-text-color), 0.05); - --bg-color-disabled: rgba(var(--rgb-disabled), 0.2); - height: var(--control-height); - width: calc(var(--control-height) * var(--control-button-ratio) * 3); - flex: none; - } - .container { - box-sizing: border-box; - width: 100%; - height: 100%; - padding: 6px; - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - border-radius: var(--control-border-radius); - border: none; - background-color: var(--bg-color); - transition: background-color 280ms ease-in-out; - height: var(--control-height); - overflow: hidden; - } - .button { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - padding: 4px; - border: none; - background: none; - cursor: pointer; - border-radius: var(--control-border-radius); - line-height: 0; - height: 100%; - } - .minus { - padding-right: 0; - } - .plus { - padding-left: 0; - } - .button:disabled { - cursor: not-allowed; - } - .button ha-icon { - font-size: var(--control-height); - --mdc-icon-size: var(--control-icon-size); - color: var(--icon-color); - pointer-events: none; - } - .button:disabled ha-icon { - color: var(--icon-color-disabled); - } - .value { - text-align: center; - flex-grow: 1; - flex-shrink: 0; - flex-basis: 20px; - font-weight: bold; - color: var(--text-color); - } - .value.disabled { - color: var(--text-color-disabled); - } - .value.pending { - opacity: 0.5; - } - `}};n([_t({attribute:!1})],Rs.prototype,"locale",void 0),n([_t({type:Boolean})],Rs.prototype,"disabled",void 0),n([_t({attribute:!1,type:Number,reflect:!0})],Rs.prototype,"value",void 0),n([_t({type:Number})],Rs.prototype,"step",void 0),n([_t({type:Number})],Rs.prototype,"min",void 0),n([_t({type:Number})],Rs.prototype,"max",void 0),n([_t({attribute:"false"})],Rs.prototype,"formatOptions",void 0),n([vt()],Rs.prototype,"pending",void 0),n([xt("#container")],Rs.prototype,"container",void 0),Rs=n([pt("mushroom-input-number")],Rs);let Fs=class extends ht{constructor(){super(...arguments),this.fill=!1}get _stepSize(){return this.entity.attributes.target_temp_step?this.entity.attributes.target_temp_step:"°F"===this.hass.config.unit_system.temperature?1:.5}onValueChange(t){const e=t.detail.value;this.hass.callService("climate","set_temperature",{entity_id:this.entity.entity_id,temperature:e})}onLowValueChange(t){const e=t.detail.value;this.hass.callService("climate","set_temperature",{entity_id:this.entity.entity_id,target_temp_low:e,target_temp_high:this.entity.attributes.target_temp_high})}onHighValueChange(t){const e=t.detail.value;this.hass.callService("climate","set_temperature",{entity_id:this.entity.entity_id,target_temp_low:this.entity.attributes.target_temp_low,target_temp_high:e})}render(){const t=Ie(this.hass),e=Ut(this.entity),i=1===this._stepSize?{maximumFractionDigits:0}:{minimumFractionDigits:1,maximumFractionDigits:1},o=t=>({"--bg-color":`rgba(var(--rgb-state-climate-${t}), 0.05)`,"--icon-color":`rgb(var(--rgb-state-climate-${t}))`,"--text-color":`rgb(var(--rgb-state-climate-${t}))`});return Y` - - ${null!=this.entity.attributes.temperature?Y` - - `:K} - ${null!=this.entity.attributes.target_temp_low&&null!=this.entity.attributes.target_temp_high?Y` - - `:K} - - `}};n([_t({attribute:!1})],Fs.prototype,"hass",void 0),n([_t({attribute:!1})],Fs.prototype,"entity",void 0),n([_t()],Fs.prototype,"fill",void 0),Fs=n([pt("mushroom-climate-temperature-control")],Fs);const Vs={temperature_control:"mdi:thermometer",hvac_mode_control:"mdi:thermostat"};Pl({type:Ts,name:"Mushroom Climate Card",description:"Card for climate entity"});let Bs=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return af})),document.createElement(Os)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>zs.includes(t.split(".")[0])));return{type:`custom:${Ts}`,entity:e[0]}}get _controls(){if(!this._config||!this._stateObj)return[];const t=this._stateObj,e=[];var i;return(null!=(i=t).attributes.temperature||null!=i.attributes.target_temp_low&&null!=i.attributes.target_temp_high)&&this._config.show_temperature_control&&e.push("temperature_control"),((t,e)=>(t.attributes.hvac_modes||[]).some((t=>(null!=e?e:[]).includes(t))))(t,this._config.hvac_modes)&&e.push("hvac_mode_control"),e}get hasControls(){return this._controls.length>0}_onControlTap(t,e){e.stopPropagation(),this._activeControl=t}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updateActiveControl()}updated(t){super.updated(t),this.hass&&t.has("hass")&&this.updateActiveControl()}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);if(null!==t.attributes.current_temperature){r+=` - ${te(t.attributes.current_temperature,this.hass.locale)} ${this.hass.config.unit_system.temperature}`}const a=Ie(this.hass),l=(!this._config.collapsible_controls||Bt(t))&&this._controls.length;return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e,r)}; - - ${l?Y` -
    - ${this.renderActiveControl(t)} - ${this.renderOtherControls()} -
    - `:K} -
    -
    - `}renderIcon(t,e){const i=Ut(t),o=Ps(t.state),n={};return n["--icon-color"]=`rgb(${o})`,n["--shape-color"]=`rgba(${o}, 0.2)`,Y` - - - - `}renderBadge(t){return!Ut(t)?super.renderBadge(t):this.renderActionBadge(t)}renderActionBadge(t){const e=t.attributes.hvac_action;if(!e||"off"==e)return K;const i=function(t){var e;return null!==(e=js[t])&&void 0!==e?e:js.off}(e),o=function(t){var e;return null!==(e=Ls[t])&&void 0!==e?e:""}(e);return o?Y` - - `:K}renderOtherControls(){const t=this._controls.filter((t=>t!=this._activeControl));return Y` - ${t.map((t=>Y` - this._onControlTap(t,e)}> - - - `))} - `}renderActiveControl(t){var e;const i=null!==(e=this._config.hvac_modes)&&void 0!==e?e:[],o=$l(this._config);switch(this._activeControl){case"temperature_control":return Y` - - `;case"hvac_mode_control":return Y` - - `;default:return K}}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-climate-temperature-control, - mushroom-climate-hvac-modes-control { - flex: 1; - } - `]}};n([vt()],Bs.prototype,"_activeControl",void 0),Bs=n([pt(Ts)],Bs);const Us=`${Nl}-cover-card`,Hs=`${Us}-editor`,Ys=["cover"];let Ws=class extends ht{constructor(){super(...arguments),this.fill=!1}_onOpenTap(t){t.stopPropagation(),this.hass.callService("cover","open_cover",{entity_id:this.entity.entity_id})}_onCloseTap(t){t.stopPropagation(),this.hass.callService("cover","close_cover",{entity_id:this.entity.entity_id})}_onStopTap(t){t.stopPropagation(),this.hass.callService("cover","stop_cover",{entity_id:this.entity.entity_id})}get openDisabled(){const t=!0===this.entity.attributes.assumed_state;return((void 0!==(e=this.entity).attributes.current_position?100===e.attributes.current_position:"open"===e.state)||function(t){return"opening"===t.state}(this.entity))&&!t;var e}get closedDisabled(){const t=!0===this.entity.attributes.assumed_state;return((void 0!==(e=this.entity).attributes.current_position?0===e.attributes.current_position:"closed"===e.state)||function(t){return"closing"===t.state}(this.entity))&&!t;var e}render(){const t=Ie(this.hass);return Y` - - ${Wt(this.entity,1)?Y` - - {switch(t.attributes.device_class){case"awning":case"curtain":case"door":case"gate":return"mdi:arrow-expand-horizontal";default:return"mdi:arrow-up"}})(this.entity)}> - - `:void 0} - ${Wt(this.entity,8)?Y` - - - - `:void 0} - ${Wt(this.entity,2)?Y` - - {switch(t.attributes.device_class){case"awning":case"curtain":case"door":case"gate":return"mdi:arrow-collapse-horizontal";default:return"mdi:arrow-down"}})(this.entity)}> - - `:void 0} - - `}};n([_t({attribute:!1})],Ws.prototype,"hass",void 0),n([_t({attribute:!1})],Ws.prototype,"entity",void 0),n([_t()],Ws.prototype,"fill",void 0),Ws=n([pt("mushroom-cover-buttons-control")],Ws);var Xs; + @click=${this._onStopTap} + > + +
    + `:void 0} + ${Wt(this.entity,2)?Y` + + {switch(t.attributes.device_class){case"awning":case"curtain":case"door":case"gate":return"mdi:arrow-collapse-horizontal";default:return"mdi:arrow-down"}})(this.entity)}> + + `:void 0} + + `}};n([_t({attribute:!1})],Xl.prototype,"hass",void 0),n([_t({attribute:!1})],Xl.prototype,"entity",void 0),n([_t()],Xl.prototype,"fill",void 0),Xl=n([pt("mushroom-cover-buttons-control")],Xl);var Kl; /*! Hammer.JS - v2.0.7 - 2016-04-22 * http://hammerjs.github.io/ * * Copyright (c) 2016 Jorik Tangelder; - * Licensed under the MIT license */Xs={exports:{}},function(t,e,i,o){var n,r=["","webkit","Moz","MS","ms","o"],a=e.createElement("div"),l="function",s=Math.round,c=Math.abs,d=Date.now;function u(t,e,i){return setTimeout(v(t,i),e)}function h(t,e,i){return!!Array.isArray(t)&&(m(t,i[e],i),!0)}function m(t,e,i){var n;if(t)if(t.forEach)t.forEach(e,i);else if(t.length!==o)for(n=0;n\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",r=t.console&&(t.console.warn||t.console.log);return r&&r.call(t.console,n,o),e.apply(this,arguments)}}n="function"!=typeof Object.assign?function(t){if(t===o||null===t)throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(t),i=1;i-1}function $(t){return t.trim().split(/\s+/g)}function E(t,e,i){if(t.indexOf&&!i)return t.indexOf(e);for(var o=0;oi[e]})),o}function I(t,e){for(var i,n,a=e[0].toUpperCase()+e.slice(1),l=0;l1&&!i.firstMultiple?i.firstMultiple=Q(e):1===r&&(i.firstMultiple=!1);var a=i.firstInput,l=i.firstMultiple,s=l?l.center:a.center,u=e.center=tt(n);e.timeStamp=d(),e.deltaTime=e.timeStamp-a.timeStamp,e.angle=nt(s,u),e.distance=ot(s,u),function(t,e){var i=e.center,o=t.offsetDelta||{},n=t.prevDelta||{},r=t.prevInput||{};e.eventType!==N&&r.eventType!==R||(n=t.prevDelta={x:r.deltaX||0,y:r.deltaY||0},o=t.offsetDelta={x:i.x,y:i.y}),e.deltaX=n.x+(i.x-o.x),e.deltaY=n.y+(i.y-o.y)}(i,e),e.offsetDirection=it(e.deltaX,e.deltaY);var h,m,p=et(e.deltaTime,e.deltaX,e.deltaY);e.overallVelocityX=p.x,e.overallVelocityY=p.y,e.overallVelocity=c(p.x)>c(p.y)?p.x:p.y,e.scale=l?(h=l.pointers,ot((m=n)[0],m[1],q)/ot(h[0],h[1],q)):1,e.rotation=l?function(t,e){return nt(e[1],e[0],q)+nt(t[1],t[0],q)}(l.pointers,n):0,e.maxPointers=i.prevInput?e.pointers.length>i.prevInput.maxPointers?e.pointers.length:i.prevInput.maxPointers:e.pointers.length,function(t,e){var i,n,r,a,l=t.lastInterval||e,s=e.timeStamp-l.timeStamp;if(e.eventType!=F&&(s>P||l.velocity===o)){var d=e.deltaX-l.deltaX,u=e.deltaY-l.deltaY,h=et(s,d,u);n=h.x,r=h.y,i=c(h.x)>c(h.y)?h.x:h.y,a=it(d,u),t.lastInterval=e}else i=l.velocity,n=l.velocityX,r=l.velocityY,a=l.direction;e.velocity=i,e.velocityX=n,e.velocityY=r,e.direction=a}(i,e);var f=t.element;k(e.srcEvent.target,f)&&(f=e.srcEvent.target),e.target=f}(t,i),t.emit("hammer.input",i),t.recognize(i),t.session.prevInput=i}function Q(t){for(var e=[],i=0;i=c(e)?t<0?B:U:e<0?H:Y}function ot(t,e,i){i||(i=G);var o=e[i[0]]-t[i[0]],n=e[i[1]]-t[i[1]];return Math.sqrt(o*o+n*n)}function nt(t,e,i){i||(i=G);var o=e[i[0]]-t[i[0]],n=e[i[1]]-t[i[1]];return 180*Math.atan2(n,o)/Math.PI}Z.prototype={handler:function(){},init:function(){this.evEl&&x(this.element,this.evEl,this.domHandler),this.evTarget&&x(this.target,this.evTarget,this.domHandler),this.evWin&&x(O(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&w(this.element,this.evEl,this.domHandler),this.evTarget&&w(this.target,this.evTarget,this.domHandler),this.evWin&&w(O(this.element),this.evWin,this.domHandler)}};var rt={mousedown:N,mousemove:2,mouseup:R},at="mousedown",lt="mousemove mouseup";function st(){this.evEl=at,this.evWin=lt,this.pressed=!1,Z.apply(this,arguments)}_(st,Z,{handler:function(t){var e=rt[t.type];e&N&&0===t.button&&(this.pressed=!0),2&e&&1!==t.which&&(e=R),this.pressed&&(e&R&&(this.pressed=!1),this.callback(this.manager,e,{pointers:[t],changedPointers:[t],pointerType:L,srcEvent:t}))}});var ct={pointerdown:N,pointermove:2,pointerup:R,pointercancel:F,pointerout:F},dt={2:D,3:"pen",4:L,5:"kinect"},ut="pointerdown",ht="pointermove pointerup pointercancel";function mt(){this.evEl=ut,this.evWin=ht,Z.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}t.MSPointerEvent&&!t.PointerEvent&&(ut="MSPointerDown",ht="MSPointerMove MSPointerUp MSPointerCancel"),_(mt,Z,{handler:function(t){var e=this.store,i=!1,o=t.type.toLowerCase().replace("ms",""),n=ct[o],r=dt[t.pointerType]||t.pointerType,a=r==D,l=E(e,t.pointerId,"pointerId");n&N&&(0===t.button||a)?l<0&&(e.push(t),l=e.length-1):n&(R|F)&&(i=!0),l<0||(e[l]=t,this.callback(this.manager,n,{pointers:e,changedPointers:[t],pointerType:r,srcEvent:t}),i&&e.splice(l,1))}});var pt={touchstart:N,touchmove:2,touchend:R,touchcancel:F};function ft(){this.evTarget="touchstart",this.evWin="touchstart touchmove touchend touchcancel",this.started=!1,Z.apply(this,arguments)}function gt(t,e){var i=A(t.touches),o=A(t.changedTouches);return e&(R|F)&&(i=S(i.concat(o),"identifier")),[i,o]}_(ft,Z,{handler:function(t){var e=pt[t.type];if(e===N&&(this.started=!0),this.started){var i=gt.call(this,t,e);e&(R|F)&&i[0].length-i[1].length==0&&(this.started=!1),this.callback(this.manager,e,{pointers:i[0],changedPointers:i[1],pointerType:D,srcEvent:t})}}});var _t={touchstart:N,touchmove:2,touchend:R,touchcancel:F},vt="touchstart touchmove touchend touchcancel";function bt(){this.evTarget=vt,this.targetIds={},Z.apply(this,arguments)}function yt(t,e){var i=A(t.touches),o=this.targetIds;if(e&(2|N)&&1===i.length)return o[i[0].identifier]=!0,[i,i];var n,r,a=A(t.changedTouches),l=[],s=this.target;if(r=i.filter((function(t){return k(t.target,s)})),e===N)for(n=0;n-1&&o.splice(t,1)}),xt)}}function $t(t){for(var e=t.srcEvent.clientX,i=t.srcEvent.clientY,o=0;o-1&&this.requireFail.splice(e,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(t){return!!this.simultaneous[t.id]},emit:function(t){var e=this,i=this.state;function o(i){e.manager.emit(i,t)}i<8&&o(e.options.event+Rt(i)),o(e.options.event),t.additionalEvent&&o(t.additionalEvent),i>=8&&o(e.options.event+Rt(i))},tryEmit:function(t){if(this.canEmit())return this.emit(t);this.state=Pt},canEmit:function(){for(var t=0;te.threshold&&n&e.direction},attrTest:function(t){return Bt.prototype.attrTest.call(this,t)&&(2&this.state||!(2&this.state)&&this.directionTest(t))},emit:function(t){this.pX=t.deltaX,this.pY=t.deltaY;var e=Ft(t.direction);e&&(t.additionalEvent=this.options.event+e),this._super.emit.call(this,t)}}),_(Ht,Bt,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[Ot]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.scale-1)>this.options.threshold||2&this.state)},emit:function(t){if(1!==t.scale){var e=t.scale<1?"in":"out";t.additionalEvent=this.options.event+e}this._super.emit.call(this,t)}}),_(Yt,Nt,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[It]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,o=t.distancee.time;if(this._input=t,!o||!i||t.eventType&(R|F)&&!n)this.reset();else if(t.eventType&N)this.reset(),this._timer=u((function(){this.state=8,this.tryEmit()}),e.time,this);else if(t.eventType&R)return 8;return Pt},reset:function(){clearTimeout(this._timer)},emit:function(t){8===this.state&&(t&&t.eventType&R?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=d(),this.manager.emit(this.options.event,this._input)))}}),_(Wt,Bt,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[Ot]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.rotation)>this.options.threshold||2&this.state)}}),_(Xt,Bt,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:W|X,pointers:1},getTouchAction:function(){return Ut.prototype.getTouchAction.call(this)},attrTest:function(t){var e,i=this.options.direction;return i&(W|X)?e=t.overallVelocity:i&W?e=t.overallVelocityX:i&X&&(e=t.overallVelocityY),this._super.attrTest.call(this,t)&&i&t.offsetDirection&&t.distance>this.options.threshold&&t.maxPointers==this.options.pointers&&c(e)>this.options.velocity&&t.eventType&R},emit:function(t){var e=Ft(t.offsetDirection);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)}}),_(Kt,Nt,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[Tt]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,o=t.distance{const e=t.center.x,i=t.target.getBoundingClientRect().left,o=t.target.clientWidth;return Math.max(Math.min(1,(e-i)/o),0)};let Gs=class extends ht{constructor(){super(...arguments),this.disabled=!1,this.inactive=!1,this.step=1,this.min=0,this.max=100,this.controlled=!1}valueToPercentage(t){return(t-this.min)/(this.max-this.min)}percentageToValue(t){return(this.max-this.min)*t+this.min}firstUpdated(t){super.firstUpdated(t),this.setupListeners()}connectedCallback(){super.connectedCallback(),this.setupListeners()}disconnectedCallback(){super.disconnectedCallback(),this.destroyListeners()}setupListeners(){if(this.slider&&!this._mc){const t=(t=>{const e=window.getComputedStyle(t).getPropertyValue("--slider-threshold"),i=parseFloat(e);return isNaN(i)?10:i})(this.slider);let e;this._mc=new Hammer.Manager(this.slider,{touchAction:"pan-y"}),this._mc.add(new Hammer.Pan({threshold:t,direction:Hammer.DIRECTION_ALL,enable:!0})),this._mc.add(new Hammer.Tap({event:"singletap"})),this._mc.on("panstart",(()=>{this.disabled||(this.controlled=!0,e=this.value)})),this._mc.on("pancancel",(()=>{this.disabled||(this.controlled=!1,this.value=e)})),this._mc.on("panmove",(t=>{if(this.disabled)return;const e=Ks(t);this.value=this.percentageToValue(e),this.dispatchEvent(new CustomEvent("current-change",{detail:{value:Math.round(this.value/this.step)*this.step}}))})),this._mc.on("panend",(t=>{if(this.disabled)return;this.controlled=!1;const e=Ks(t);this.value=Math.round(this.percentageToValue(e)/this.step)*this.step,this.dispatchEvent(new CustomEvent("current-change",{detail:{value:void 0}})),this.dispatchEvent(new CustomEvent("change",{detail:{value:this.value}}))})),this._mc.on("singletap",(t=>{if(this.disabled)return;const e=Ks(t);this.value=Math.round(this.percentageToValue(e)/this.step)*this.step,this.dispatchEvent(new CustomEvent("change",{detail:{value:this.value}}))}))}}destroyListeners(){this._mc&&(this._mc.destroy(),this._mc=void 0)}render(){var t;return Y` -
    -
    -
    - ${this.showActive?Y`
    `:K} - ${this.showIndicator?Y`
    `:K} + * Licensed under the MIT license */Kl={exports:{}},function(t,e,i,o){var n,r=["","webkit","Moz","MS","ms","o"],a=e.createElement("div"),s="function",l=Math.round,c=Math.abs,d=Date.now;function u(t,e,i){return setTimeout(v(t,i),e)}function h(t,e,i){return!!Array.isArray(t)&&(m(t,i[e],i),!0)}function m(t,e,i){var n;if(t)if(t.forEach)t.forEach(e,i);else if(t.length!==o)for(n=0;n\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",r=t.console&&(t.console.warn||t.console.log);return r&&r.call(t.console,n,o),e.apply(this,arguments)}}n="function"!=typeof Object.assign?function(t){if(t===o||null===t)throw new TypeError("Cannot convert undefined or null to object");for(var e=Object(t),i=1;i-1}function $(t){return t.trim().split(/\s+/g)}function E(t,e,i){if(t.indexOf&&!i)return t.indexOf(e);for(var o=0;oi[e]})),o}function I(t,e){for(var i,n,a=e[0].toUpperCase()+e.slice(1),s=0;s1&&!i.firstMultiple?i.firstMultiple=Q(e):1===r&&(i.firstMultiple=!1);var a=i.firstInput,s=i.firstMultiple,l=s?s.center:a.center,u=e.center=tt(n);e.timeStamp=d(),e.deltaTime=e.timeStamp-a.timeStamp,e.angle=nt(l,u),e.distance=ot(l,u),function(t,e){var i=e.center,o=t.offsetDelta||{},n=t.prevDelta||{},r=t.prevInput||{};e.eventType!==N&&r.eventType!==R||(n=t.prevDelta={x:r.deltaX||0,y:r.deltaY||0},o=t.offsetDelta={x:i.x,y:i.y}),e.deltaX=n.x+(i.x-o.x),e.deltaY=n.y+(i.y-o.y)}(i,e),e.offsetDirection=it(e.deltaX,e.deltaY);var h,m,p=et(e.deltaTime,e.deltaX,e.deltaY);e.overallVelocityX=p.x,e.overallVelocityY=p.y,e.overallVelocity=c(p.x)>c(p.y)?p.x:p.y,e.scale=s?(h=s.pointers,ot((m=n)[0],m[1],q)/ot(h[0],h[1],q)):1,e.rotation=s?function(t,e){return nt(e[1],e[0],q)+nt(t[1],t[0],q)}(s.pointers,n):0,e.maxPointers=i.prevInput?e.pointers.length>i.prevInput.maxPointers?e.pointers.length:i.prevInput.maxPointers:e.pointers.length,function(t,e){var i,n,r,a,s=t.lastInterval||e,l=e.timeStamp-s.timeStamp;if(e.eventType!=F&&(l>P||s.velocity===o)){var d=e.deltaX-s.deltaX,u=e.deltaY-s.deltaY,h=et(l,d,u);n=h.x,r=h.y,i=c(h.x)>c(h.y)?h.x:h.y,a=it(d,u),t.lastInterval=e}else i=s.velocity,n=s.velocityX,r=s.velocityY,a=s.direction;e.velocity=i,e.velocityX=n,e.velocityY=r,e.direction=a}(i,e);var f=t.element;k(e.srcEvent.target,f)&&(f=e.srcEvent.target),e.target=f}(t,i),t.emit("hammer.input",i),t.recognize(i),t.session.prevInput=i}function Q(t){for(var e=[],i=0;i=c(e)?t<0?B:U:e<0?H:Y}function ot(t,e,i){i||(i=G);var o=e[i[0]]-t[i[0]],n=e[i[1]]-t[i[1]];return Math.sqrt(o*o+n*n)}function nt(t,e,i){i||(i=G);var o=e[i[0]]-t[i[0]],n=e[i[1]]-t[i[1]];return 180*Math.atan2(n,o)/Math.PI}Z.prototype={handler:function(){},init:function(){this.evEl&&x(this.element,this.evEl,this.domHandler),this.evTarget&&x(this.target,this.evTarget,this.domHandler),this.evWin&&x(z(this.element),this.evWin,this.domHandler)},destroy:function(){this.evEl&&w(this.element,this.evEl,this.domHandler),this.evTarget&&w(this.target,this.evTarget,this.domHandler),this.evWin&&w(z(this.element),this.evWin,this.domHandler)}};var rt={mousedown:N,mousemove:2,mouseup:R},at="mousedown",st="mousemove mouseup";function lt(){this.evEl=at,this.evWin=st,this.pressed=!1,Z.apply(this,arguments)}_(lt,Z,{handler:function(t){var e=rt[t.type];e&N&&0===t.button&&(this.pressed=!0),2&e&&1!==t.which&&(e=R),this.pressed&&(e&R&&(this.pressed=!1),this.callback(this.manager,e,{pointers:[t],changedPointers:[t],pointerType:L,srcEvent:t}))}});var ct={pointerdown:N,pointermove:2,pointerup:R,pointercancel:F,pointerout:F},dt={2:D,3:"pen",4:L,5:"kinect"},ut="pointerdown",ht="pointermove pointerup pointercancel";function mt(){this.evEl=ut,this.evWin=ht,Z.apply(this,arguments),this.store=this.manager.session.pointerEvents=[]}t.MSPointerEvent&&!t.PointerEvent&&(ut="MSPointerDown",ht="MSPointerMove MSPointerUp MSPointerCancel"),_(mt,Z,{handler:function(t){var e=this.store,i=!1,o=t.type.toLowerCase().replace("ms",""),n=ct[o],r=dt[t.pointerType]||t.pointerType,a=r==D,s=E(e,t.pointerId,"pointerId");n&N&&(0===t.button||a)?s<0&&(e.push(t),s=e.length-1):n&(R|F)&&(i=!0),s<0||(e[s]=t,this.callback(this.manager,n,{pointers:e,changedPointers:[t],pointerType:r,srcEvent:t}),i&&e.splice(s,1))}});var pt={touchstart:N,touchmove:2,touchend:R,touchcancel:F};function ft(){this.evTarget="touchstart",this.evWin="touchstart touchmove touchend touchcancel",this.started=!1,Z.apply(this,arguments)}function gt(t,e){var i=A(t.touches),o=A(t.changedTouches);return e&(R|F)&&(i=S(i.concat(o),"identifier")),[i,o]}_(ft,Z,{handler:function(t){var e=pt[t.type];if(e===N&&(this.started=!0),this.started){var i=gt.call(this,t,e);e&(R|F)&&i[0].length-i[1].length==0&&(this.started=!1),this.callback(this.manager,e,{pointers:i[0],changedPointers:i[1],pointerType:D,srcEvent:t})}}});var _t={touchstart:N,touchmove:2,touchend:R,touchcancel:F},vt="touchstart touchmove touchend touchcancel";function bt(){this.evTarget=vt,this.targetIds={},Z.apply(this,arguments)}function yt(t,e){var i=A(t.touches),o=this.targetIds;if(e&(2|N)&&1===i.length)return o[i[0].identifier]=!0,[i,i];var n,r,a=A(t.changedTouches),s=[],l=this.target;if(r=i.filter((function(t){return k(t.target,l)})),e===N)for(n=0;n-1&&o.splice(t,1)}),xt)}}function $t(t){for(var e=t.srcEvent.clientX,i=t.srcEvent.clientY,o=0;o-1&&this.requireFail.splice(e,1),this},hasRequireFailures:function(){return this.requireFail.length>0},canRecognizeWith:function(t){return!!this.simultaneous[t.id]},emit:function(t){var e=this,i=this.state;function o(i){e.manager.emit(i,t)}i<8&&o(e.options.event+Rt(i)),o(e.options.event),t.additionalEvent&&o(t.additionalEvent),i>=8&&o(e.options.event+Rt(i))},tryEmit:function(t){if(this.canEmit())return this.emit(t);this.state=Pt},canEmit:function(){for(var t=0;te.threshold&&n&e.direction},attrTest:function(t){return Bt.prototype.attrTest.call(this,t)&&(2&this.state||!(2&this.state)&&this.directionTest(t))},emit:function(t){this.pX=t.deltaX,this.pY=t.deltaY;var e=Ft(t.direction);e&&(t.additionalEvent=this.options.event+e),this._super.emit.call(this,t)}}),_(Ht,Bt,{defaults:{event:"pinch",threshold:0,pointers:2},getTouchAction:function(){return[zt]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.scale-1)>this.options.threshold||2&this.state)},emit:function(t){if(1!==t.scale){var e=t.scale<1?"in":"out";t.additionalEvent=this.options.event+e}this._super.emit.call(this,t)}}),_(Yt,Nt,{defaults:{event:"press",pointers:1,time:251,threshold:9},getTouchAction:function(){return[It]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,o=t.distancee.time;if(this._input=t,!o||!i||t.eventType&(R|F)&&!n)this.reset();else if(t.eventType&N)this.reset(),this._timer=u((function(){this.state=8,this.tryEmit()}),e.time,this);else if(t.eventType&R)return 8;return Pt},reset:function(){clearTimeout(this._timer)},emit:function(t){8===this.state&&(t&&t.eventType&R?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=d(),this.manager.emit(this.options.event,this._input)))}}),_(Wt,Bt,{defaults:{event:"rotate",threshold:0,pointers:2},getTouchAction:function(){return[zt]},attrTest:function(t){return this._super.attrTest.call(this,t)&&(Math.abs(t.rotation)>this.options.threshold||2&this.state)}}),_(Xt,Bt,{defaults:{event:"swipe",threshold:10,velocity:.3,direction:W|X,pointers:1},getTouchAction:function(){return Ut.prototype.getTouchAction.call(this)},attrTest:function(t){var e,i=this.options.direction;return i&(W|X)?e=t.overallVelocity:i&W?e=t.overallVelocityX:i&X&&(e=t.overallVelocityY),this._super.attrTest.call(this,t)&&i&t.offsetDirection&&t.distance>this.options.threshold&&t.maxPointers==this.options.pointers&&c(e)>this.options.velocity&&t.eventType&R},emit:function(t){var e=Ft(t.offsetDirection);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)}}),_(Kt,Nt,{defaults:{event:"tap",pointers:1,taps:1,interval:300,time:250,threshold:9,posThreshold:10},getTouchAction:function(){return[Tt]},process:function(t){var e=this.options,i=t.pointers.length===e.pointers,o=t.distance{const e=t.center.x,i=t.target.getBoundingClientRect().left,o=t.target.clientWidth;return Math.max(Math.min(1,(e-i)/o),0)};let ql=class extends ht{constructor(){super(...arguments),this.disabled=!1,this.inactive=!1,this.step=1,this.min=0,this.max=100,this.controlled=!1}valueToPercentage(t){return(t-this.min)/(this.max-this.min)}percentageToValue(t){return(this.max-this.min)*t+this.min}firstUpdated(t){super.firstUpdated(t),this.setupListeners()}connectedCallback(){super.connectedCallback(),this.setupListeners()}disconnectedCallback(){super.disconnectedCallback(),this.destroyListeners()}setupListeners(){if(this.slider&&!this._mc){const t=(t=>{const e=window.getComputedStyle(t).getPropertyValue("--slider-threshold"),i=parseFloat(e);return isNaN(i)?10:i})(this.slider);let e;this._mc=new Hammer.Manager(this.slider,{touchAction:"pan-y"}),this._mc.add(new Hammer.Pan({threshold:t,direction:Hammer.DIRECTION_ALL,enable:!0})),this._mc.add(new Hammer.Tap({event:"singletap"})),this._mc.on("panstart",(()=>{this.disabled||(this.controlled=!0,e=this.value)})),this._mc.on("pancancel",(()=>{this.disabled||(this.controlled=!1,this.value=e)})),this._mc.on("panmove",(t=>{if(this.disabled)return;const e=Gl(t);this.value=this.percentageToValue(e),this.dispatchEvent(new CustomEvent("current-change",{detail:{value:Math.round(this.value/this.step)*this.step}}))})),this._mc.on("panend",(t=>{if(this.disabled)return;this.controlled=!1;const e=Gl(t);this.value=Math.round(this.percentageToValue(e)/this.step)*this.step,this.dispatchEvent(new CustomEvent("current-change",{detail:{value:void 0}})),this.dispatchEvent(new CustomEvent("change",{detail:{value:this.value}}))})),this._mc.on("singletap",(t=>{if(this.disabled)return;const e=Gl(t);this.value=Math.round(this.percentageToValue(e)/this.step)*this.step,this.dispatchEvent(new CustomEvent("change",{detail:{value:this.value}}))}))}}destroyListeners(){this._mc&&(this._mc.destroy(),this._mc=void 0)}render(){var t;return Y` +
    +
    +
    + ${this.showActive?Y`
    `:K} + ${this.showIndicator?Y`
    `:K} +
    +
    + `}static get styles(){return h` + :host { + --main-color: rgba(var(--rgb-secondary-text-color), 1); + --bg-gradient: none; + --bg-color: rgba(var(--rgb-secondary-text-color), 0.2); + --main-color-inactive: rgb(var(--rgb-disabled)); + --bg-color-inactive: rgba(var(--rgb-disabled), 0.2); + } + .container { + display: flex; + flex-direction: row; + height: var(--control-height); + } + .slider { + position: relative; + height: 100%; + width: 100%; + border-radius: var(--control-border-radius); + transform: translateZ(0); + overflow: hidden; + cursor: pointer; + } + .slider * { + pointer-events: none; + } + .slider .slider-track-background { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + background-color: var(--bg-color); + background-image: var(--gradient); + } + .slider .slider-track-active { + position: absolute; + top: 0; + left: 0; + height: 100%; + width: 100%; + transform: scale3d(var(--value, 0), 1, 1); + transform-origin: left; + background-color: var(--main-color); + transition: transform 180ms ease-in-out; + } + .slider .slider-track-indicator { + position: absolute; + top: 0; + bottom: 0; + left: calc(var(--value, 0) * (100% - 10px)); + width: 10px; + border-radius: 3px; + background-color: white; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); + transition: left 180ms ease-in-out; + } + .slider .slider-track-indicator:after { + display: block; + content: ""; + background-color: var(--main-color); + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + margin: auto; + height: 20px; + width: 2px; + border-radius: 1px; + } + .inactive .slider .slider-track-background { + background-color: var(--bg-color-inactive); + background-image: none; + } + .inactive .slider .slider-track-indicator:after { + background-color: var(--main-color-inactive); + } + .inactive .slider .slider-track-active { + background-color: var(--main-color-inactive); + } + .controlled .slider .slider-track-active { + transition: none; + } + .controlled .slider .slider-track-indicator { + transition: none; + } + `}};function Zl(t){return null!=t.attributes.current_position?Math.round(t.attributes.current_position):void 0}function Jl(t){const e=t.state;return"open"===e||"opening"===e?"var(--rgb-state-cover-open)":"closed"===e||"closing"===e?"var(--rgb-state-cover-closed)":"var(--rgb-disabled)"}n([_t({type:Boolean})],ql.prototype,"disabled",void 0),n([_t({type:Boolean})],ql.prototype,"inactive",void 0),n([_t({type:Boolean,attribute:"show-active"})],ql.prototype,"showActive",void 0),n([_t({type:Boolean,attribute:"show-indicator"})],ql.prototype,"showIndicator",void 0),n([_t({attribute:!1,type:Number,reflect:!0})],ql.prototype,"value",void 0),n([_t({type:Number})],ql.prototype,"step",void 0),n([_t({type:Number})],ql.prototype,"min",void 0),n([_t({type:Number})],ql.prototype,"max",void 0),n([vt()],ql.prototype,"controlled",void 0),n([xt("#slider")],ql.prototype,"slider",void 0),ql=n([pt("mushroom-slider")],ql);let Ql=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("cover","set_cover_position",{entity_id:this.entity.entity_id,position:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=Zl(this.entity);return Y` + + `}static get styles(){return h` + mushroom-slider { + --main-color: var(--slider-color); + --bg-color: var(--slider-bg-color); + } + `}};n([_t({attribute:!1})],Ql.prototype,"hass",void 0),n([_t({attribute:!1})],Ql.prototype,"entity",void 0),Ql=n([pt("mushroom-cover-position-control")],Ql);const tc=function(t=24,e=.2){const i=[];for(let o=0;o + `}static get styles(){const t=tc.map((([t,e])=>`${e} ${100*t}%`)).join(", ");return h` + mushroom-slider { + --main-color: var(--slider-color); + --bg-color: var(--slider-bg-color); + --gradient: -webkit-linear-gradient(right, ${u(t)}); + } + `}};n([_t({attribute:!1})],ec.prototype,"hass",void 0),n([_t({attribute:!1})],ec.prototype,"entity",void 0),ec=n([pt("mushroom-cover-tilt-position-control")],ec);const ic={buttons_control:"mdi:gesture-tap-button",position_control:"mdi:gesture-swipe-horizontal",tilt_position_control:"mdi:rotate-right"};Ns({type:Hl,name:"Mushroom Cover Card",description:"Card for cover entity"});let oc=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return xf})),document.createElement(Yl)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Wl.includes(t.split(".")[0])));return{type:`custom:${Hl}`,entity:e[0]}}get hasControls(){return this._controls.length>0}get _nextControl(){var t;if(this._activeControl)return null!==(t=this._controls[this._controls.indexOf(this._activeControl)+1])&&void 0!==t?t:this._controls[0]}_onNextControlTap(t){t.stopPropagation(),this._activeControl=this._nextControl}getCardSize(){return 1}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updateActiveControl(),this.updatePosition()}get _controls(){if(!this._config||!this._stateObj)return[];const t=[];return this._config.show_buttons_control&&t.push("buttons_control"),this._config.show_position_control&&t.push("position_control"),this._config.show_tilt_position_control&&t.push("tilt_position_control"),t}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}updated(t){super.updated(t),this.hass&&t.has("hass")&&(this.updatePosition(),this.updateActiveControl())}updatePosition(){this.position=void 0;const t=this._stateObj;t&&(this.position=Zl(t))}onCurrentPositionChange(t){null!=t.detail.value&&(this.position=t.detail.value)}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);this.position&&(r+=` - ${this.position}${oe(this.hass.locale)}%`);const a=Ie(this.hass);return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e,r)}; + + ${this._controls.length>0?Y` +
    + ${this.renderActiveControl(t,o.layout)} + ${this.renderNextControlButton()}
    -
    - `}static get styles(){return h` - :host { - --main-color: rgba(var(--rgb-secondary-text-color), 1); - --bg-gradient: none; - --bg-color: rgba(var(--rgb-secondary-text-color), 0.2); - --main-color-inactive: rgb(var(--rgb-disabled)); - --bg-color-inactive: rgba(var(--rgb-disabled), 0.2); - } - .container { - display: flex; - flex-direction: row; - height: var(--control-height); - } - .slider { - position: relative; - height: 100%; - width: 100%; - border-radius: var(--control-border-radius); - transform: translateZ(0); - overflow: hidden; - cursor: pointer; - } - .slider * { - pointer-events: none; - } - .slider .slider-track-background { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - background-color: var(--bg-color); - background-image: var(--gradient); - } - .slider .slider-track-active { - position: absolute; - top: 0; - left: 0; - height: 100%; - width: 100%; - transform: scale3d(var(--value, 0), 1, 1); - transform-origin: left; - background-color: var(--main-color); - transition: transform 180ms ease-in-out; - } - .slider .slider-track-indicator { - position: absolute; - top: 0; - bottom: 0; - left: calc(var(--value, 0) * (100% - 10px)); - width: 10px; - border-radius: 3px; - background-color: white; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12); - transition: left 180ms ease-in-out; - } - .slider .slider-track-indicator:after { - display: block; - content: ""; - background-color: var(--main-color); - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; - margin: auto; - height: 20px; - width: 2px; - border-radius: 1px; - } - .inactive .slider .slider-track-background { - background-color: var(--bg-color-inactive); - background-image: none; - } - .inactive .slider .slider-track-indicator:after { - background-color: var(--main-color-inactive); - } - .inactive .slider .slider-track-active { - background-color: var(--main-color-inactive); - } - .controlled .slider .slider-track-active { - transition: none; - } - .controlled .slider .slider-track-indicator { - transition: none; - } - `}};function qs(t){return null!=t.attributes.current_position?Math.round(t.attributes.current_position):void 0}function Zs(t){const e=t.state;return"open"===e||"opening"===e?"var(--rgb-state-cover-open)":"closed"===e||"closing"===e?"var(--rgb-state-cover-closed)":"var(--rgb-disabled)"}n([_t({type:Boolean})],Gs.prototype,"disabled",void 0),n([_t({type:Boolean})],Gs.prototype,"inactive",void 0),n([_t({type:Boolean,attribute:"show-active"})],Gs.prototype,"showActive",void 0),n([_t({type:Boolean,attribute:"show-indicator"})],Gs.prototype,"showIndicator",void 0),n([_t({attribute:!1,type:Number,reflect:!0})],Gs.prototype,"value",void 0),n([_t({type:Number})],Gs.prototype,"step",void 0),n([_t({type:Number})],Gs.prototype,"min",void 0),n([_t({type:Number})],Gs.prototype,"max",void 0),n([vt()],Gs.prototype,"controlled",void 0),n([xt("#slider")],Gs.prototype,"slider",void 0),Gs=n([pt("mushroom-slider")],Gs);let Js=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("cover","set_cover_position",{entity_id:this.entity.entity_id,position:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=qs(this.entity);return Y` - - `}static get styles(){return h` - mushroom-slider { - --main-color: var(--slider-color); - --bg-color: var(--slider-bg-color); - } - `}};n([_t({attribute:!1})],Js.prototype,"hass",void 0),n([_t({attribute:!1})],Js.prototype,"entity",void 0),Js=n([pt("mushroom-cover-position-control")],Js);const Qs=function(t=24,e=.2){const i=[];for(let o=0;o - `}static get styles(){const t=Qs.map((([t,e])=>`${e} ${100*t}%`)).join(", ");return h` - mushroom-slider { - --main-color: var(--slider-color); - --bg-color: var(--slider-bg-color); - --gradient: -webkit-linear-gradient(right, ${u(t)}); - } - `}};n([_t({attribute:!1})],tc.prototype,"hass",void 0),n([_t({attribute:!1})],tc.prototype,"entity",void 0),tc=n([pt("mushroom-cover-tilt-position-control")],tc);const ec={buttons_control:"mdi:gesture-tap-button",position_control:"mdi:gesture-swipe-horizontal",tilt_position_control:"mdi:rotate-right"};Pl({type:Us,name:"Mushroom Cover Card",description:"Card for cover entity"});let ic=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return uf})),document.createElement(Hs)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Ys.includes(t.split(".")[0])));return{type:`custom:${Us}`,entity:e[0]}}get hasControls(){return this._controls.length>0}get _nextControl(){var t;if(this._activeControl)return null!==(t=this._controls[this._controls.indexOf(this._activeControl)+1])&&void 0!==t?t:this._controls[0]}_onNextControlTap(t){t.stopPropagation(),this._activeControl=this._nextControl}getCardSize(){return 1}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updateActiveControl(),this.updatePosition()}get _controls(){if(!this._config||!this._stateObj)return[];const t=[];return this._config.show_buttons_control&&t.push("buttons_control"),this._config.show_position_control&&t.push("position_control"),this._config.show_tilt_position_control&&t.push("tilt_position_control"),t}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}updated(t){super.updated(t),this.hass&&t.has("hass")&&(this.updatePosition(),this.updateActiveControl())}updatePosition(){this.position=void 0;const t=this._stateObj;t&&(this.position=qs(t))}onCurrentPositionChange(t){null!=t.detail.value&&(this.position=t.detail.value)}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this.hass||!this._config||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);this.position&&(r+=` - ${this.position}${oe(this.hass.locale)}%`);const a=Ie(this.hass);return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e,r)}; - - ${this._controls.length>0?Y` -
    - ${this.renderActiveControl(t,o.layout)} - ${this.renderNextControlButton()} -
    - `:K} -
    -
    - `}renderIcon(t,e){const i={},o=Ut(t),n=Zs(t);return i["--icon-color"]=`rgb(${n})`,i["--shape-color"]=`rgba(${n}, 0.2)`,Y` - - + + `}renderIcon(t,e){const i={},o=Ut(t),n=Jl(t);return i["--icon-color"]=`rgb(${n})`,i["--shape-color"]=`rgba(${n}, 0.2)`,Y` + + + `}renderNextControlButton(){return this._nextControl&&this._nextControl!=this._activeControl?Y` + + + + `:K}renderActiveControl(t,e){switch(this._activeControl){case"buttons_control":return Y` + + `;case"position_control":{const e=Jl(t),i={};return i["--slider-color"]=`rgb(${e})`,i["--slider-bg-color"]=`rgba(${e}, 0.2)`,Y` + + `}case"tilt_position_control":{const e=Jl(t),i={};return i["--slider-color"]=`rgb(${e})`,i["--slider-bg-color"]=`rgba(${e}, 0.2)`,Y` + + `}default:return K}}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-cover)); + --shape-color: rgba(var(--rgb-state-cover), 0.2); + } + mushroom-cover-buttons-control, + mushroom-cover-position-control { + flex: 1; + } + mushroom-cover-tilt-position-control { + flex: 1; + } + `]}};n([vt()],oc.prototype,"_activeControl",void 0),n([vt()],oc.prototype,"position",void 0),oc=n([pt(Hl)],oc);const nc=`${Rs}-entity-card`,rc=`${nc}-editor`;Ns({type:nc,name:"Mushroom Entity Card",description:"Card for all entities"});let ac=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return $f})),document.createElement(rc)}static async getStubConfig(t){const e=Object.keys(t.states);return{type:`custom:${nc}`,entity:e[0]}}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type),r=Ie(this.hass);return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e)}; + + + + `}renderIcon(t,e){var i;const o=Bt(t),n={},r=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(r){const t=Za(r);n["--icon-color"]=`rgb(${t})`,n["--shape-color"]=`rgba(${t}, 0.2)`}return Y` + + + + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-entity)); + --shape-color: rgba(var(--rgb-state-entity), 0.2); + } + `]}};ac=n([pt(nc)],ac);const sc=`${Rs}-fan-card`,lc=`${sc}-editor`,cc=["fan"];function dc(t){return null!=t.attributes.percentage?Math.round(t.attributes.percentage):void 0}function uc(t){return null!=t.attributes.oscillating&&Boolean(t.attributes.oscillating)}let hc=class extends ht{_onTap(t){t.stopPropagation();const e=uc(this.entity);this.hass.callService("fan","oscillate",{entity_id:this.entity.entity_id,oscillating:!e})}render(){const t=uc(this.entity),e=Bt(this.entity);return Y` + + + + `}static get styles(){return h` + :host { + display: flex; + } + mushroom-button.active { + --icon-color: rgb(var(--rgb-state-fan)); + --bg-color: rgba(var(--rgb-state-fan), 0.2); + } + `}};n([_t({attribute:!1})],hc.prototype,"hass",void 0),n([_t({attribute:!1})],hc.prototype,"entity",void 0),hc=n([pt("mushroom-fan-oscillate-control")],hc);let mc=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("fan","set_percentage",{entity_id:this.entity.entity_id,percentage:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=dc(this.entity);return Y` + + `;var e}static get styles(){return h` + mushroom-slider { + --main-color: rgb(var(--rgb-state-fan)); + --bg-color: rgba(var(--rgb-state-fan), 0.2); + } + `}};n([_t({attribute:!1})],mc.prototype,"hass",void 0),n([_t({attribute:!1})],mc.prototype,"entity",void 0),mc=n([pt("mushroom-fan-percentage-control")],mc),Ns({type:sc,name:"Mushroom Fan Card",description:"Card for fan entity"});let pc=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Tf})),document.createElement(lc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>cc.includes(t.split(".")[0])));return{type:`custom:${sc}`,entity:e[0]}}get hasControls(){var t,e;return Boolean(null===(t=this._config)||void 0===t?void 0:t.show_percentage_control)||Boolean(null===(e=this._config)||void 0===e?void 0:e.show_oscillate_control)}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updatePercentage()}updated(t){super.updated(t),this.hass&&t.has("hass")&&this.updatePercentage()}updatePercentage(){this.percentage=void 0;const t=this._stateObj;this._config&&this.hass&&t&&(this.percentage=dc(t))}onCurrentPercentageChange(t){null!=t.detail.value&&(this.percentage=Math.round(t.detail.value))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);null!=this.percentage&&"on"===t.state&&(r=`${this.percentage}${oe(this.hass.locale)}%`);const a=Ie(this.hass),s=(!this._config.collapsible_controls||Bt(t))&&(this._config.show_percentage_control||this._config.show_oscillate_control);return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e,r)}; + + ${s?Y` +
    + ${this._config.show_percentage_control?Y` + + `:K} + ${this._config.show_oscillate_control?Y` + + `:K} +
    + `:K} +
    +
    + `}renderIcon(t,e){var i;let o={};const n=dc(t),r=Bt(t);if(r)if(n){const t=1.5*(n/100)**.5;o["--animation-duration"]=1/t+"s"}else o["--animation-duration"]="1s";return Y` + + + + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-fan)); + --shape-color: rgba(var(--rgb-state-fan), 0.2); + } + .spin ha-state-icon { + animation: var(--animation-duration) infinite linear spin; + } + mushroom-fan-percentage-control { + flex: 1; + } + `]}};n([vt()],pc.prototype,"percentage",void 0),pc=n([pt(sc)],pc);const fc=`${Rs}-humidifier-card`,gc=`${fc}-editor`,_c=["humidifier"];let vc=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("humidifier","set_humidity",{entity_id:this.entity.entity_id,humidity:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=this.entity.attributes.max_humidity||100,e=this.entity.attributes.min_humidity||0;return Y``}static get styles(){return h` + mushroom-slider { + --main-color: rgb(var(--rgb-state-humidifier)); + --bg-color: rgba(var(--rgb-state-humidifier), 0.2); + } + `}};n([_t({attribute:!1})],vc.prototype,"hass",void 0),n([_t({attribute:!1})],vc.prototype,"entity",void 0),n([_t({attribute:!1})],vc.prototype,"color",void 0),vc=n([pt("mushroom-humidifier-humidity-control")],vc),Ns({type:fc,name:"Mushroom Humidifier Card",description:"Card for humidifier entity"});let bc=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Df})),document.createElement(gc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>_c.includes(t.split(".")[0])));return{type:`custom:${fc}`,entity:e[0]}}get hasControls(){var t;return Boolean(null===(t=this._config)||void 0===t?void 0:t.show_target_humidity_control)}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}onCurrentHumidityChange(t){null!=t.detail.value&&(this.humidity=t.detail.value)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);this.humidity&&(r=`${this.humidity}${oe(this.hass.locale)}%`);const a=Ie(this.hass),s=(!this._config.collapsible_controls||Bt(t))&&this._config.show_target_humidity_control;return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e,r)}; + + ${s?Y` +
    + - `}renderNextControlButton(){return this._nextControl&&this._nextControl!=this._activeControl?Y` - - - - `:K}renderActiveControl(t,e){switch(this._activeControl){case"buttons_control":return Y` - - `;case"position_control":{const e=Zs(t),i={};return i["--slider-color"]=`rgb(${e})`,i["--slider-bg-color"]=`rgba(${e}, 0.2)`,Y` - - `}case"tilt_position_control":{const e=Zs(t),i={};return i["--slider-color"]=`rgb(${e})`,i["--slider-bg-color"]=`rgba(${e}, 0.2)`,Y` - - `}default:return K}}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-cover)); - --shape-color: rgba(var(--rgb-state-cover), 0.2); - } - mushroom-cover-buttons-control, - mushroom-cover-position-control { - flex: 1; - } - mushroom-cover-tilt-position-control { - flex: 1; - } - `]}};n([vt()],ic.prototype,"_activeControl",void 0),n([vt()],ic.prototype,"position",void 0),ic=n([pt(Us)],ic);const oc=`${Nl}-entity-card`,nc=`${oc}-editor`;Pl({type:oc,name:"Mushroom Entity Card",description:"Card for all entities"});let rc=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return ff})),document.createElement(nc)}static async getStubConfig(t){const e=Object.keys(t.states);return{type:`custom:${oc}`,entity:e[0]}}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type),r=Ie(this.hass);return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e)}; - - - - `}renderIcon(t,e){var i;const o=Bt(t),n={},r=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(r){const t=qa(r);n["--icon-color"]=`rgb(${t})`,n["--shape-color"]=`rgba(${t}, 0.2)`}return Y` - - - - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-entity)); - --shape-color: rgba(var(--rgb-state-entity), 0.2); - } - `]}};rc=n([pt(oc)],rc);const ac=`${Nl}-fan-card`,lc=`${ac}-editor`,sc=["fan"];function cc(t){return null!=t.attributes.percentage?Math.round(t.attributes.percentage):void 0}function dc(t){return null!=t.attributes.oscillating&&Boolean(t.attributes.oscillating)}let uc=class extends ht{_onTap(t){t.stopPropagation();const e=dc(this.entity);this.hass.callService("fan","oscillate",{entity_id:this.entity.entity_id,oscillating:!e})}render(){const t=dc(this.entity),e=Bt(this.entity);return Y` + .entity=${t} + @current-change=${this.onCurrentHumidityChange} + > +
    + `:K} +
    +
    + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-humidifier)); + --shape-color: rgba(var(--rgb-state-humidifier), 0.2); + } + mushroom-humidifier-humidity-control { + flex: 1; + } + `]}};n([vt()],bc.prototype,"humidity",void 0),bc=n([pt(fc)],bc);const yc=`${Rs}-number-card`,xc=`${yc}-editor`,wc=["number","input_number"];let kc=class extends ht{onChange(t){const e=t.detail.value,i=this.entity.entity_id.split(".")[0];this.hass.callService(i,"set_value",{entity_id:this.entity.entity_id,value:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){var t;const e=Number(this.entity.state),i=null!==(t=ee(this.entity,this.hass.entities[this.entity.entity_id]))&&void 0!==t?t:ie(this.entity.state);return"buttons"===this.displayMode?Y` + + `:Y` + + `}static get styles(){return h` + :host { + --slider-color: rgb(var(--rgb-state-number)); + --slider-outline-color: transparent; + --slider-bg-color: rgba(var(--rgb-state-number), 0.2); + } + mushroom-slider { + --main-color: var(--slider-color); + --bg-color: var(--slider-bg-color); + --main-outline-color: var(--slider-outline-color); + } + `}};n([_t({attribute:!1})],kc.prototype,"hass",void 0),n([_t({attribute:!1})],kc.prototype,"entity",void 0),n([_t({attribute:!1})],kc.prototype,"displayMode",void 0),kc=n([pt("mushroom-number-value-control")],kc),Ns({type:yc,name:"Mushroom Number Card",description:"Card for number and input number entity"});let Cc=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Vf})),document.createElement(xc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>wc.includes(t.split(".")[0])));return{type:`custom:${yc}`,entity:e[0]}}get hasControls(){return!0}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}onCurrentValueChange(t){null!=t.detail.value&&(this.value=t.detail.value)}updated(t){super.updated(t),this.hass&&t.has("hass")&&this.updateValue()}updateValue(){this.value=void 0;const t=this._stateObj;t&&!Number.isNaN(t.state)&&(this.value=Number(t.state))}render(){var t,e,i;if(!this._config||!this.hass||!this._config.entity)return K;const o=this._stateObj;if(!o)return this.renderNotFound(this._config);const n=this._config.name||o.attributes.friendly_name||"",r=this._config.icon,a=Es(this._config),s=as(o,a.icon_type);let l=this.hass.formatEntityState?this.hass.formatEntityState(o):ne(this.hass.localize,o,this.hass.locale,this.hass.config,this.hass.entities);if(void 0!==this.value){l=`${te(this.value,this.hass.locale,null!==(t=ee(o,this.hass.entities[o.entity_id]))&&void 0!==t?t:ie(o.state))} ${null!==(e=o.attributes.unit_of_measurement)&&void 0!==e?e:""}`}const c=Ie(this.hass),d={},u=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(u){const t=Za(u);d["--slider-color"]=`rgb(${t})`,d["--slider-bg-color"]=`rgba(${t}, 0.2)`}return Y` + + + + ${s?this.renderPicture(s):this.renderIcon(o,r)} + ${this.renderBadge(o)} + ${this.renderStateInfo(o,a,n,l)}; + +
    + +
    +
    +
    + `}renderIcon(t,e){var i;const o=Bt(t),n={},r=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(r){const t=Za(r);n["--icon-color"]=`rgb(${t})`,n["--shape-color"]=`rgba(${t}, 0.2)`}return Y` + + + + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-number)); + --shape-color: rgba(var(--rgb-state-number), 0.2); + } + mushroom-number-value-control { + flex: 1; + } + `]}};n([vt()],Cc.prototype,"value",void 0),Cc=n([pt(yc)],Cc);const $c=`${Rs}-light-card`,Ec=`${$c}-editor`,Ac=["light"];let Sc=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("light","turn_on",{entity_id:this.entity.entity_id,brightness_pct:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=bl(this.entity);return Y` + + `}static get styles(){return h` + :host { + --slider-color: rgb(var(--rgb-state-light)); + --slider-outline-color: transparent; + --slider-bg-color: rgba(var(--rgb-state-light), 0.2); + } + mushroom-slider { + --main-color: var(--slider-color); + --bg-color: var(--slider-bg-color); + --main-outline-color: var(--slider-outline-color); + } + `}};n([_t({attribute:!1})],Sc.prototype,"hass",void 0),n([_t({attribute:!1})],Sc.prototype,"entity",void 0),Sc=n([pt("mushroom-light-brightness-control")],Sc);const Ic=[[0,"#f00"],[.17,"#ff0"],[.33,"#0f0"],[.5,"#0ff"],[.66,"#00f"],[.83,"#f0f"],[1,"#f00"]];let Tc=class extends ht{constructor(){super(...arguments),this._percent=0}_percentToRGB(t){return Ga.hsv(360*t,100,100).rgb().array()}_rgbToPercent(t){return Ga.rgb(t).hsv().hue()/360}onChange(t){const e=t.detail.value;this._percent=e;const i=this._percentToRGB(e/100);3===i.length&&this.hass.callService("light","turn_on",{entity_id:this.entity.entity_id,rgb_color:i})}render(){const t=this._percent||100*this._rgbToPercent(this.entity.attributes.rgb_color);return Y` + + `}static get styles(){const t=Ic.map((([t,e])=>`${e} ${100*t}%`)).join(", ");return h` + mushroom-slider { + --gradient: -webkit-linear-gradient(left, ${u(t)}); + } + `}};n([_t({attribute:!1})],Tc.prototype,"hass",void 0),n([_t({attribute:!1})],Tc.prototype,"entity",void 0),Tc=n([pt("mushroom-light-color-control")],Tc);let zc=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("light","turn_on",{entity_id:this.entity.entity_id,color_temp:e})}render(){var t,e;const i=null!=(o=this.entity).attributes.color_temp?Math.round(o.attributes.color_temp):void 0;var o;return Y` + + `}static get styles(){return h` + mushroom-slider { + --gradient: -webkit-linear-gradient( + right, + rgb(255, 160, 0) 0%, + white 100% + ); + } + `}};n([_t({attribute:!1})],zc.prototype,"hass",void 0),n([_t({attribute:!1})],zc.prototype,"entity",void 0),zc=n([pt("mushroom-light-color-temp-control")],zc);const Oc={brightness_control:"mdi:brightness-4",color_temp_control:"mdi:thermometer",color_control:"mdi:palette"};Ns({type:$c,name:"Mushroom Light Card",description:"Card for light entity"});let Mc=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Pp})),document.createElement(Ec)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Ac.includes(t.split(".")[0])));return{type:`custom:${$c}`,entity:e[0]}}get _controls(){if(!this._config||!this._stateObj)return[];const t=this._stateObj,e=[];return this._config.show_brightness_control&&Cl(t)&&e.push("brightness_control"),this._config.show_color_temp_control&&function(t){var e,i;return null!==(i=null===(e=t.attributes.supported_color_modes)||void 0===e?void 0:e.some((t=>["color_temp"].includes(t))))&&void 0!==i&&i}(t)&&e.push("color_temp_control"),this._config.show_color_control&&kl(t)&&e.push("color_control"),e}get hasControls(){return this._controls.length>0}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updateActiveControl(),this.updateBrightness()}_onControlTap(t,e){e.stopPropagation(),this._activeControl=t}updated(t){super.updated(t),this.hass&&t.has("hass")&&(this.updateActiveControl(),this.updateBrightness())}updateBrightness(){this.brightness=void 0;const t=this._stateObj;t&&(this.brightness=bl(t))}onCurrentBrightnessChange(t){null!=t.detail.value&&(this.brightness=t.detail.value)}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);null!=this.brightness&&(r=`${this.brightness}${oe(this.hass.locale)}%`);const a=Ie(this.hass),s=(!this._config.collapsible_controls||Bt(t))&&this._controls.length;return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e,r)}; + + ${s?Y` +
    + ${this.renderActiveControl(t)} + ${this.renderOtherControls()} +
    + `:K} +
    +
    + `}renderIcon(t,e){var i,o;const n=yl(t),r=Bt(t),a={},s=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(n&&(null===(o=this._config)||void 0===o?void 0:o.use_light_color)){const t=n.join(",");a["--icon-color"]=`rgb(${t})`,a["--shape-color"]=`rgba(${t}, 0.25)`,xl(n)&&!this.hass.themes.darkMode&&(a["--shape-outline-color"]="rgba(var(--rgb-primary-text-color), 0.05)",wl(n)&&(a["--icon-color"]="rgba(var(--rgb-primary-text-color), 0.2)"))}else if(s){const t=Za(s);a["--icon-color"]=`rgb(${t})`,a["--shape-color"]=`rgba(${t}, 0.2)`}return Y` + + + + `}renderOtherControls(){const t=this._controls.filter((t=>t!=this._activeControl));return Y` + ${t.map((t=>Y` + this._onControlTap(t,e)}> + + + `))} + `}renderActiveControl(t){var e,i;switch(this._activeControl){case"brightness_control":const o=yl(t),n={},r=null===(e=this._config)||void 0===e?void 0:e.icon_color;if(o&&(null===(i=this._config)||void 0===i?void 0:i.use_light_color)){const t=o.join(",");n["--slider-color"]=`rgb(${t})`,n["--slider-bg-color"]=`rgba(${t}, 0.2)`,xl(o)&&!this.hass.themes.darkMode&&(n["--slider-bg-color"]="rgba(var(--rgb-primary-text-color), 0.05)",n["--slider-color"]="rgba(var(--rgb-primary-text-color), 0.15)")}else if(r){const t=Za(r);n["--slider-color"]=`rgb(${t})`,n["--slider-bg-color"]=`rgba(${t}, 0.2)`}return Y` + + `;case"color_temp_control":return Y` + + `;case"color_control":return Y` + + `;default:return K}}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-light)); + --shape-color: rgba(var(--rgb-state-light), 0.2); + } + mushroom-light-brightness-control, + mushroom-light-color-temp-control, + mushroom-light-color-control { + flex: 1; + } + `]}};n([vt()],Mc.prototype,"_activeControl",void 0),n([vt()],Mc.prototype,"brightness",void 0),Mc=n([pt($c)],Mc);const jc=`${Rs}-lock-card`,Dc=`${jc}-editor`,Lc=["lock"];function Pc(t){return"unlocked"===t.state}function Nc(t){return"locked"===t.state}function Rc(t){switch(t.state){case"locking":case"unlocking":return!0;default:return!1}}const Fc=[{icon:"mdi:lock",title:"lock",serviceName:"lock",isVisible:t=>Pc(t),isDisabled:()=>!1},{icon:"mdi:lock-open",title:"unlock",serviceName:"unlock",isVisible:t=>Nc(t),isDisabled:()=>!1},{icon:"mdi:lock-clock",isVisible:t=>Rc(t),isDisabled:()=>!0},{icon:"mdi:door-open",title:"open",serviceName:"open",isVisible:t=>Wt(t,1)&&Pc(t),isDisabled:t=>Rc(t)}];let Vc=class extends ht{constructor(){super(...arguments),this.fill=!1}callService(t){t.stopPropagation();const e=t.target.entry;this.hass.callService("lock",e.serviceName,{entity_id:this.entity.entity_id})}render(){const t=Ie(this.hass),e=Oo(this.hass);return Y` + ${Fc.filter((t=>t.isVisible(this.entity))).map((t=>Y` - + - `}static get styles(){return h` - :host { - display: flex; - } - mushroom-button.active { - --icon-color: rgb(var(--rgb-state-fan)); - --bg-color: rgba(var(--rgb-state-fan), 0.2); - } - `}};n([_t({attribute:!1})],uc.prototype,"hass",void 0),n([_t({attribute:!1})],uc.prototype,"entity",void 0),uc=n([pt("mushroom-fan-oscillate-control")],uc);let hc=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("fan","set_percentage",{entity_id:this.entity.entity_id,percentage:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=cc(this.entity);return Y` - - `;var e}static get styles(){return h` - mushroom-slider { - --main-color: rgb(var(--rgb-state-fan)); - --bg-color: rgba(var(--rgb-state-fan), 0.2); - } - `}};n([_t({attribute:!1})],hc.prototype,"hass",void 0),n([_t({attribute:!1})],hc.prototype,"entity",void 0),hc=n([pt("mushroom-fan-percentage-control")],hc),Pl({type:ac,name:"Mushroom Fan Card",description:"Card for fan entity"});let mc=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return yf})),document.createElement(lc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>sc.includes(t.split(".")[0])));return{type:`custom:${ac}`,entity:e[0]}}get hasControls(){var t,e;return Boolean(null===(t=this._config)||void 0===t?void 0:t.show_percentage_control)||Boolean(null===(e=this._config)||void 0===e?void 0:e.show_oscillate_control)}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updatePercentage()}updated(t){super.updated(t),this.hass&&t.has("hass")&&this.updatePercentage()}updatePercentage(){this.percentage=void 0;const t=this._stateObj;this._config&&this.hass&&t&&(this.percentage=cc(t))}onCurrentPercentageChange(t){null!=t.detail.value&&(this.percentage=Math.round(t.detail.value))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);null!=this.percentage&&"on"===t.state&&(r=`${this.percentage}${oe(this.hass.locale)}%`);const a=Ie(this.hass),l=(!this._config.collapsible_controls||Bt(t))&&(this._config.show_percentage_control||this._config.show_oscillate_control);return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e,r)}; - - ${l?Y` -
    - ${this._config.show_percentage_control?Y` - - `:K} - ${this._config.show_oscillate_control?Y` - - `:K} -
    - `:K} -
    -
    - `}renderIcon(t,e){var i;let o={};const n=cc(t),r=Bt(t);if(r)if(n){const t=1.5*(n/100)**.5;o["--animation-duration"]=1/t+"s"}else o["--animation-duration"]="1s";return Y` - + `}};n([_t({attribute:!1})],Vc.prototype,"hass",void 0),n([_t({attribute:!1})],Vc.prototype,"entity",void 0),n([_t({type:Boolean})],Vc.prototype,"fill",void 0),Vc=n([pt("mushroom-lock-buttons-control")],Vc),Ns({type:jc,name:"Mushroom Lock Card",description:"Card for all lock entities"});let Bc=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Yf})),document.createElement(Dc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Lc.includes(t.split(".")[0])));return{type:`custom:${jc}`,entity:e[0]}}get hasControls(){return!0}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type),r=Ie(this.hass);return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e)}; + +
    + - - - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-fan)); - --shape-color: rgba(var(--rgb-state-fan), 0.2); - } - .spin ha-state-icon { - animation: var(--animation-duration) infinite linear spin; - } - mushroom-fan-percentage-control { - flex: 1; - } - `]}};n([vt()],mc.prototype,"percentage",void 0),mc=n([pt(ac)],mc);const pc=`${Nl}-humidifier-card`,fc=`${pc}-editor`,gc=["humidifier"];let _c=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("humidifier","set_humidity",{entity_id:this.entity.entity_id,humidity:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=this.entity.attributes.max_humidity||100,e=this.entity.attributes.min_humidity||0;return Y``}static get styles(){return h` - mushroom-slider { - --main-color: rgb(var(--rgb-state-humidifier)); - --bg-color: rgba(var(--rgb-state-humidifier), 0.2); - } - `}};n([_t({attribute:!1})],_c.prototype,"hass",void 0),n([_t({attribute:!1})],_c.prototype,"entity",void 0),n([_t({attribute:!1})],_c.prototype,"color",void 0),_c=n([pt("mushroom-humidifier-humidity-control")],_c),Pl({type:pc,name:"Mushroom Humidifier Card",description:"Card for humidifier entity"});let vc=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return $f})),document.createElement(fc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>gc.includes(t.split(".")[0])));return{type:`custom:${pc}`,entity:e[0]}}get hasControls(){var t;return Boolean(null===(t=this._config)||void 0===t?void 0:t.show_target_humidity_control)}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}onCurrentHumidityChange(t){null!=t.detail.value&&(this.humidity=t.detail.value)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);this.humidity&&(r=`${this.humidity}${oe(this.hass.locale)}%`);const a=Ie(this.hass),l=(!this._config.collapsible_controls||Bt(t))&&this._config.show_target_humidity_control;return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e,r)}; - - ${l?Y` -
    - -
    - `:K} -
    -
    - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-humidifier)); - --shape-color: rgba(var(--rgb-state-humidifier), 0.2); - } - mushroom-humidifier-humidity-control { - flex: 1; - } - `]}};n([vt()],vc.prototype,"humidity",void 0),vc=n([pt(pc)],vc);const bc=`${Nl}-number-card`,yc=`${bc}-editor`,xc=["number","input_number"];let wc=class extends ht{onChange(t){const e=t.detail.value,i=this.entity.entity_id.split(".")[0];this.hass.callService(i,"set_value",{entity_id:this.entity.entity_id,value:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){var t;const e=Number(this.entity.state),i=null!==(t=ee(this.entity,this.hass.entities[this.entity.entity_id]))&&void 0!==t?t:ie(this.entity.state);return"buttons"===this.displayMode?Y` - - `:Y` - - `}static get styles(){return h` - :host { - --slider-color: rgb(var(--rgb-state-number)); - --slider-outline-color: transparent; - --slider-bg-color: rgba(var(--rgb-state-number), 0.2); - } - mushroom-slider { - --main-color: var(--slider-color); - --bg-color: var(--slider-bg-color); - --main-outline-color: var(--slider-outline-color); - } - `}};n([_t({attribute:!1})],wc.prototype,"hass",void 0),n([_t({attribute:!1})],wc.prototype,"entity",void 0),n([_t({attribute:!1})],wc.prototype,"displayMode",void 0),wc=n([pt("mushroom-number-value-control")],wc),Pl({type:bc,name:"Mushroom Number Card",description:"Card for number and input number entity"});let kc=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return Of})),document.createElement(yc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>xc.includes(t.split(".")[0])));return{type:`custom:${bc}`,entity:e[0]}}get hasControls(){return!0}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}onCurrentValueChange(t){null!=t.detail.value&&(this.value=t.detail.value)}updated(t){super.updated(t),this.hass&&t.has("hass")&&this.updateValue()}updateValue(){this.value=void 0;const t=this._stateObj;t&&!Number.isNaN(t.state)&&(this.value=Number(t.state))}render(){var t,e,i;if(!this._config||!this.hass||!this._config.entity)return K;const o=this._stateObj;if(!o)return this.renderNotFound(this._config);const n=this._config.name||o.attributes.friendly_name||"",r=this._config.icon,a=$l(this._config),l=rl(o,a.icon_type);let s=this.hass.formatEntityState?this.hass.formatEntityState(o):ne(this.hass.localize,o,this.hass.locale,this.hass.config,this.hass.entities);if(void 0!==this.value){s=`${te(this.value,this.hass.locale,null!==(t=ee(o,this.hass.entities[o.entity_id]))&&void 0!==t?t:ie(o.state))} ${null!==(e=o.attributes.unit_of_measurement)&&void 0!==e?e:""}`}const c=Ie(this.hass),d={},u=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(u){const t=qa(u);d["--slider-color"]=`rgb(${t})`,d["--slider-bg-color"]=`rgba(${t}, 0.2)`}return Y` - - - - ${l?this.renderPicture(l):this.renderIcon(o,r)} - ${this.renderBadge(o)} - ${this.renderStateInfo(o,a,n,s)}; - -
    - -
    -
    -
    - `}renderIcon(t,e){var i;const o=Bt(t),n={},r=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(r){const t=qa(r);n["--icon-color"]=`rgb(${t})`,n["--shape-color"]=`rgba(${t}, 0.2)`}return Y` - - - - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-number)); - --shape-color: rgba(var(--rgb-state-number), 0.2); - } - mushroom-number-value-control { - flex: 1; - } - `]}};n([vt()],kc.prototype,"value",void 0),kc=n([pt(bc)],kc);const Cc=`${Nl}-light-card`,$c=`${Cc}-editor`,Ec=["light"];let Ac=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("light","turn_on",{entity_id:this.entity.entity_id,brightness_pct:e})}onCurrentChange(t){const e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}render(){const t=vs(this.entity);return Y` - - `}static get styles(){return h` - :host { - --slider-color: rgb(var(--rgb-state-light)); - --slider-outline-color: transparent; - --slider-bg-color: rgba(var(--rgb-state-light), 0.2); - } - mushroom-slider { - --main-color: var(--slider-color); - --bg-color: var(--slider-bg-color); - --main-outline-color: var(--slider-outline-color); - } - `}};n([_t({attribute:!1})],Ac.prototype,"hass",void 0),n([_t({attribute:!1})],Ac.prototype,"entity",void 0),Ac=n([pt("mushroom-light-brightness-control")],Ac);const Sc=[[0,"#f00"],[.17,"#ff0"],[.33,"#0f0"],[.5,"#0ff"],[.66,"#00f"],[.83,"#f0f"],[1,"#f00"]];let Ic=class extends ht{constructor(){super(...arguments),this._percent=0}_percentToRGB(t){return Ka.hsv(360*t,100,100).rgb().array()}_rgbToPercent(t){return Ka.rgb(t).hsv().hue()/360}onChange(t){const e=t.detail.value;this._percent=e;const i=this._percentToRGB(e/100);3===i.length&&this.hass.callService("light","turn_on",{entity_id:this.entity.entity_id,rgb_color:i})}render(){const t=this._percent||100*this._rgbToPercent(this.entity.attributes.rgb_color);return Y` - - `}static get styles(){const t=Sc.map((([t,e])=>`${e} ${100*t}%`)).join(", ");return h` - mushroom-slider { - --gradient: -webkit-linear-gradient(left, ${u(t)}); - } - `}};n([_t({attribute:!1})],Ic.prototype,"hass",void 0),n([_t({attribute:!1})],Ic.prototype,"entity",void 0),Ic=n([pt("mushroom-light-color-control")],Ic);let Tc=class extends ht{onChange(t){const e=t.detail.value;this.hass.callService("light","turn_on",{entity_id:this.entity.entity_id,color_temp:e})}render(){var t,e;const i=null!=(o=this.entity).attributes.color_temp?Math.round(o.attributes.color_temp):void 0;var o;return Y` - - `}static get styles(){return h` - mushroom-slider { - --gradient: -webkit-linear-gradient(right, rgb(255, 160, 0) 0%, white 100%); - } - `}};n([_t({attribute:!1})],Tc.prototype,"hass",void 0),n([_t({attribute:!1})],Tc.prototype,"entity",void 0),Tc=n([pt("mushroom-light-color-temp-control")],Tc);const Oc={brightness_control:"mdi:brightness-4",color_temp_control:"mdi:thermometer",color_control:"mdi:palette"};Pl({type:Cc,name:"Mushroom Light Card",description:"Card for light entity"});let zc=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return Sp})),document.createElement($c)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Ec.includes(t.split(".")[0])));return{type:`custom:${Cc}`,entity:e[0]}}get _controls(){if(!this._config||!this._stateObj)return[];const t=this._stateObj,e=[];return this._config.show_brightness_control&&ks(t)&&e.push("brightness_control"),this._config.show_color_temp_control&&function(t){var e,i;return null!==(i=null===(e=t.attributes.supported_color_modes)||void 0===e?void 0:e.some((t=>["color_temp"].includes(t))))&&void 0!==i&&i}(t)&&e.push("color_temp_control"),this._config.show_color_control&&ws(t)&&e.push("color_control"),e}get hasControls(){return this._controls.length>0}setConfig(t){super.setConfig(Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)),this.updateActiveControl(),this.updateBrightness()}_onControlTap(t,e){e.stopPropagation(),this._activeControl=t}updated(t){super.updated(t),this.hass&&t.has("hass")&&(this.updateActiveControl(),this.updateBrightness())}updateBrightness(){this.brightness=void 0;const t=this._stateObj;t&&(this.brightness=vs(t))}onCurrentBrightnessChange(t){null!=t.detail.value&&(this.brightness=t.detail.value)}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type);let r=this.hass.formatEntityState?this.hass.formatEntityState(t):ne(this.hass.localize,t,this.hass.locale,this.hass.config,this.hass.entities);null!=this.brightness&&(r=`${this.brightness}${oe(this.hass.locale)}%`);const a=Ie(this.hass),l=(!this._config.collapsible_controls||Bt(t))&&this._controls.length;return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e,r)}; - - ${l?Y` -
    - ${this.renderActiveControl(t)} - ${this.renderOtherControls()} -
    - `:K} -
    -
    - `}renderIcon(t,e){var i,o;const n=bs(t),r=Bt(t),a={},l=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(n&&(null===(o=this._config)||void 0===o?void 0:o.use_light_color)){const t=n.join(",");a["--icon-color"]=`rgb(${t})`,a["--shape-color"]=`rgba(${t}, 0.25)`,ys(n)&&!this.hass.themes.darkMode&&(a["--shape-outline-color"]="rgba(var(--rgb-primary-text-color), 0.05)",xs(n)&&(a["--icon-color"]="rgba(var(--rgb-primary-text-color), 0.2)"))}else if(l){const t=qa(l);a["--icon-color"]=`rgb(${t})`,a["--shape-color"]=`rgba(${t}, 0.2)`}return Y` - - - - `}renderOtherControls(){const t=this._controls.filter((t=>t!=this._activeControl));return Y` - ${t.map((t=>Y` - this._onControlTap(t,e)}> - - - `))} - `}renderActiveControl(t){var e,i;switch(this._activeControl){case"brightness_control":const o=bs(t),n={},r=null===(e=this._config)||void 0===e?void 0:e.icon_color;if(o&&(null===(i=this._config)||void 0===i?void 0:i.use_light_color)){const t=o.join(",");n["--slider-color"]=`rgb(${t})`,n["--slider-bg-color"]=`rgba(${t}, 0.2)`,ys(o)&&!this.hass.themes.darkMode&&(n["--slider-bg-color"]="rgba(var(--rgb-primary-text-color), 0.05)",n["--slider-color"]="rgba(var(--rgb-primary-text-color), 0.15)")}else if(r){const t=qa(r);n["--slider-color"]=`rgb(${t})`,n["--slider-bg-color"]=`rgba(${t}, 0.2)`}return Y` - - `;case"color_temp_control":return Y` - - `;case"color_control":return Y` - - `;default:return K}}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-light)); - --shape-color: rgba(var(--rgb-state-light), 0.2); - } - mushroom-light-brightness-control, - mushroom-light-color-temp-control, - mushroom-light-color-control { - flex: 1; - } - `]}};n([vt()],zc.prototype,"_activeControl",void 0),n([vt()],zc.prototype,"brightness",void 0),zc=n([pt(Cc)],zc);const Mc=`${Nl}-lock-card`,jc=`${Mc}-editor`,Dc=["lock"];function Lc(t){return"unlocked"===t.state}function Pc(t){return"locked"===t.state}function Nc(t){switch(t.state){case"locking":case"unlocking":return!0;default:return!1}}const Rc=[{icon:"mdi:lock",title:"lock",serviceName:"lock",isVisible:t=>Lc(t),isDisabled:()=>!1},{icon:"mdi:lock-open",title:"unlock",serviceName:"unlock",isVisible:t=>Pc(t),isDisabled:()=>!1},{icon:"mdi:lock-clock",isVisible:t=>Nc(t),isDisabled:()=>!0},{icon:"mdi:door-open",title:"open",serviceName:"open",isVisible:t=>Wt(t,1)&&Lc(t),isDisabled:t=>Nc(t)}];let Fc=class extends ht{constructor(){super(...arguments),this.fill=!1}callService(t){t.stopPropagation();const e=t.target.entry;this.hass.callService("lock",e.serviceName,{entity_id:this.entity.entity_id})}render(){const t=Ie(this.hass),e=Oo(this.hass);return Y` - ${Rc.filter((t=>t.isVisible(this.entity))).map((t=>Y` - - - - `))} +
    +
    +
    + `}renderIcon(t,e){const i=Ut(t),o={"--icon-color":"rgb(var(--rgb-state-lock))","--shape-color":"rgba(var(--rgb-state-lock), 0.2)"};return Nc(t)?(o["--icon-color"]="rgb(var(--rgb-state-lock-locked))",o["--shape-color"]="rgba(var(--rgb-state-lock-locked), 0.2)"):Pc(t)?(o["--icon-color"]="rgb(var(--rgb-state-lock-unlocked))",o["--shape-color"]="rgba(var(--rgb-state-lock-unlocked), 0.2)"):Rc(t)&&(o["--icon-color"]="rgb(var(--rgb-state-lock-pending))",o["--shape-color"]="rgba(var(--rgb-state-lock-pending), 0.2)"),Y` + + + + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-lock-buttons-control { + flex: 1; + } + `]}};Bc=n([pt(jc)],Bc);const Uc=`${Rs}-media-player-card`,Hc=`${Uc}-editor`,Yc=["media_player"];function Wc(t){return null!=t.attributes.volume_level?100*t.attributes.volume_level:void 0}const Xc=(t,e)=>{if(!t)return[];const i=t.state;if("off"===i)return Wt(t,128)&&e.includes("on_off")?[{icon:"mdi:power",action:"turn_on"}]:[];const o=[];Wt(t,256)&&e.includes("on_off")&&o.push({icon:"mdi:power",action:"turn_off"});const n=!0===t.attributes.assumed_state,r=t.attributes;return("playing"===i||"paused"===i||n)&&Wt(t,32768)&&e.includes("shuffle")&&o.push({icon:!0===r.shuffle?"mdi:shuffle":"mdi:shuffle-disabled",action:"shuffle_set"}),("playing"===i||"paused"===i||n)&&Wt(t,16)&&e.includes("previous")&&o.push({icon:"mdi:skip-previous",action:"media_previous_track"}),!n&&("playing"===i&&(Wt(t,1)||Wt(t,4096))||("paused"===i||"idle"===i)&&Wt(t,Pe)||"on"===i&&(Wt(t,Pe)||Wt(t,1)))&&e.includes("play_pause_stop")&&o.push({icon:"on"===i?"mdi:play-pause":"playing"!==i?"mdi:play":Wt(t,1)?"mdi:pause":"mdi:stop",action:"playing"!==i?"media_play":Wt(t,1)?"media_pause":"media_stop"}),n&&Wt(t,Pe)&&e.includes("play_pause_stop")&&o.push({icon:"mdi:play",action:"media_play"}),n&&Wt(t,1)&&e.includes("play_pause_stop")&&o.push({icon:"mdi:pause",action:"media_pause"}),n&&Wt(t,4096)&&e.includes("play_pause_stop")&&o.push({icon:"mdi:stop",action:"media_stop"}),("playing"===i||"paused"===i||n)&&Wt(t,32)&&e.includes("next")&&o.push({icon:"mdi:skip-next",action:"media_next_track"}),("playing"===i||"paused"===i||n)&&Wt(t,262144)&&e.includes("repeat")&&o.push({icon:"all"===r.repeat?"mdi:repeat":"one"===r.repeat?"mdi:repeat-once":"mdi:repeat-off",action:"repeat_set"}),o.length>0?o:[]},Kc=(t,e,i)=>{let o={};"shuffle_set"===i?o={shuffle:!e.attributes.shuffle}:"repeat_set"===i?o={repeat:"all"===e.attributes.repeat?"one":"off"===e.attributes.repeat?"all":"off"}:"volume_mute"===i&&(o={is_volume_muted:!e.attributes.is_volume_muted}),t.callService("media_player",i,Object.assign({entity_id:e.entity_id},o))};let Gc=class extends ht{constructor(){super(...arguments),this.fill=!1}_handleClick(t){t.stopPropagation();const e=t.target.action;Kc(this.hass,this.entity,e)}render(){const t=Ie(this.hass),e=Xc(this.entity,this.controls);return Y` + + ${e.map((t=>Y` + - `}};n([_t({attribute:!1})],Fc.prototype,"hass",void 0),n([_t({attribute:!1})],Fc.prototype,"entity",void 0),n([_t({type:Boolean})],Fc.prototype,"fill",void 0),Fc=n([pt("mushroom-lock-buttons-control")],Fc),Pl({type:Mc,name:"Mushroom Lock Card",description:"Card for all lock entities"});let Vc=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return Df})),document.createElement(jc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Dc.includes(t.split(".")[0])));return{type:`custom:${Mc}`,entity:e[0]}}get hasControls(){return!0}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type),r=Ie(this.hass);return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e)}; - -
    - - -
    -
    -
    - `}renderIcon(t,e){const i=Ut(t),o={"--icon-color":"rgb(var(--rgb-state-lock))","--shape-color":"rgba(var(--rgb-state-lock), 0.2)"};return Pc(t)?(o["--icon-color"]="rgb(var(--rgb-state-lock-locked))",o["--shape-color"]="rgba(var(--rgb-state-lock-locked), 0.2)"):Lc(t)?(o["--icon-color"]="rgb(var(--rgb-state-lock-unlocked))",o["--shape-color"]="rgba(var(--rgb-state-lock-unlocked), 0.2)"):Nc(t)&&(o["--icon-color"]="rgb(var(--rgb-state-lock-pending))",o["--shape-color"]="rgba(var(--rgb-state-lock-pending), 0.2)"),Y` - - +
    + `))} +
    + `}};n([_t({attribute:!1})],Gc.prototype,"hass",void 0),n([_t({attribute:!1})],Gc.prototype,"entity",void 0),n([_t({attribute:!1})],Gc.prototype,"controls",void 0),n([_t({type:Boolean})],Gc.prototype,"fill",void 0),Gc=n([pt("mushroom-media-player-media-control")],Gc);let qc=class extends ht{constructor(){super(...arguments),this.fill=!1}handleSliderChange(t){const e=t.detail.value;this.hass.callService("media_player","volume_set",{entity_id:this.entity.entity_id,volume_level:e/100})}handleSliderCurrentChange(t){let e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}handleClick(t){t.stopPropagation();const e=t.target.action;Kc(this.hass,this.entity,e)}render(){var t,e,i;if(!this.entity)return K;const o=Wc(this.entity),n=Ie(this.hass),r=(null===(t=this.controls)||void 0===t?void 0:t.includes("volume_set"))&&Wt(this.entity,4),a=(null===(e=this.controls)||void 0===e?void 0:e.includes("volume_mute"))&&Wt(this.entity,8),s=(null===(i=this.controls)||void 0===i?void 0:i.includes("volume_buttons"))&&Wt(this.entity,1024);return Y` + + ${r?Y` `:K} + ${a?Y` + + + + `:void 0} + ${s?Y` + + + `:void 0} + ${s?Y` + + + `:void 0} + + `}static get styles(){return h` + mushroom-slider { + flex: 1; + --main-color: rgb(var(--rgb-state-media-player)); + --bg-color: rgba(var(--rgb-state-media-player), 0.2); + } + `}};n([_t({attribute:!1})],qc.prototype,"hass",void 0),n([_t({attribute:!1})],qc.prototype,"entity",void 0),n([_t({type:Boolean})],qc.prototype,"fill",void 0),n([_t({attribute:!1})],qc.prototype,"controls",void 0),qc=n([pt("mushroom-media-player-volume-control")],qc);const Zc={media_control:"mdi:play-pause",volume_control:"mdi:volume-high"};Ns({type:Uc,name:"Mushroom Media Card",description:"Card for media player entity"});let Jc=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Jf})),document.createElement(Hc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Yc.includes(t.split(".")[0])));return{type:`custom:${Uc}`,entity:e[0]}}get hasControls(){var t,e,i,o;return Boolean(null===(e=null===(t=this._config)||void 0===t?void 0:t.media_controls)||void 0===e?void 0:e.length)||Boolean(null===(o=null===(i=this._config)||void 0===i?void 0:i.volume_controls)||void 0===o?void 0:o.length)}get _controls(){if(!this._config||!this._stateObj)return[];const t=this._stateObj,e=[];return((t,e)=>Xc(t,null!=e?e:[]).length>0)(t,this._config.media_controls)&&e.push("media_control"),((t,e)=>(null==e?void 0:e.includes("volume_buttons"))&&Wt(t,1024)||(null==e?void 0:e.includes("volume_mute"))&&Wt(t,8)||(null==e?void 0:e.includes("volume_set"))&&Wt(t,4))(t,this._config.volume_controls)&&e.push("volume_control"),e}_onControlTap(t,e){e.stopPropagation(),this._activeControl=t}setConfig(t){super.setConfig(t),this.updateActiveControl(),this.updateVolume()}updated(t){super.updated(t),this.hass&&t.has("hass")&&(this.updateActiveControl(),this.updateVolume())}updateVolume(){this.volume=void 0;const t=this._stateObj;if(!t)return;const e=Wc(t);this.volume=null!=e?Math.round(e):e}onCurrentVolumeChange(t){null!=t.detail.value&&(this.volume=t.detail.value)}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=function(t,e){var i,o=t.icon;if(![Nt,Rt,Ft].includes(e.state)&&t.use_media_info)switch(null===(i=e.attributes.app_name)||void 0===i?void 0:i.toLowerCase()){case"spotify":return"mdi:spotify";case"google podcasts":return"mdi:google-podcast";case"plex":return"mdi:plex";case"soundcloud":return"mdi:soundcloud";case"youtube":return"mdi:youtube";case"oto music":return"mdi:music-circle";case"netflix":return"mdi:netflix";default:return}return o}(this._config,t),i=function(t,e){let i=t.name||e.attributes.friendly_name||"";return![Nt,Rt,Ft].includes(e.state)&&t.use_media_info&&e.attributes.media_title&&(i=e.attributes.media_title),i}(this._config,t),o=function(t,e,i){let o=i.formatEntityState?i.formatEntityState(e):ne(i.localize,e,i.locale,i.config,i.entities);return![Nt,Rt,Ft].includes(e.state)&&t.use_media_info&&(t=>{let e;switch(t.attributes.media_content_type){case"music":case"image":e=t.attributes.media_artist;break;case"playlist":e=t.attributes.media_playlist;break;case"tvshow":e=t.attributes.media_series_title,t.attributes.media_season&&(e+=" S"+t.attributes.media_season,t.attributes.media_episode&&(e+="E"+t.attributes.media_episode));break;default:e=t.attributes.app_name||""}return e})(e)||o}(this._config,t,this.hass),n=Es(this._config),r=as(t,n.icon_type),a=null!=this.volume&&this._config.show_volume_level?`${o} - ${this.volume}${oe(this.hass.locale)}%`:o,s=Ie(this.hass),l=(!this._config.collapsible_controls||Bt(t))&&this._controls.length;return Y` + + + + ${r?this.renderPicture(r):this.renderIcon(t,e)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,n,i,a)}; + + ${l?Y` +
    + ${this.renderActiveControl(t,n.layout)} + ${this.renderOtherControls()} +
    + `:K} +
    +
    + `}renderOtherControls(){const t=this._controls.filter((t=>t!=this._activeControl));return Y` + ${t.map((t=>Y` + this._onControlTap(t,e)}> + + + `))} + `}renderActiveControl(t,e){var i,o,n,r;const a=null!==(o=null===(i=this._config)||void 0===i?void 0:i.media_controls)&&void 0!==o?o:[],s=null!==(r=null===(n=this._config)||void 0===n?void 0:n.volume_controls)&&void 0!==r?r:[];switch(this._activeControl){case"media_control":return Y` + + + `;case"volume_control":return Y` + + `;default:return K}}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-media-player)); + --shape-color: rgba(var(--rgb-state-media-player), 0.2); + } + mushroom-media-player-media-control, + mushroom-media-player-volume-control { + flex: 1; + } + `]}};n([vt()],Jc.prototype,"_activeControl",void 0),n([vt()],Jc.prototype,"volume",void 0),Jc=n([pt(Uc)],Jc);const Qc=`${Rs}-person-card`,td=`${Qc}-editor`,ed=["person","device_tracker"];Ns({type:Qc,name:"Mushroom Person Card",description:"Card for person entity"});let id=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return og})),document.createElement(td)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>ed.includes(t.split(".")[0])));return{type:`custom:${Qc}`,entity:e[0]}}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type),r=Ie(this.hass);return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e)}; + + + + `}renderStateBadge(t){const e=Object.values(this.hass.states).filter((t=>t.entity_id.startsWith("zone."))),i=function(t,e){const i=t.state;if(i===Rt)return"mdi:help";if("not_home"===i)return"mdi:home-export-outline";if("home"===i)return"mdi:home";const o=e.find((t=>i===t.attributes.friendly_name));return o&&o.attributes.icon?o.attributes.icon:"mdi:home"}(t,e),o=function(t,e){const i=t.state;if(i===Rt)return"var(--rgb-state-person-unknown)";if("not_home"===i)return"var(--rgb-state-person-not-home)";if("home"===i)return"var(--rgb-state-person-home)";const o=e.some((t=>i===t.attributes.friendly_name));return o?"var(--rgb-state-person-zone)":"var(--rgb-state-person-home)"}(t,e);return Y` + + `}renderBadge(t){return!Ut(t)?super.renderBadge(t):this.renderStateBadge(t)}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + `]}};id=n([pt(Qc)],id);const od=`${Rs}-select-card`,nd=`${od}-editor`,rd=["input_select","select"];function ad(t){return null!=t.state?t.state:void 0}let sd=class extends ht{_selectChanged(t){const e=t.target.value,i=ad(this.entity);e&&e!==i&&this._setValue(e)}_setValue(t){const e=this.entity.entity_id.split(".")[0];this.hass.callService(e,"select_option",{entity_id:this.entity.entity_id,option:t})}render(){const t=ad(this.entity),e=this.entity.attributes.options;return Y` + t.stopPropagation()} + .value=${null!=t?t:""} + naturalMenuWidth + fixedMenuPosition + > + ${e.map((t=>Y` + + ${this.hass.formatEntityState?this.hass.formatEntityState(this.entity,t):ne(this.hass.localize,this.entity,this.hass.locale,this.hass.config,this.hass.entities,t)} + + `))} + + `}static get styles(){return h` + :host { + display: flex; + height: 100%; + align-items: center; + } + mushroom-select { + --select-height: var(--control-height); + width: 100%; + } + `}};n([_t()],sd.prototype,"hass",void 0),n([_t({attribute:!1})],sd.prototype,"entity",void 0),sd=n([pt("mushroom-select-option-control")],sd),Ns({type:od,name:"Mushroom Select Card",description:"Card for select and input_select entities"});let ld=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return lg})),document.createElement(nd)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>rd.includes(t.split(".")[0])));return{type:`custom:${od}`,entity:e[0]}}get hasControls(){return!0}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t;if(!this._config||!this.hass||!this._config.entity)return K;const e=this._stateObj;if(!e)return this.renderNotFound(this._config);const i=this._config.name||e.attributes.friendly_name||"",o=this._config.icon,n=Es(this._config),r=as(e,n.icon_type),a=Ie(this.hass),s=null===(t=this._config)||void 0===t?void 0:t.icon_color,l={};if(s){const t=Za(s);l["--mdc-theme-primary"]=`rgb(${t})`}return Y` + + + + ${r?this.renderPicture(r):this.renderIcon(e,o)} + ${this.renderBadge(e)} + ${this.renderStateInfo(e,n,i)}; + +
    + +
    +
    +
    + `}renderIcon(t,e){var i;const o=Bt(t),n={},r=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(r){const t=Za(r);n["--icon-color"]=`rgb(${t})`,n["--shape-color"]=`rgba(${t}, 0.2)`}return Y` + + + + `}static get styles(){return[super.styles,Ps,h` + .actions { + overflow: visible; + display: block; + } + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-entity)); + --shape-color: rgba(var(--rgb-state-entity), 0.2); + } + mushroom-select-option-control { + flex: 1; + --mdc-theme-primary: rgb(var(--rgb-state-entity)); + } + `]}};ld=n([pt(od)],ld);const cd=`${Rs}-template-card`,dd=`${cd}-editor`;Ns({type:cd,name:"Mushroom Template",description:"Build your own mushroom card using templates"});const ud=["icon","icon_color","badge_color","badge_icon","primary","secondary","picture"];let hd=class extends Ds{constructor(){super(...arguments),this._templateResults={},this._unsubRenderTemplates=new Map}static async getConfigElement(){return await Promise.resolve().then((function(){return wu})),document.createElement(dd)}static async getStubConfig(t){return{type:`custom:${cd}`,primary:"Hello, {{user}}",secondary:"How are you?",icon:"mdi:home"}}getCardSize(){let t=1;if(!this._config)return t;return"vertical"===Es(this._config).layout&&(t+=1),t}getLayoutOptions(){var t;const e={grid_columns:2,grid_rows:1};if(!this._config)return e;const i=Es(this._config);return"vertical"===i.layout&&(e.grid_rows+=1),"horizontal"===i.layout&&(e.grid_columns=4),(null===(t=this._config)||void 0===t?void 0:t.multiline_secondary)&&(e.grid_rows=void 0),e}setConfig(t){ud.forEach((e=>{var i,o;(null===(i=this._config)||void 0===i?void 0:i[e])===t[e]&&(null===(o=this._config)||void 0===o?void 0:o.entity)==t.entity||this._tryDisconnectKey(e)})),this._config=Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)}connectedCallback(){super.connectedCallback(),this._tryConnect()}disconnectedCallback(){this._tryDisconnect()}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}isTemplate(t){var e;const i=null===(e=this._config)||void 0===e?void 0:e[t];return null==i?void 0:i.includes("{")}getValue(t){var e,i,o;return this.isTemplate(t)?null===(i=null===(e=this._templateResults[t])||void 0===e?void 0:e.result)||void 0===i?void 0:i.toString():null===(o=this._config)||void 0===o?void 0:o[t]}render(){if(!this._config||!this.hass)return K;const t=this.getValue("icon"),e=this.getValue("icon_color"),i=this.getValue("badge_icon"),o=this.getValue("badge_color"),n=this.getValue("primary"),r=this.getValue("secondary"),a=this.getValue("picture"),s=this._config.multiline_secondary,l=Ie(this.hass),c=Es({fill_container:this._config.fill_container,layout:this._config.layout,icon_type:Boolean(a)?"entity-picture":Boolean(t)?"icon":"none",primary_info:Boolean(n)?"name":"none",secondary_info:Boolean(r)?"state":"none"}),d=ml(t);return Y` + + + + ${a?this.renderPicture(a):d?Y`
    ${d}
    `:t?this.renderIcon(t,e):K} + ${(t||a)&&i?this.renderBadgeIcon(i,o):void 0} + +
    +
    +
    + `}renderPicture(t){return Y` + + `}renderIcon(t,e){const i={};if(e){const t=Za(e);i["--icon-color"]=`rgb(${t})`,i["--shape-color"]=`rgba(${t}, 0.2)`}return Y` + + + + `}renderBadgeIcon(t,e){const i={};if(e){const t=Za(e);i["--main-color"]=`rgba(${t})`}return Y` + + `}updated(t){super.updated(t),this._config&&this.hass&&this._tryConnect()}async _tryConnect(){ud.forEach((t=>{this._tryConnectKey(t)}))}async _tryConnectKey(t){var e,i;if(void 0===this._unsubRenderTemplates.get(t)&&this.hass&&this._config&&this.isTemplate(t))try{const i=Fe(this.hass.connection,(e=>{this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:e})}),{template:null!==(e=this._config[t])&&void 0!==e?e:"",entity_ids:this._config.entity_id,variables:{config:this._config,user:this.hass.user.name,entity:this._config.entity},strict:!0});this._unsubRenderTemplates.set(t,i),await i}catch(e){const o={result:null!==(i=this._config[t])&&void 0!==i?i:"",listeners:{all:!1,domains:[],entities:[],time:!1}};this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:o}),this._unsubRenderTemplates.delete(t)}}async _tryDisconnect(){ud.forEach((t=>{this._tryDisconnectKey(t)}))}async _tryDisconnectKey(t){const e=this._unsubRenderTemplates.get(t);if(e)try{(await e)(),this._unsubRenderTemplates.delete(t)}catch(t){if("not_found"!==t.code&&"template_error"!==t.code)throw t}}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-disabled)); + --shape-color: rgba(var(--rgb-disabled), 0.2); + } + svg { + width: var(--icon-size); + height: var(--icon-size); + display: flex; + } + ${ol} + `]}};n([vt()],hd.prototype,"_config",void 0),n([vt()],hd.prototype,"_templateResults",void 0),n([vt()],hd.prototype,"_unsubRenderTemplates",void 0),n([_t({reflect:!0,type:String})],hd.prototype,"layout",void 0),hd=n([pt(cd)],hd);const md=`${Rs}-title-card`,pd=`${md}-editor`;Ns({type:md,name:"Mushroom Title Card",description:"Title and subtitle to separate sections"});const fd=["title","subtitle"];let gd=class extends Ds{constructor(){super(...arguments),this._templateResults={},this._unsubRenderTemplates=new Map}static async getConfigElement(){return await Promise.resolve().then((function(){return pg})),document.createElement(pd)}static async getStubConfig(t){return{type:`custom:${md}`,title:"Hello, {{ user }} !"}}getCardSize(){return 1}setConfig(t){fd.forEach((e=>{var i;(null===(i=this._config)||void 0===i?void 0:i[e])!==t[e]&&this._tryDisconnectKey(e)})),this._config=Object.assign({title_tap_action:{action:"none"},subtitle_tap_action:{action:"none"}},t)}connectedCallback(){super.connectedCallback(),this._tryConnect()}disconnectedCallback(){this._tryDisconnect()}isTemplate(t){var e;const i=null===(e=this._config)||void 0===e?void 0:e[t];return null==i?void 0:i.includes("{")}getValue(t){var e,i,o;return this.isTemplate(t)?null===(i=null===(e=this._templateResults[t])||void 0===e?void 0:e.result)||void 0===i?void 0:i.toString():null===(o=this._config)||void 0===o?void 0:o[t]}_handleTitleAction(t){const e={tap_action:this._config.title_tap_action};Ke(this,this.hass,e,t.detail.action)}_handleSubtitleAction(t){const e={tap_action:this._config.subtitle_tap_action};Ke(this,this.hass,e,t.detail.action)}render(){if(!this._config||!this.hass)return K;const t=this.getValue("title"),e=this.getValue("subtitle");let i="";this._config.alignment&&(i=`align-${this._config.alignment}`);const o=Boolean(this._config.title_tap_action&&"none"!==this._config.title_tap_action.action),n=Boolean(this._config.subtitle_tap_action&&"none"!==this._config.subtitle_tap_action.action),r=Ie(this.hass);return Y` + + ${t?Y` +
    +

    ${t}${this.renderArrow()}

    +
    + `:K} + ${e?Y` +
    +

    ${e}${this.renderArrow()}

    +
    + `:K} +
    + `}renderArrow(){const t=Ie(this.hass);return Y` `}updated(t){super.updated(t),this._config&&this.hass&&this._tryConnect()}async _tryConnect(){fd.forEach((t=>{this._tryConnectKey(t)}))}async _tryConnectKey(t){var e,i;if(void 0===this._unsubRenderTemplates.get(t)&&this.hass&&this._config&&this.isTemplate(t))try{const i=Fe(this.hass.connection,(e=>{this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:e})}),{template:null!==(e=this._config[t])&&void 0!==e?e:"",entity_ids:this._config.entity_id,variables:{config:this._config,user:this.hass.user.name},strict:!0});this._unsubRenderTemplates.set(t,i),await i}catch(e){const o={result:null!==(i=this._config[t])&&void 0!==i?i:"",listeners:{all:!1,domains:[],entities:[],time:!1}};this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:o}),this._unsubRenderTemplates.delete(t)}}async _tryDisconnect(){fd.forEach((t=>{this._tryDisconnectKey(t)}))}async _tryDisconnectKey(t){const e=this._unsubRenderTemplates.get(t);if(e)try{(await e)(),this._unsubRenderTemplates.delete(t)}catch(t){if("not_found"!==t.code&&"template_error"!==t.code)throw t}}static get styles(){return[super.styles,Ps,h` + .header { + display: block; + padding: var(--title-padding); + background: none; + border: none; + box-shadow: none; + } + .header div * { + margin: 0; + white-space: pre-wrap; + } + .header div:not(:last-of-type) { + margin-bottom: var(--title-spacing); + } + .actionable { + cursor: pointer; + } + .header ha-icon { + display: none; + } + .actionable ha-icon { + display: inline-block; + margin-left: 4px; + transition: transform 180ms ease-in-out; + } + .actionable:hover ha-icon { + transform: translateX(4px); + } + [rtl] .actionable ha-icon { + margin-left: initial; + margin-right: 4px; + } + [rtl] .actionable:hover ha-icon { + transform: translateX(-4px); + } + .title { + color: var(--title-color); + font-size: var(--title-font-size); + font-weight: var(--title-font-weight); + line-height: var(--title-line-height); + letter-spacing: var(--title-letter-spacing); + --mdc-icon-size: var(--title-font-size); + } + .subtitle { + color: var(--subtitle-color); + font-size: var(--subtitle-font-size); + font-weight: var(--subtitle-font-weight); + line-height: var(--subtitle-line-height); + letter-spacing: var(--subtitle-letter-spacing); + --mdc-icon-size: var(--subtitle-font-size); + } + .align-start { + text-align: start; + } + .align-end { + text-align: end; + } + .align-center { + text-align: center; + } + .align-justify { + text-align: justify; + } + `]}};n([vt()],gd.prototype,"_config",void 0),n([vt()],gd.prototype,"_templateResults",void 0),n([vt()],gd.prototype,"_unsubRenderTemplates",void 0),gd=n([pt(md)],gd);const _d=`${Rs}-update-card`,vd=`${_d}-editor`,bd=["update"],yd={on:"var(--rgb-state-update-on)",off:"var(--rgb-state-update-off)",installing:"var(--rgb-state-update-installing)"};let xd=class extends ht{constructor(){super(...arguments),this.fill=!1}_handleInstall(){this.hass.callService("update","install",{entity_id:this.entity.entity_id})}_handleSkip(t){t.stopPropagation(),this.hass.callService("update","skip",{entity_id:this.entity.entity_id})}get installDisabled(){if(!Ut(this.entity))return!0;const t=this.entity.attributes.latest_version&&this.entity.attributes.skipped_version===this.entity.attributes.latest_version;return!Bt(this.entity)&&!t||Gt(this.entity)}get skipDisabled(){if(!Ut(this.entity))return!0;return this.entity.attributes.latest_version&&this.entity.attributes.skipped_version===this.entity.attributes.latest_version||!Bt(this.entity)||Gt(this.entity)}render(){const t=Ie(this.hass);return Y` + + + + + + + + + `}};n([_t({attribute:!1})],xd.prototype,"hass",void 0),n([_t({attribute:!1})],xd.prototype,"entity",void 0),n([_t({type:Boolean})],xd.prototype,"fill",void 0),xd=n([pt("mushroom-update-buttons-control")],xd),Ns({type:_d,name:"Mushroom Update Card",description:"Card for update entity"});let wd=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return yg})),document.createElement(vd)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>bd.includes(t.split(".")[0])));return{type:`custom:${_d}`,entity:e[0]}}get hasControls(){return!(!this._stateObj||!this._config)&&(Boolean(this._config.show_buttons_control)&&Wt(this._stateObj,1))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=Es(this._config),n=as(t,o.icon_type),r=Ie(this.hass),a=(!this._config.collapsible_controls||Bt(t))&&this._config.show_buttons_control&&Wt(t,1);return Y` + + + + ${n?this.renderPicture(n):this.renderIcon(t,i)} + ${this.renderBadge(t)} + ${this.renderStateInfo(t,o,e)}; + + ${a?Y` +
    + - - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-lock-buttons-control { - flex: 1; - } - `]}};Vc=n([pt(Mc)],Vc);const Bc=`${Nl}-media-player-card`,Uc=`${Bc}-editor`,Hc=["media_player"];function Yc(t){return null!=t.attributes.volume_level?100*t.attributes.volume_level:void 0}const Wc=(t,e)=>{if(!t)return[];const i=t.state;if("off"===i)return Wt(t,128)&&e.includes("on_off")?[{icon:"mdi:power",action:"turn_on"}]:[];const o=[];Wt(t,256)&&e.includes("on_off")&&o.push({icon:"mdi:power",action:"turn_off"});const n=!0===t.attributes.assumed_state,r=t.attributes;return("playing"===i||"paused"===i||n)&&Wt(t,32768)&&e.includes("shuffle")&&o.push({icon:!0===r.shuffle?"mdi:shuffle":"mdi:shuffle-disabled",action:"shuffle_set"}),("playing"===i||"paused"===i||n)&&Wt(t,16)&&e.includes("previous")&&o.push({icon:"mdi:skip-previous",action:"media_previous_track"}),!n&&("playing"===i&&(Wt(t,1)||Wt(t,4096))||("paused"===i||"idle"===i)&&Wt(t,Pe)||"on"===i&&(Wt(t,Pe)||Wt(t,1)))&&e.includes("play_pause_stop")&&o.push({icon:"on"===i?"mdi:play-pause":"playing"!==i?"mdi:play":Wt(t,1)?"mdi:pause":"mdi:stop",action:"playing"!==i?"media_play":Wt(t,1)?"media_pause":"media_stop"}),n&&Wt(t,Pe)&&e.includes("play_pause_stop")&&o.push({icon:"mdi:play",action:"media_play"}),n&&Wt(t,1)&&e.includes("play_pause_stop")&&o.push({icon:"mdi:pause",action:"media_pause"}),n&&Wt(t,4096)&&e.includes("play_pause_stop")&&o.push({icon:"mdi:stop",action:"media_stop"}),("playing"===i||"paused"===i||n)&&Wt(t,32)&&e.includes("next")&&o.push({icon:"mdi:skip-next",action:"media_next_track"}),("playing"===i||"paused"===i||n)&&Wt(t,262144)&&e.includes("repeat")&&o.push({icon:"all"===r.repeat?"mdi:repeat":"one"===r.repeat?"mdi:repeat-once":"mdi:repeat-off",action:"repeat_set"}),o.length>0?o:[]},Xc=(t,e,i)=>{let o={};"shuffle_set"===i?o={shuffle:!e.attributes.shuffle}:"repeat_set"===i?o={repeat:"all"===e.attributes.repeat?"one":"off"===e.attributes.repeat?"all":"off"}:"volume_mute"===i&&(o={is_volume_muted:!e.attributes.is_volume_muted}),t.callService("media_player",i,Object.assign({entity_id:e.entity_id},o))};let Kc=class extends ht{constructor(){super(...arguments),this.fill=!1}_handleClick(t){t.stopPropagation();const e=t.target.action;Xc(this.hass,this.entity,e)}render(){const t=Ie(this.hass),e=Wc(this.entity,this.controls);return Y` - - ${e.map((t=>Y` - - - - `))} - - `}};n([_t({attribute:!1})],Kc.prototype,"hass",void 0),n([_t({attribute:!1})],Kc.prototype,"entity",void 0),n([_t({attribute:!1})],Kc.prototype,"controls",void 0),n([_t({type:Boolean})],Kc.prototype,"fill",void 0),Kc=n([pt("mushroom-media-player-media-control")],Kc);let Gc=class extends ht{constructor(){super(...arguments),this.fill=!1}handleSliderChange(t){const e=t.detail.value;this.hass.callService("media_player","volume_set",{entity_id:this.entity.entity_id,volume_level:e/100})}handleSliderCurrentChange(t){let e=t.detail.value;this.dispatchEvent(new CustomEvent("current-change",{detail:{value:e}}))}handleClick(t){t.stopPropagation();const e=t.target.action;Xc(this.hass,this.entity,e)}render(){var t,e,i;if(!this.entity)return K;const o=Yc(this.entity),n=Ie(this.hass),r=(null===(t=this.controls)||void 0===t?void 0:t.includes("volume_set"))&&Wt(this.entity,4),a=(null===(e=this.controls)||void 0===e?void 0:e.includes("volume_mute"))&&Wt(this.entity,8),l=(null===(i=this.controls)||void 0===i?void 0:i.includes("volume_buttons"))&&Wt(this.entity,1024);return Y` - - ${r?Y` `:K} - ${a?Y` - - - - `:void 0} - ${l?Y` - - - `:void 0} - ${l?Y` - - - `:void 0} - - `}static get styles(){return h` - mushroom-slider { - flex: 1; - --main-color: rgb(var(--rgb-state-media-player)); - --bg-color: rgba(var(--rgb-state-media-player), 0.2); - } - `}};n([_t({attribute:!1})],Gc.prototype,"hass",void 0),n([_t({attribute:!1})],Gc.prototype,"entity",void 0),n([_t({type:Boolean})],Gc.prototype,"fill",void 0),n([_t({attribute:!1})],Gc.prototype,"controls",void 0),Gc=n([pt("mushroom-media-player-volume-control")],Gc);const qc={media_control:"mdi:play-pause",volume_control:"mdi:volume-high"};Pl({type:Bc,name:"Mushroom Media Card",description:"Card for media player entity"});let Zc=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return Bf})),document.createElement(Uc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Hc.includes(t.split(".")[0])));return{type:`custom:${Bc}`,entity:e[0]}}get hasControls(){var t,e,i,o;return Boolean(null===(e=null===(t=this._config)||void 0===t?void 0:t.media_controls)||void 0===e?void 0:e.length)||Boolean(null===(o=null===(i=this._config)||void 0===i?void 0:i.volume_controls)||void 0===o?void 0:o.length)}get _controls(){if(!this._config||!this._stateObj)return[];const t=this._stateObj,e=[];return((t,e)=>Wc(t,null!=e?e:[]).length>0)(t,this._config.media_controls)&&e.push("media_control"),((t,e)=>(null==e?void 0:e.includes("volume_buttons"))&&Wt(t,1024)||(null==e?void 0:e.includes("volume_mute"))&&Wt(t,8)||(null==e?void 0:e.includes("volume_set"))&&Wt(t,4))(t,this._config.volume_controls)&&e.push("volume_control"),e}_onControlTap(t,e){e.stopPropagation(),this._activeControl=t}setConfig(t){super.setConfig(t),this.updateActiveControl(),this.updateVolume()}updated(t){super.updated(t),this.hass&&t.has("hass")&&(this.updateActiveControl(),this.updateVolume())}updateVolume(){this.volume=void 0;const t=this._stateObj;if(!t)return;const e=Yc(t);this.volume=null!=e?Math.round(e):e}onCurrentVolumeChange(t){null!=t.detail.value&&(this.volume=t.detail.value)}updateActiveControl(){const t=!!this._activeControl&&this._controls.includes(this._activeControl);this._activeControl=t?this._activeControl:this._controls[0]}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=function(t,e){var i,o=t.icon;if(![Nt,Rt,Ft].includes(e.state)&&t.use_media_info)switch(null===(i=e.attributes.app_name)||void 0===i?void 0:i.toLowerCase()){case"spotify":return"mdi:spotify";case"google podcasts":return"mdi:google-podcast";case"plex":return"mdi:plex";case"soundcloud":return"mdi:soundcloud";case"youtube":return"mdi:youtube";case"oto music":return"mdi:music-circle";case"netflix":return"mdi:netflix";default:return}return o}(this._config,t),i=function(t,e){let i=t.name||e.attributes.friendly_name||"";return![Nt,Rt,Ft].includes(e.state)&&t.use_media_info&&e.attributes.media_title&&(i=e.attributes.media_title),i}(this._config,t),o=function(t,e,i){let o=i.formatEntityState?i.formatEntityState(e):ne(i.localize,e,i.locale,i.config,i.entities);return![Nt,Rt,Ft].includes(e.state)&&t.use_media_info&&(t=>{let e;switch(t.attributes.media_content_type){case"music":case"image":e=t.attributes.media_artist;break;case"playlist":e=t.attributes.media_playlist;break;case"tvshow":e=t.attributes.media_series_title,t.attributes.media_season&&(e+=" S"+t.attributes.media_season,t.attributes.media_episode&&(e+="E"+t.attributes.media_episode));break;default:e=t.attributes.app_name||""}return e})(e)||o}(this._config,t,this.hass),n=$l(this._config),r=rl(t,n.icon_type),a=null!=this.volume&&this._config.show_volume_level?`${o} - ${this.volume}${oe(this.hass.locale)}%`:o,l=Ie(this.hass),s=(!this._config.collapsible_controls||Bt(t))&&this._controls.length;return Y` - +
    + `:K} +
    +
    + `}renderIcon(t,e){const i=Gt(t),o=function(t,e){return e?yd.installing:yd[t]||"var(--rgb-grey)"}(t.state,i),n={"--icon-color":`rgb(${o})`,"--shape-color":`rgba(${o}, 0.2)`};return Y` + + + + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-entity)); + --shape-color: rgba(var(--rgb-state-entity), 0.2); + } + mushroom-shape-icon.pulse { + --shape-animation: 1s ease 0s infinite normal none running pulse; + } + mushroom-update-buttons-control { + flex: 1; + } + `]}};wd=n([pt(_d)],wd);const kd=`${Rs}-vacuum-card`,Cd=`${kd}-editor`,$d=["vacuum"];function Ed(t){switch(t.state){case"cleaning":case"on":return!0;default:return!1}}function Ad(t){return t.state===Ne}const Sd=(t,e,i)=>Id(t,e,i)&&(!e.isVisible||e.isVisible(t)),Id=(t,e,i)=>e.isSupported(t)&&i.includes(e.command),Td=[{icon:"mdi:power",serviceName:"turn_on",command:"on_off",isSupported:t=>Wt(t,1),isVisible:t=>!Bt(t),isDisabled:()=>!1},{icon:"mdi:power",serviceName:"turn_off",command:"on_off",isSupported:t=>Wt(t,2),isVisible:t=>Bt(t),isDisabled:()=>!1},{icon:"mdi:play",serviceName:"start",command:"start_pause",isSupported:t=>Wt(t,Re),isVisible:t=>!Ed(t),isDisabled:()=>!1},{icon:"mdi:pause",serviceName:"pause",command:"start_pause",isSupported:t=>Wt(t,Re)&&Wt(t,4),isVisible:t=>Ed(t),isDisabled:()=>!1},{icon:"mdi:play-pause",serviceName:"start_pause",command:"start_pause",isSupported:t=>!Wt(t,Re)&&Wt(t,4),isDisabled:()=>!1},{icon:"mdi:stop",serviceName:"stop",command:"stop",isSupported:t=>Wt(t,8),isDisabled:t=>function(t){switch(t.state){case"docked":case"off":case"idle":case Ne:return!0;default:return!1}}(t)},{icon:"mdi:target-variant",serviceName:"clean_spot",command:"clean_spot",isSupported:t=>Wt(t,1024),isDisabled:()=>!1},{icon:"mdi:map-marker",serviceName:"locate",command:"locate",isSupported:t=>Wt(t,512),isDisabled:t=>Ad(t)},{icon:"mdi:home-map-marker",serviceName:"return_to_base",command:"return_home",isSupported:t=>Wt(t,16),isDisabled:()=>!1}];let zd=class extends ht{constructor(){super(...arguments),this.fill=!1}callService(t){t.stopPropagation();const e=t.target.entry;this.hass.callService("vacuum",e.serviceName,{entity_id:this.entity.entity_id})}render(){const t=Ie(this.hass);return Y` + + ${Td.filter((t=>Sd(this.entity,t,this.commands))).map((t=>Y` + - - - ${r?this.renderPicture(r):this.renderIcon(t,e)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,n,i,a)}; - - ${s?Y` -
    - ${this.renderActiveControl(t,n.layout)} - ${this.renderOtherControls()} -
    - `:K} -
    - - `}renderOtherControls(){const t=this._controls.filter((t=>t!=this._activeControl));return Y` - ${t.map((t=>Y` - this._onControlTap(t,e)}> - - - `))} - `}renderActiveControl(t,e){var i,o,n,r;const a=null!==(o=null===(i=this._config)||void 0===i?void 0:i.media_controls)&&void 0!==o?o:[],l=null!==(r=null===(n=this._config)||void 0===n?void 0:n.volume_controls)&&void 0!==r?r:[];switch(this._activeControl){case"media_control":return Y` - - - `;case"volume_control":return Y` - - `;default:return K}}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-media-player)); - --shape-color: rgba(var(--rgb-state-media-player), 0.2); - } - mushroom-media-player-media-control, - mushroom-media-player-volume-control { - flex: 1; - } - `]}};n([vt()],Zc.prototype,"_activeControl",void 0),n([vt()],Zc.prototype,"volume",void 0),Zc=n([pt(Bc)],Zc);const Jc=`${Nl}-person-card`,Qc=`${Jc}-editor`,td=["person","device_tracker"];Pl({type:Jc,name:"Mushroom Person Card",description:"Card for person entity"});let ed=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return Wf})),document.createElement(Qc)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>td.includes(t.split(".")[0])));return{type:`custom:${Jc}`,entity:e[0]}}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type),r=Ie(this.hass);return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e)}; - - - - `}renderStateBadge(t){const e=Object.values(this.hass.states).filter((t=>t.entity_id.startsWith("zone."))),i=function(t,e){const i=t.state;if(i===Rt)return"mdi:help";if("not_home"===i)return"mdi:home-export-outline";if("home"===i)return"mdi:home";const o=e.find((t=>i===t.attributes.friendly_name));return o&&o.attributes.icon?o.attributes.icon:"mdi:home"}(t,e),o=function(t,e){const i=t.state;if(i===Rt)return"var(--rgb-state-person-unknown)";if("not_home"===i)return"var(--rgb-state-person-not-home)";if("home"===i)return"var(--rgb-state-person-home)";const o=e.some((t=>i===t.attributes.friendly_name));return o?"var(--rgb-state-person-zone)":"var(--rgb-state-person-home)"}(t,e);return Y` - - `}renderBadge(t){return!Ut(t)?super.renderBadge(t):this.renderStateBadge(t)}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - `]}};ed=n([pt(Jc)],ed);const id=`${Nl}-select-card`,od=`${id}-editor`,nd=["input_select","select"];function rd(t){return null!=t.state?t.state:void 0}let ad=class extends ht{_selectChanged(t){const e=t.target.value,i=rd(this.entity);e&&e!==i&&this._setValue(e)}_setValue(t){const e=this.entity.entity_id.split(".")[0];this.hass.callService(e,"select_option",{entity_id:this.entity.entity_id,option:t})}render(){const t=rd(this.entity),e=this.entity.attributes.options;return Y` - t.stopPropagation()} - .value=${null!=t?t:""} - naturalMenuWidth - fixedMenuPosition - > - ${e.map((t=>Y` - - ${this.hass.formatEntityState?this.hass.formatEntityState(this.entity,t):ne(this.hass.localize,this.entity,this.hass.locale,this.hass.config,this.hass.entities,t)} - - `))} - - `}static get styles(){return h` - mushroom-select { - --select-height: 42px; - width: 100%; - } - `}};n([_t()],ad.prototype,"hass",void 0),n([_t({attribute:!1})],ad.prototype,"entity",void 0),ad=n([pt("mushroom-select-option-control")],ad),Pl({type:id,name:"Mushroom Select Card",description:"Card for select and input_select entities"});let ld=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return qf})),document.createElement(od)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>nd.includes(t.split(".")[0])));return{type:`custom:${id}`,entity:e[0]}}get hasControls(){return!0}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t;if(!this._config||!this.hass||!this._config.entity)return K;const e=this._stateObj;if(!e)return this.renderNotFound(this._config);const i=this._config.name||e.attributes.friendly_name||"",o=this._config.icon,n=$l(this._config),r=rl(e,n.icon_type),a=Ie(this.hass),l=null===(t=this._config)||void 0===t?void 0:t.icon_color,s={};if(l){const t=qa(l);s["--mdc-theme-primary"]=`rgb(${t})`}return Y` - - - - ${r?this.renderPicture(r):this.renderIcon(e,o)} - ${this.renderBadge(e)} - ${this.renderStateInfo(e,n,i)}; - -
    - -
    -
    -
    - `}renderIcon(t,e){var i;const o=Bt(t),n={},r=null===(i=this._config)||void 0===i?void 0:i.icon_color;if(r){const t=qa(r);n["--icon-color"]=`rgb(${t})`,n["--shape-color"]=`rgba(${t}, 0.2)`}return Y` - - +
    + `))} +
    + `}};n([_t({attribute:!1})],zd.prototype,"hass",void 0),n([_t({attribute:!1})],zd.prototype,"entity",void 0),n([_t({attribute:!1})],zd.prototype,"commands",void 0),n([_t({type:Boolean})],zd.prototype,"fill",void 0),zd=n([pt("mushroom-vacuum-commands-control")],zd),Ns({type:kd,name:"Mushroom Vacuum Card",description:"Card for vacuum entity"});let Od=class extends Ls{static async getConfigElement(){return await Promise.resolve().then((function(){return Eg})),document.createElement(Cd)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>$d.includes(t.split(".")[0])));return{type:`custom:${kd}`,entity:e[0]}}get hasControls(){var t,e,i;return!(!this._stateObj||!this._config)&&(e=this._stateObj,i=null!==(t=this._config.commands)&&void 0!==t?t:[],Td.some((t=>Id(e,t,i))))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t,e;if(!this._config||!this.hass||!this._config.entity)return K;const i=this._stateObj;if(!i)return this.renderNotFound(this._config);const o=this._config.name||i.attributes.friendly_name||"",n=this._config.icon,r=Es(this._config),a=as(i,r.icon_type),s=Ie(this.hass),l=null!==(e=null===(t=this._config)||void 0===t?void 0:t.commands)&&void 0!==e?e:[];return Y` + + + + ${a?this.renderPicture(a):this.renderIcon(i,n)} + ${this.renderBadge(i)} + ${this.renderStateInfo(i,r,o)}; + + ${((t,e)=>Td.some((i=>Sd(t,i,e))))(i,l)?Y` +
    + - - `}static get styles(){return[super.styles,Ll,h` - .actions { - overflow: visible; - display: block; - } - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-entity)); - --shape-color: rgba(var(--rgb-state-entity), 0.2); - } - mushroom-select-option-control { - flex: 1; - --mdc-theme-primary: rgb(var(--rgb-state-entity)); - } - `]}};ld=n([pt(id)],ld);const sd=`${Nl}-template-card`,cd=`${sd}-editor`;Pl({type:sd,name:"Mushroom Template Card",description:"Card for custom rendering with templates"});const dd=["icon","icon_color","badge_color","badge_icon","primary","secondary","picture"];let ud=class extends jl{constructor(){super(...arguments),this._templateResults={},this._unsubRenderTemplates=new Map,this._inGrid=!1}static async getConfigElement(){return await Promise.resolve().then((function(){return mu})),document.createElement(cd)}static async getStubConfig(t){return{type:`custom:${sd}`,primary:"Hello, {{user}}",secondary:"How are you?",icon:"mdi:home"}}getCardSize(){let t=1;if(!this._config)return t;return"vertical"===$l(this._config).layout&&(t+=1),t}getGridSize(){const{grid_columns:t,grid_rows:e}=this.getLayoutOptions();return[t,e]}getLayoutOptions(){var t;this._inGrid=!0;const e={grid_columns:2,grid_rows:1};if(!this._config)return e;const i=$l(this._config);return"vertical"===i.layout&&(e.grid_rows+=1),"horizontal"===i.layout&&(e.grid_columns=4),(null===(t=this._config)||void 0===t?void 0:t.multiline_secondary)&&(e.grid_rows=void 0),e}setConfig(t){dd.forEach((e=>{var i,o;(null===(i=this._config)||void 0===i?void 0:i[e])===t[e]&&(null===(o=this._config)||void 0===o?void 0:o.entity)==t.entity||this._tryDisconnectKey(e)})),this._config=Object.assign({tap_action:{action:"toggle"},hold_action:{action:"more-info"}},t)}connectedCallback(){super.connectedCallback(),this._tryConnect()}disconnectedCallback(){this._tryDisconnect()}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}isTemplate(t){var e;const i=null===(e=this._config)||void 0===e?void 0:e[t];return null==i?void 0:i.includes("{")}getValue(t){var e,i,o;return this.isTemplate(t)?null===(i=null===(e=this._templateResults[t])||void 0===e?void 0:e.result)||void 0===i?void 0:i.toString():null===(o=this._config)||void 0===o?void 0:o[t]}render(){if(!this._config||!this.hass)return K;const t=this.getValue("icon"),e=this.getValue("icon_color"),i=this.getValue("badge_icon"),o=this.getValue("badge_color"),n=this.getValue("primary"),r=this.getValue("secondary"),a=this.getValue("picture"),l=this._config.multiline_secondary,s=Ie(this.hass),c=$l({fill_container:this._config.fill_container,layout:this._config.layout,icon_type:Boolean(a)?"entity-picture":Boolean(t)?"icon":"none",primary_info:Boolean(n)?"name":"none",secondary_info:Boolean(r)?"state":"none"}),d=hs(t);return Y` - - - - ${a?this.renderPicture(a):d?Y`
    ${d}
    `:t?this.renderIcon(t,e):K} - ${(t||a)&&i?this.renderBadgeIcon(i,o):void 0} - -
    -
    -
    - `}renderPicture(t){return Y` - - `}renderIcon(t,e){const i={};if(e){const t=qa(e);i["--icon-color"]=`rgb(${t})`,i["--shape-color"]=`rgba(${t}, 0.2)`}return Y` - - - - `}renderBadgeIcon(t,e){const i={};if(e){const t=qa(e);i["--main-color"]=`rgba(${t})`}return Y` - - `}updated(t){super.updated(t),this._config&&this.hass&&this._tryConnect()}async _tryConnect(){dd.forEach((t=>{this._tryConnectKey(t)}))}async _tryConnectKey(t){var e,i;if(void 0===this._unsubRenderTemplates.get(t)&&this.hass&&this._config&&this.isTemplate(t))try{const i=Fe(this.hass.connection,(e=>{this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:e})}),{template:null!==(e=this._config[t])&&void 0!==e?e:"",entity_ids:this._config.entity_id,variables:{config:this._config,user:this.hass.user.name,entity:this._config.entity},strict:!0});this._unsubRenderTemplates.set(t,i),await i}catch(e){const o={result:null!==(i=this._config[t])&&void 0!==i?i:"",listeners:{all:!1,domains:[],entities:[],time:!1}};this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:o}),this._unsubRenderTemplates.delete(t)}}async _tryDisconnect(){dd.forEach((t=>{this._tryDisconnectKey(t)}))}async _tryDisconnectKey(t){const e=this._unsubRenderTemplates.get(t);if(e)try{(await e)(),this._unsubRenderTemplates.delete(t)}catch(t){if("not_found"!==t.code&&"template_error"!==t.code)throw t}}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-disabled)); - --shape-color: rgba(var(--rgb-disabled), 0.2); - } - svg { - width: var(--icon-size); - height: var(--icon-size); - display: flex; - } - ${is} - `]}};n([vt()],ud.prototype,"_config",void 0),n([vt()],ud.prototype,"_templateResults",void 0),n([vt()],ud.prototype,"_unsubRenderTemplates",void 0),n([_t({attribute:"in-grid",reflect:!0,type:Boolean})],ud.prototype,"_inGrid",void 0),ud=n([pt(sd)],ud);const hd=`${Nl}-title-card`,md=`${hd}-editor`;Pl({type:hd,name:"Mushroom Title Card",description:"Title and subtitle to separate sections"});const pd=["title","subtitle"];let fd=class extends jl{constructor(){super(...arguments),this._templateResults={},this._unsubRenderTemplates=new Map}static async getConfigElement(){return await Promise.resolve().then((function(){return ig})),document.createElement(md)}static async getStubConfig(t){return{type:`custom:${hd}`,title:"Hello, {{ user }} !"}}getCardSize(){return 1}setConfig(t){pd.forEach((e=>{var i;(null===(i=this._config)||void 0===i?void 0:i[e])!==t[e]&&this._tryDisconnectKey(e)})),this._config=Object.assign({title_tap_action:{action:"none"},subtitle_tap_action:{action:"none"}},t)}connectedCallback(){super.connectedCallback(),this._tryConnect()}disconnectedCallback(){this._tryDisconnect()}isTemplate(t){var e;const i=null===(e=this._config)||void 0===e?void 0:e[t];return null==i?void 0:i.includes("{")}getValue(t){var e,i,o;return this.isTemplate(t)?null===(i=null===(e=this._templateResults[t])||void 0===e?void 0:e.result)||void 0===i?void 0:i.toString():null===(o=this._config)||void 0===o?void 0:o[t]}_handleTitleAction(t){const e={tap_action:this._config.title_tap_action};Ke(this,this.hass,e,t.detail.action)}_handleSubtitleAction(t){const e={tap_action:this._config.subtitle_tap_action};Ke(this,this.hass,e,t.detail.action)}render(){if(!this._config||!this.hass)return K;const t=this.getValue("title"),e=this.getValue("subtitle");let i="";this._config.alignment&&(i=`align-${this._config.alignment}`);const o=Boolean(this._config.title_tap_action&&"none"!==this._config.title_tap_action.action),n=Boolean(this._config.subtitle_tap_action&&"none"!==this._config.subtitle_tap_action.action),r=Ie(this.hass);return Y` - - ${t?Y` -
    -

    ${t}${this.renderArrow()}

    -
    - `:K} - ${e?Y` -
    -

    ${e}${this.renderArrow()}

    -
    - `:K} -
    - `}renderArrow(){const t=Ie(this.hass);return Y` `}updated(t){super.updated(t),this._config&&this.hass&&this._tryConnect()}async _tryConnect(){pd.forEach((t=>{this._tryConnectKey(t)}))}async _tryConnectKey(t){var e,i;if(void 0===this._unsubRenderTemplates.get(t)&&this.hass&&this._config&&this.isTemplate(t))try{const i=Fe(this.hass.connection,(e=>{this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:e})}),{template:null!==(e=this._config[t])&&void 0!==e?e:"",entity_ids:this._config.entity_id,variables:{config:this._config,user:this.hass.user.name},strict:!0});this._unsubRenderTemplates.set(t,i),await i}catch(e){const o={result:null!==(i=this._config[t])&&void 0!==i?i:"",listeners:{all:!1,domains:[],entities:[],time:!1}};this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:o}),this._unsubRenderTemplates.delete(t)}}async _tryDisconnect(){pd.forEach((t=>{this._tryDisconnectKey(t)}))}async _tryDisconnectKey(t){const e=this._unsubRenderTemplates.get(t);if(e)try{(await e)(),this._unsubRenderTemplates.delete(t)}catch(t){if("not_found"!==t.code&&"template_error"!==t.code)throw t}}static get styles(){return[super.styles,Ll,h` - .header { - display: block; - padding: var(--title-padding); - background: none; - border: none; - box-shadow: none; - } - .header div * { - margin: 0; - white-space: pre-wrap; - } - .header div:not(:last-of-type) { - margin-bottom: var(--title-spacing); - } - .actionable { - cursor: pointer; - } - .header ha-icon { - display: none; - } - .actionable ha-icon { - display: inline-block; - margin-left: 4px; - transition: transform 180ms ease-in-out; - } - .actionable:hover ha-icon { - transform: translateX(4px); - } - [rtl] .actionable ha-icon { - margin-left: initial; - margin-right: 4px; - } - [rtl] .actionable:hover ha-icon { - transform: translateX(-4px); - } - .title { - color: var(--title-color); - font-size: var(--title-font-size); - font-weight: var(--title-font-weight); - line-height: var(--title-line-height); - letter-spacing: var(--title-letter-spacing); - --mdc-icon-size: var(--title-font-size); - } - .subtitle { - color: var(--subtitle-color); - font-size: var(--subtitle-font-size); - font-weight: var(--subtitle-font-weight); - line-height: var(--subtitle-line-height); - letter-spacing: var(--subtitle-letter-spacing); - --mdc-icon-size: var(--subtitle-font-size); - } - .align-start { - text-align: start; - } - .align-end { - text-align: end; - } - .align-center { - text-align: center; - } - .align-justify { - text-align: justify; - } - `]}};n([vt()],fd.prototype,"_config",void 0),n([vt()],fd.prototype,"_templateResults",void 0),n([vt()],fd.prototype,"_unsubRenderTemplates",void 0),fd=n([pt(hd)],fd);const gd=`${Nl}-update-card`,_d=`${gd}-editor`,vd=["update"],bd={on:"var(--rgb-state-update-on)",off:"var(--rgb-state-update-off)",installing:"var(--rgb-state-update-installing)"};let yd=class extends ht{constructor(){super(...arguments),this.fill=!1}_handleInstall(){this.hass.callService("update","install",{entity_id:this.entity.entity_id})}_handleSkip(t){t.stopPropagation(),this.hass.callService("update","skip",{entity_id:this.entity.entity_id})}get installDisabled(){if(!Ut(this.entity))return!0;const t=this.entity.attributes.latest_version&&this.entity.attributes.skipped_version===this.entity.attributes.latest_version;return!Bt(this.entity)&&!t||Gt(this.entity)}get skipDisabled(){if(!Ut(this.entity))return!0;return this.entity.attributes.latest_version&&this.entity.attributes.skipped_version===this.entity.attributes.latest_version||!Bt(this.entity)||Gt(this.entity)}render(){const t=Ie(this.hass);return Y` - - - - - - - - - `}};n([_t({attribute:!1})],yd.prototype,"hass",void 0),n([_t({attribute:!1})],yd.prototype,"entity",void 0),n([_t({type:Boolean})],yd.prototype,"fill",void 0),yd=n([pt("mushroom-update-buttons-control")],yd),Pl({type:gd,name:"Mushroom Update Card",description:"Card for update entity"});let xd=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return lg})),document.createElement(_d)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>vd.includes(t.split(".")[0])));return{type:`custom:${gd}`,entity:e[0]}}get hasControls(){return!(!this._stateObj||!this._config)&&(Boolean(this._config.show_buttons_control)&&Wt(this._stateObj,1))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){if(!this._config||!this.hass||!this._config.entity)return K;const t=this._stateObj;if(!t)return this.renderNotFound(this._config);const e=this._config.name||t.attributes.friendly_name||"",i=this._config.icon,o=$l(this._config),n=rl(t,o.icon_type),r=Ie(this.hass),a=(!this._config.collapsible_controls||Bt(t))&&this._config.show_buttons_control&&Wt(t,1);return Y` - - - - ${n?this.renderPicture(n):this.renderIcon(t,i)} - ${this.renderBadge(t)} - ${this.renderStateInfo(t,o,e)}; - - ${a?Y` -
    - -
    - `:K} -
    -
    - `}renderIcon(t,e){const i=Gt(t),o=function(t,e){return e?bd.installing:bd[t]||"var(--rgb-grey)"}(t.state,i),n={"--icon-color":`rgb(${o})`,"--shape-color":`rgba(${o}, 0.2)`};return Y` - - +
    +
    + `:K} +
    +
    + `}renderIcon(t,e){var i,o;return Y` + + + `}static get styles(){return[super.styles,Ps,h` + mushroom-state-item { + cursor: pointer; + } + mushroom-shape-icon { + --icon-color: rgb(var(--rgb-state-vacuum)); + --shape-color: rgba(var(--rgb-state-vacuum), 0.2); + } + .cleaning ha-state-icon { + animation: 5s infinite linear cleaning; + } + .cleaning ha-state-icon { + animation: 2s infinite linear returning; + } + mushroom-vacuum-commands-control { + flex: 1; + } + `]}};Od=n([pt(kd)],Od);const Md=new Set(["primary","accent","disabled","red","pink","purple","deep-purple","indigo","blue","light-blue","cyan","teal","green","light-green","lime","yellow","amber","orange","deep-orange","brown","light-grey","grey","dark-grey","blue-grey","black","white"]);const jd=`${Rs}-template-badge`,Dd=`${jd}-editor`;!function(e){const i=window;i.customBadges=i.customBadges||[];const o=e.type.replace("-badge","").replace("mushroom-","");i.customBadges.push(Object.assign(Object.assign({},e),{preview:!0,documentationURL:`${t}/blob/main/docs/badges/${o}.md`}))}({type:jd,name:"Mushroom Template",description:"Build your own badge using templates"});const Ld=["icon","color","label","content","picture"];let Pd=class extends ht{constructor(){super(...arguments),this._templateResults={},this._unsubRenderTemplates=new Map}static async getConfigElement(){return await Promise.resolve().then((function(){return zg})),document.createElement(Dd)}static async getStubConfig(t){return{type:`custom:${jd}`,content:"Hello",icon:"mdi:mushroom",color:"red"}}connectedCallback(){super.connectedCallback(),this._tryConnect()}disconnectedCallback(){this._tryDisconnect()}updated(t){super.updated(t),this._config&&this.hass&&this._tryConnect()}async _tryConnect(){Ld.forEach((t=>{this._tryConnectKey(t)}))}async _tryConnectKey(t){var e,i;if(void 0===this._unsubRenderTemplates.get(t)&&this.hass&&this._config&&this.isTemplate(t))try{const i=Fe(this.hass.connection,(e=>{this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:e})}),{template:null!==(e=this._config[t])&&void 0!==e?e:"",entity_ids:this._config.entity_id,variables:{config:this._config,user:this.hass.user.name,entity:this._config.entity},strict:!0});this._unsubRenderTemplates.set(t,i),await i}catch(e){const o={result:null!==(i=this._config[t])&&void 0!==i?i:"",listeners:{all:!1,domains:[],entities:[],time:!1}};this._templateResults=Object.assign(Object.assign({},this._templateResults),{[t]:o}),this._unsubRenderTemplates.delete(t)}}async _tryDisconnect(){Ld.forEach((t=>{this._tryDisconnectKey(t)}))}async _tryDisconnectKey(t){const e=this._unsubRenderTemplates.get(t);if(e)try{(await e)(),this._unsubRenderTemplates.delete(t)}catch(t){if("not_found"!==t.code&&"template_error"!==t.code)throw t}}setConfig(t){Ld.forEach((e=>{var i,o;(null===(i=this._config)||void 0===i?void 0:i[e])===t[e]&&(null===(o=this._config)||void 0===o?void 0:o.entity)==t.entity||this._tryDisconnectKey(e)})),this._config=Object.assign({tap_action:{action:"none"}},t)}get hasAction(){var t,e,i,o;return!(null===(t=this._config)||void 0===t?void 0:t.tap_action)||Ge(null===(e=this._config)||void 0===e?void 0:e.tap_action)||Ge(null===(i=this._config)||void 0===i?void 0:i.hold_action)||Ge(null===(o=this._config)||void 0===o?void 0:o.double_tap_action)}render(){var t,e;if(!this._config||!this.hass)return K;const i=this.getValue("icon"),o=this.getValue("color"),n=this.getValue("content"),r=this.getValue("label"),a=this.getValue("picture"),s=!!n,l=!!i||!!a,c={};o&&(c["--badge-color"]=function(t){return Md.has(t)?`var(--${t}-color)`:t}(o));const d=ml(i);return Y` +
    + + ${a?Y``:d||(i?Y` + - - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-entity)); - --shape-color: rgba(var(--rgb-state-entity), 0.2); - } - mushroom-shape-icon.pulse { - --shape-animation: 1s ease 0s infinite normal none running pulse; - } - mushroom-update-buttons-control { - flex: 1; - } - `]}};xd=n([pt(gd)],xd);const wd=`${Nl}-vacuum-card`,kd=`${wd}-editor`,Cd=["vacuum"];function $d(t){switch(t.state){case"cleaning":case"on":return!0;default:return!1}}function Ed(t){return t.state===Ne}const Ad=(t,e,i)=>Sd(t,e,i)&&(!e.isVisible||e.isVisible(t)),Sd=(t,e,i)=>e.isSupported(t)&&i.includes(e.command),Id=[{icon:"mdi:power",serviceName:"turn_on",command:"on_off",isSupported:t=>Wt(t,1),isVisible:t=>!Bt(t),isDisabled:()=>!1},{icon:"mdi:power",serviceName:"turn_off",command:"on_off",isSupported:t=>Wt(t,2),isVisible:t=>Bt(t),isDisabled:()=>!1},{icon:"mdi:play",serviceName:"start",command:"start_pause",isSupported:t=>Wt(t,Re),isVisible:t=>!$d(t),isDisabled:()=>!1},{icon:"mdi:pause",serviceName:"pause",command:"start_pause",isSupported:t=>Wt(t,Re)&&Wt(t,4),isVisible:t=>$d(t),isDisabled:()=>!1},{icon:"mdi:play-pause",serviceName:"start_pause",command:"start_pause",isSupported:t=>!Wt(t,Re)&&Wt(t,4),isDisabled:()=>!1},{icon:"mdi:stop",serviceName:"stop",command:"stop",isSupported:t=>Wt(t,8),isDisabled:t=>function(t){switch(t.state){case"docked":case"off":case"idle":case Ne:return!0;default:return!1}}(t)},{icon:"mdi:target-variant",serviceName:"clean_spot",command:"clean_spot",isSupported:t=>Wt(t,1024),isDisabled:()=>!1},{icon:"mdi:map-marker",serviceName:"locate",command:"locate",isSupported:t=>Wt(t,512),isDisabled:t=>Ed(t)},{icon:"mdi:home-map-marker",serviceName:"return_to_base",command:"return_home",isSupported:t=>Wt(t,16),isDisabled:()=>!1}];let Td=class extends ht{constructor(){super(...arguments),this.fill=!1}callService(t){t.stopPropagation();const e=t.target.entry;this.hass.callService("vacuum",e.serviceName,{entity_id:this.entity.entity_id})}render(){const t=Ie(this.hass);return Y` - - ${Id.filter((t=>Ad(this.entity,t,this.commands))).map((t=>Y` - - - - `))} - - `}};n([_t({attribute:!1})],Td.prototype,"hass",void 0),n([_t({attribute:!1})],Td.prototype,"entity",void 0),n([_t({attribute:!1})],Td.prototype,"commands",void 0),n([_t({type:Boolean})],Td.prototype,"fill",void 0),Td=n([pt("mushroom-vacuum-commands-control")],Td),Pl({type:wd,name:"Mushroom Vacuum Card",description:"Card for vacuum entity"});let Od=class extends Dl{static async getConfigElement(){return await Promise.resolve().then((function(){return mg})),document.createElement(kd)}static async getStubConfig(t){const e=Object.keys(t.states).filter((t=>Cd.includes(t.split(".")[0])));return{type:`custom:${wd}`,entity:e[0]}}get hasControls(){var t,e,i;return!(!this._stateObj||!this._config)&&(e=this._stateObj,i=null!==(t=this._config.commands)&&void 0!==t?t:[],Id.some((t=>Sd(e,t,i))))}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}render(){var t,e;if(!this._config||!this.hass||!this._config.entity)return K;const i=this._stateObj;if(!i)return this.renderNotFound(this._config);const o=this._config.name||i.attributes.friendly_name||"",n=this._config.icon,r=$l(this._config),a=rl(i,r.icon_type),l=Ie(this.hass),s=null!==(e=null===(t=this._config)||void 0===t?void 0:t.commands)&&void 0!==e?e:[];return Y` - - - - ${a?this.renderPicture(a):this.renderIcon(i,n)} - ${this.renderBadge(i)} - ${this.renderStateInfo(i,r,o)}; - - ${((t,e)=>Id.some((i=>Ad(t,i,e))))(i,s)?Y` -
    - - -
    - `:K} -
    -
    - `}renderIcon(t,e){var i,o;return Y` - - - `}static get styles(){return[super.styles,Ll,h` - mushroom-state-item { - cursor: pointer; - } - mushroom-shape-icon { - --icon-color: rgb(var(--rgb-state-vacuum)); - --shape-color: rgba(var(--rgb-state-vacuum), 0.2); - } - .cleaning ha-state-icon { - animation: 5s infinite linear cleaning; - } - .cleaning ha-state-icon { - animation: 2s infinite linear returning; - } - mushroom-vacuum-commands-control { - flex: 1; - } - `]}};Od=n([pt(wd)],Od),console.info("%c🍄 Mushroom 🍄 - 3.6.0","color: #ef5350; font-weight: 700;");const zd=Ce({tap_action:$e(ni),hold_action:$e(ni),double_tap_action:$e(ni)}),Md=t=>[{name:"tap_action",selector:{"ui-action":{actions:t}}},{name:"hold_action",selector:{"ui-action":{actions:t}}},{name:"double_tap_action",selector:{"ui-action":{actions:t}}}],jd=Ce({layout:$e(Se([we("horizontal"),we("vertical"),we("default")])),fill_container:$e(ye()),primary_info:$e(xe(il)),secondary_info:$e(xe(il)),icon_type:$e(xe(ol))}),Dd=[{type:"grid",name:"",schema:[{name:"layout",selector:{mush_layout:{}}},{name:"fill_container",selector:{boolean:{}}}]},{type:"grid",name:"",schema:[{name:"primary_info",selector:{mush_info:{}}},{name:"secondary_info",selector:{mush_info:{}}},{name:"icon_type",selector:{mush_icon_type:{}}}]}],Ld=["icon_color","layout","fill_container","primary_info","secondary_info","icon_type","content_info","use_entity_picture","collapsible_controls","icon_animation"],Pd=Ce({entity:$e(Ee()),name:$e(Ee()),icon:$e(Ee())}),Nd=Ce({index:$e(ke()),view_index:$e(ke()),view_layout:ve(),type:Ee(),layout_options:ve(),visibility:ve()}),Rd=fe(Nd,fe(Pd,jd,zd),Ce({states:$e(be()),show_keypad:(Fd=$e(ye()),Vd=(t,e)=>{console.warn(`🍄 "${e.path}" option is deprecated and no longer available. Remove it from your YAML configuration or use the built-in Home Assistant alarm panel card if you want keypad.`)},new he({...Fd,refiner:(t,e)=>void 0===t||Fd.refiner(t,e),validator:(t,e)=>void 0===t||(Vd(t,e),Fd.validator(t,e))}))}));var Fd,Vd;const Bd=["more-info","navigate","url","call-service","assist","none"],Ud=["armed_home","armed_away","armed_night","armed_vacation","armed_custom_bypass"],Hd=Tt((t=>[{name:"entity",selector:{entity:{domain:Vl}}},{name:"name",selector:{text:{}}},{name:"icon",selector:{icon:{}},context:{icon_entity:"entity"}},...Dd,{type:"multi_select",name:"states",options:Ud.map((e=>[e,t(`ui.card.alarm_control_panel.${e.replace("armed","arm")}`)]))},...Md(Bd)]));let Yd=class extends jl{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Ld.includes(t.name)?e(`editor.card.generic.${t.name}`):"states"===t.name?this.hass.localize("ui.panel.lovelace.editor.card.alarm-panel.available_states"):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}connectedCallback(){super.connectedCallback(),fs()}setConfig(t){me(t,Rd),this._config=t}render(){if(!this.hass||!this._config)return K;const t=Hd(this.hass.localize);return Y` - - `}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([vt()],Yd.prototype,"_config",void 0),Yd=n([pt(Fl)],Yd);var Wd=Object.freeze({__proto__:null,get SwitchCardEditor(){return Yd}});const Xd=[{name:"entity",selector:{entity:{}}},{type:"grid",name:"",schema:[{name:"name",selector:{text:{}}},{name:"content_info",selector:{mush_info:{}}}]},{type:"grid",name:"",schema:[{name:"icon",selector:{icon:{}},context:{icon_entity:"entity"}},{name:"icon_color",selector:{mush_color:{}}}]},{name:"use_entity_picture",selector:{boolean:{}}},...Md()];let Kd=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Ld.includes(t.name)?e(`editor.card.generic.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` - - `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],Kd.prototype,"hass",void 0),n([vt()],Kd.prototype,"_config",void 0),Kd=n([pt(Gl("entity"))],Kd);var Gd=Object.freeze({__proto__:null,get EntityChipEditor(){return Kd}});const qd=["show_conditions","show_temperature"],Zd=[{name:"entity",selector:{entity:{domain:["weather"]}}},{type:"grid",name:"",schema:[{name:"show_conditions",selector:{boolean:{}}},{name:"show_temperature",selector:{boolean:{}}}]},...Md(["more-info","navigate","url","call-service","assist","none"])];let Jd=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Ld.includes(t.name)?e(`editor.card.generic.${t.name}`):qd.includes(t.name)?e(`editor.card.weather.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` - - `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],Jd.prototype,"hass",void 0),n([vt()],Jd.prototype,"_config",void 0),Jd=n([pt(Gl("weather"))],Jd);var Qd=Object.freeze({__proto__:null,get WeatherChipEditor(){return Jd}});const tu=[{name:"icon",selector:{icon:{placeholder:rs}}}];let eu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` - - `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],eu.prototype,"hass",void 0),n([vt()],eu.prototype,"_config",void 0),eu=n([pt(Gl("back"))],eu);var iu=Object.freeze({__proto__:null,get BackChipEditor(){return eu}});const ou=[{type:"grid",name:"",schema:[{name:"icon",selector:{icon:{placeholder:ls}}},{name:"icon_color",selector:{mush_color:{}}}]},...Md(["navigate","url","call-service","assist","none"])];let nu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Ld.includes(t.name)?e(`editor.card.generic.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` - - `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],nu.prototype,"hass",void 0),n([vt()],nu.prototype,"_config",void 0),nu=n([pt(Gl("action"))],nu);var ru=Object.freeze({__proto__:null,get EntityChipEditor(){return nu}});const au=[{name:"icon",selector:{icon:{placeholder:cs}}}];let lu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` - - `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],lu.prototype,"hass",void 0),n([vt()],lu.prototype,"_config",void 0),lu=n([pt(Gl("menu"))],lu);var su=Object.freeze({__proto__:null,get MenuChipEditor(){return lu}});const cu=fe(Nd,fe(jd,zd),Ce({entity:$e(Ee()),icon:$e(Ee()),icon_color:$e(Ee()),primary:$e(Ee()),secondary:$e(Ee()),badge_icon:$e(Ee()),badge_color:$e(Ee()),picture:$e(Ee()),multiline_secondary:$e(ye()),entity_id:$e(Se([Ee(),be(Ee())]))})),du=["badge_icon","badge_color","content","primary","secondary","multiline_secondary","picture"],uu=[{name:"entity",selector:{entity:{}}},{name:"icon",selector:{template:{}}},{name:"icon_color",selector:{template:{}}},{name:"primary",selector:{template:{}}},{name:"secondary",selector:{template:{}}},{name:"badge_icon",selector:{template:{}}},{name:"badge_color",selector:{template:{}}},{name:"picture",selector:{template:{}}},{type:"grid",name:"",schema:[{name:"layout",selector:{mush_layout:{}}},{name:"fill_container",selector:{boolean:{}}},{name:"multiline_secondary",selector:{boolean:{}}}]},...Md()];let hu=class extends jl{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return"entity"===t.name?`${this.hass.localize("ui.panel.lovelace.editor.card.generic.entity")} (${e("editor.card.template.entity_extra")})`:Ld.includes(t.name)?e(`editor.card.generic.${t.name}`):du.includes(t.name)?e(`editor.card.template.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}connectedCallback(){super.connectedCallback(),fs()}setConfig(t){me(t,cu),this._config=t}render(){return this.hass&&this._config?Y` - - `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([vt()],hu.prototype,"_config",void 0),hu=n([pt(cd)],hu);var mu=Object.freeze({__proto__:null,TEMPLATE_LABELS:du,get TemplateCardEditor(){return hu}});const pu=[{name:"entity",selector:{entity:{}}},{name:"icon",selector:{template:{}}},{name:"icon_color",selector:{template:{}}},{name:"picture",selector:{template:{}}},{name:"content",selector:{template:{}}},...Md()];let fu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return"entity"===t.name?`${this.hass.localize("ui.panel.lovelace.editor.card.generic.entity")} (${e("editor.card.template.entity_extra")})`:Ld.includes(t.name)?e(`editor.card.generic.${t.name}`):du.includes(t.name)?e(`editor.card.template.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` - - `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],fu.prototype,"hass",void 0),n([vt()],fu.prototype,"_config",void 0),fu=n([pt(Gl("template"))],fu);var gu=Object.freeze({__proto__:null,get EntityChipEditor(){return fu}}); + .icon=${i} + > + `:K)} + ${n?Y` + + ${r?Y`${r}`:K} + ${n} + + `:K} +
    + `}_handleAction(t){Ke(this,this.hass,this._config,t.detail.action)}isTemplate(t){var e;const i=null===(e=this._config)||void 0===e?void 0:e[t];return null==i?void 0:i.includes("{")}getValue(t){var e,i,o;return this.isTemplate(t)?null===(i=null===(e=this._templateResults[t])||void 0===e?void 0:e.result)||void 0===i?void 0:i.toString():null===(o=this._config)||void 0===o?void 0:o[t]}static get styles(){return h` + :host { + -webkit-tap-highlight-color: transparent; + } + .badge { + position: relative; + --ha-ripple-color: var(--badge-color); + --ha-ripple-hover-opacity: 0.04; + --ha-ripple-pressed-opacity: 0.12; + transition: + box-shadow 180ms ease-in-out, + border-color 180ms ease-in-out; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 8px; + height: 36px; + min-width: 36px; + padding: 0px 8px; + box-sizing: border-box; + width: auto; + border-radius: 18px; + background-color: var(--card-background-color, white); + border-width: var(--ha-card-border-width, 1px); + border-style: solid; + border-color: var( + --ha-card-border-color, + var(--divider-color, #e0e0e0) + ); + --mdc-icon-size: 18px; + text-align: center; + font-family: Roboto; + } + .badge:focus-visible { + --shadow-default: var(--ha-card-box-shadow, 0 0 0 0 transparent); + --shadow-focus: 0 0 0 1px var(--badge-color); + border-color: var(--badge-color); + box-shadow: var(--shadow-default), var(--shadow-focus); + } + button, + [role="button"] { + cursor: pointer; + } + button:focus, + [role="button"]:focus { + outline: none; + } + .content { + display: flex; + flex-direction: column; + align-items: flex-start; + padding-right: 4px; + padding-inline-end: 4px; + padding-inline-start: initial; + } + .name { + font-size: 10px; + font-style: normal; + font-weight: 500; + line-height: 10px; + letter-spacing: 0.1px; + color: var(--secondary-text-color); + } + .state { + font-size: 12px; + font-style: normal; + font-weight: 500; + line-height: 16px; + letter-spacing: 0.1px; + color: var(--primary-text-color); + } + svg { + width: var(--mdc-icon-size); + height: var(--mdc-icon-size); + display: flex; + } + ha-state-icon { + color: var(--badge-color); + line-height: 0; + } + img { + width: 30px; + height: 30px; + border-radius: 50%; + object-fit: cover; + overflow: hidden; + } + .badge.icon-only { + padding: 0; + } + .badge:not(.icon-only) img { + margin-left: -6px; + margin-inline-start: -6px; + margin-inline-end: initial; + } + .badge.content-only .content { + padding-right: 4px; + padding-left: 4px; + padding-inline-end: 4px; + padding-inline-start: 4px; + } + ${ol} + `}};n([_t({attribute:!1})],Pd.prototype,"hass",void 0),n([vt()],Pd.prototype,"_config",void 0),n([vt()],Pd.prototype,"_templateResults",void 0),n([vt()],Pd.prototype,"_unsubRenderTemplates",void 0),Pd=n([pt(jd)],Pd),console.info("%c🍄 Mushroom 🍄 - 4.0.2","color: #ef5350; font-weight: 700;");const Nd=Ce({tap_action:$e(ni),hold_action:$e(ni),double_tap_action:$e(ni)}),Rd=(t,e)=>(e&&t&&(t=t.map((t=>"perform-action"===t?"call-service":t))),[{name:"tap_action",selector:{"ui-action":{actions:t}}},{name:"hold_action",selector:{"ui-action":{actions:t}}},{name:"double_tap_action",selector:{"ui-action":{actions:t}}}]),Fd=Ce({layout:$e(Se([we("horizontal"),we("vertical"),we("default")])),fill_container:$e(ye()),primary_info:$e(xe(os)),secondary_info:$e(xe(os)),icon_type:$e(xe(ns))}),Vd=[{type:"grid",name:"",schema:[{name:"layout",selector:{mush_layout:{}}},{name:"fill_container",selector:{boolean:{}}}]},{type:"grid",name:"",schema:[{name:"primary_info",selector:{mush_info:{}}},{name:"secondary_info",selector:{mush_info:{}}},{name:"icon_type",selector:{mush_icon_type:{}}}]}],Bd=["color","icon_color","layout","fill_container","primary_info","secondary_info","icon_type","content_info","use_entity_picture","collapsible_controls","icon_animation"],Ud=Ce({entity:$e(Ee()),name:$e(Ee()),icon:$e(Ee())}),Hd=Ce({index:$e(ke()),view_index:$e(ke()),view_layout:ve(),type:Ee(),layout_options:ve(),visibility:ve()}),Yd=fe(Hd,fe(Ud,Fd,Nd),Ce({states:$e(be()),show_keypad:(Wd=$e(ye()),Xd=(t,e)=>{console.warn(`🍄 "${e.path}" option is deprecated and no longer available. Remove it from your YAML configuration or use the built-in Home Assistant alarm panel card if you want keypad.`)},new he({...Wd,refiner:(t,e)=>void 0===t||Wd.refiner(t,e),validator:(t,e)=>void 0===t||(Xd(t,e),Wd.validator(t,e))}))}));var Wd,Xd;const Kd=["more-info","navigate","url","perform-action","assist","none"],Gd=["armed_home","armed_away","armed_night","armed_vacation","armed_custom_bypass"],qd=Tt(((t,e)=>[{name:"entity",selector:{entity:{domain:Bs}}},{name:"name",selector:{text:{}}},{name:"icon",selector:{icon:{}},context:{icon_entity:"entity"}},...Vd,{type:"multi_select",name:"states",options:Gd.map((e=>[e,t(`ui.card.alarm_control_panel.${e.replace("armed","arm")}`)]))},...Rd(Kd,e)]));let Zd=class extends Ds{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Bd.includes(t.name)?e(`editor.card.generic.${t.name}`):"states"===t.name?this.hass.localize("ui.panel.lovelace.editor.card.alarm-panel.available_states"):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}connectedCallback(){super.connectedCallback(),gl()}setConfig(t){me(t,Yd),this._config=t}render(){if(!this.hass||!this._config)return K;const t=!ai(this.hass.config.version,2024,8),e=qd(this.hass.localize,t);return Y` + + `}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([vt()],Zd.prototype,"_config",void 0),Zd=n([pt(Vs)],Zd);var Jd=Object.freeze({__proto__:null,get SwitchCardEditor(){return Zd}});const Qd=[{name:"entity",selector:{entity:{}}},{type:"grid",name:"",schema:[{name:"name",selector:{text:{}}},{name:"content_info",selector:{mush_info:{}}}]},{type:"grid",name:"",schema:[{name:"icon",selector:{icon:{}},context:{icon_entity:"entity"}},{name:"icon_color",selector:{mush_color:{}}}]},{name:"use_entity_picture",selector:{boolean:{}}},...Rd()];let tu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Bd.includes(t.name)?e(`editor.card.generic.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` + + `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],tu.prototype,"hass",void 0),n([vt()],tu.prototype,"_config",void 0),tu=n([pt(qs("entity"))],tu);var eu=Object.freeze({__proto__:null,get EntityChipEditor(){return tu}});const iu=["weather"],ou=["show_conditions","show_temperature"],nu=["more-info","navigate","url","perform-action","assist","none"],ru=Tt((t=>[{name:"entity",selector:{entity:{domain:iu}}},{type:"grid",name:"",schema:[{name:"show_conditions",selector:{boolean:{}}},{name:"show_temperature",selector:{boolean:{}}}]},...Rd(nu,t)]));let au=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Bd.includes(t.name)?e(`editor.card.generic.${t.name}`):ou.includes(t.name)?e(`editor.card.weather.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){if(!this.hass||!this._config)return K;const t=!ai(this.hass.config.version,2024,8),e=ru(t);return Y` + + `}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],au.prototype,"hass",void 0),n([vt()],au.prototype,"_config",void 0),au=n([pt(qs("weather"))],au);var su=Object.freeze({__proto__:null,get WeatherChipEditor(){return au}});const lu=[{name:"icon",selector:{icon:{placeholder:al}}}];let cu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` + + `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],cu.prototype,"hass",void 0),n([vt()],cu.prototype,"_config",void 0),cu=n([pt(qs("back"))],cu);var du=Object.freeze({__proto__:null,get BackChipEditor(){return cu}});const uu=["navigate","url","perform-action","assist","none"],hu=Tt((t=>[{type:"grid",name:"",schema:[{name:"icon",selector:{icon:{placeholder:ll}}},{name:"icon_color",selector:{mush_color:{}}}]},...Rd(uu,t)]));let mu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return Bd.includes(t.name)?e(`editor.card.generic.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){if(!this.hass||!this._config)return K;const t=!ai(this.hass.config.version,2024,8),e=hu(t);return Y` + + `}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],mu.prototype,"hass",void 0),n([vt()],mu.prototype,"_config",void 0),mu=n([pt(qs("action"))],mu);var pu=Object.freeze({__proto__:null,get EntityChipEditor(){return mu}});const fu=[{name:"icon",selector:{icon:{placeholder:dl}}}];let gu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` + + `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],gu.prototype,"hass",void 0),n([vt()],gu.prototype,"_config",void 0),gu=n([pt(qs("menu"))],gu);var _u=Object.freeze({__proto__:null,get MenuChipEditor(){return gu}});const vu=fe(Hd,fe(Fd,Nd),Ce({entity:$e(Ee()),icon:$e(Ee()),icon_color:$e(Ee()),primary:$e(Ee()),secondary:$e(Ee()),badge_icon:$e(Ee()),badge_color:$e(Ee()),picture:$e(Ee()),multiline_secondary:$e(ye()),entity_id:$e(Se([Ee(),be(Ee())]))})),bu=["badge_icon","badge_color","content","primary","secondary","multiline_secondary","picture"],yu=[{name:"entity",selector:{entity:{}}},{name:"icon",selector:{template:{}}},{name:"icon_color",selector:{template:{}}},{name:"primary",selector:{template:{}}},{name:"secondary",selector:{template:{}}},{name:"badge_icon",selector:{template:{}}},{name:"badge_color",selector:{template:{}}},{name:"picture",selector:{template:{}}},{type:"grid",name:"",schema:[{name:"layout",selector:{mush_layout:{}}},{name:"fill_container",selector:{boolean:{}}},{name:"multiline_secondary",selector:{boolean:{}}}]},...Rd()];let xu=class extends Ds{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return"entity"===t.name?`${this.hass.localize("ui.panel.lovelace.editor.card.generic.entity")} (${e("editor.card.template.entity_extra")})`:Bd.includes(t.name)?e(`editor.card.generic.${t.name}`):bu.includes(t.name)?e(`editor.card.template.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}connectedCallback(){super.connectedCallback(),gl()}setConfig(t){me(t,vu),this._config=t}render(){return this.hass&&this._config?Y` + + `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([vt()],xu.prototype,"_config",void 0),xu=n([pt(dd)],xu);var wu=Object.freeze({__proto__:null,TEMPLATE_LABELS:bu,get TemplateCardEditor(){return xu}});const ku=[{name:"entity",selector:{entity:{}}},{name:"icon",selector:{template:{}}},{name:"icon_color",selector:{template:{}}},{name:"picture",selector:{template:{}}},{name:"content",selector:{template:{}}},...Rd()];let Cu=class extends ht{constructor(){super(...arguments),this._computeLabel=t=>{const e=Oo(this.hass);return"entity"===t.name?`${this.hass.localize("ui.panel.lovelace.editor.card.generic.entity")} (${e("editor.card.template.entity_extra")})`:Bd.includes(t.name)?e(`editor.card.generic.${t.name}`):bu.includes(t.name)?e(`editor.card.template.${t.name}`):this.hass.localize(`ui.panel.lovelace.editor.card.generic.${t.name}`)}}setConfig(t){this._config=t}render(){return this.hass&&this._config?Y` + + `:K}_valueChanged(t){Lt(this,"config-changed",{config:t.detail.value})}};n([_t({attribute:!1})],Cu.prototype,"hass",void 0),n([vt()],Cu.prototype,"_config",void 0),Cu=n([pt(qs("template"))],Cu);var $u=Object.freeze({__proto__:null,get EntityChipEditor(){return Cu}}); /** * @license * Copyright 2021 Google LLC * SPDX-LIcense-Identifier: Apache-2.0 - */const _u=h`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-text-field--filled{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-text-field--filled .mdc-text-field__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-text-field--filled .mdc-text-field__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-text-field--filled.mdc-ripple-upgraded--unbounded .mdc-text-field__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-activation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-deactivation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-text-field__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-text-field{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0;display:inline-flex;align-items:baseline;padding:0 16px;position:relative;box-sizing:border-box;overflow:hidden;will-change:opacity,transform,color}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input{color:rgba(0, 0, 0, 0.87)}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.54)}}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.54)}}.mdc-text-field .mdc-text-field__input{caret-color:#6200ee;caret-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field-character-counter,.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-text-field__input{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);width:100%;min-width:0;border:none;border-radius:0;background:none;appearance:none;padding:0}.mdc-text-field__input::-ms-clear{display:none}.mdc-text-field__input::-webkit-calendar-picker-indicator{display:none}.mdc-text-field__input:focus{outline:none}.mdc-text-field__input:invalid{box-shadow:none}@media all{.mdc-text-field__input::placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field__input:-ms-input-placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field--no-label .mdc-text-field__input::placeholder,.mdc-text-field--focused .mdc-text-field__input::placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}@media all{.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder,.mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}.mdc-text-field__affix{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0;white-space:nowrap}.mdc-text-field--label-floating .mdc-text-field__affix,.mdc-text-field--no-label .mdc-text-field__affix{opacity:1}@supports(-webkit-hyphens: none){.mdc-text-field--outlined .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field__affix--prefix,.mdc-text-field__affix--prefix[dir=rtl]{padding-left:2px;padding-right:0}.mdc-text-field--end-aligned .mdc-text-field__affix--prefix{padding-left:0;padding-right:12px}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--end-aligned .mdc-text-field__affix--prefix[dir=rtl]{padding-left:12px;padding-right:0}.mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field__affix--suffix,.mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:12px}.mdc-text-field--end-aligned .mdc-text-field__affix--suffix{padding-left:2px;padding-right:0}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--end-aligned .mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:2px}.mdc-text-field--filled{height:56px}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-text-field--filled:hover .mdc-text-field__ripple::before,.mdc-text-field--filled.mdc-ripple-surface--hover .mdc-text-field__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-text-field--filled.mdc-ripple-upgraded--background-focused .mdc-text-field__ripple::before,.mdc-text-field--filled:not(.mdc-ripple-upgraded):focus .mdc-text-field__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-text-field--filled::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-text-field--filled:not(.mdc-text-field--disabled){background-color:whitesmoke}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-text-field--filled:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--filled .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-text-field--filled .mdc-floating-label,.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{height:100%}.mdc-text-field--filled.mdc-text-field--no-label .mdc-floating-label{display:none}.mdc-text-field--filled.mdc-text-field--no-label::before{display:none}@supports(-webkit-hyphens: none){.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field--outlined{height:56px;overflow:visible}.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--outlined .mdc-text-field__input{height:100%}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-text-field--outlined{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined{padding-right:max(16px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-right:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-left:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-right:max(16px, var(--mdc-shape-small, 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-right:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-right:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-text-field--outlined .mdc-text-field__ripple::before,.mdc-text-field--outlined .mdc-text-field__ripple::after{background-color:transparent;background-color:var(--mdc-ripple-color, transparent)}.mdc-text-field--outlined .mdc-floating-label{left:4px;right:initial}[dir=rtl] .mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-text-field--outlined .mdc-text-field__input{display:flex;border:none !important;background-color:transparent}.mdc-text-field--outlined .mdc-notched-outline{z-index:1}.mdc-text-field--textarea{flex-direction:column;align-items:center;width:auto;height:auto;padding:0;transition:none}.mdc-text-field--textarea .mdc-floating-label{top:19px}.mdc-text-field--textarea .mdc-floating-label:not(.mdc-floating-label--float-above){transform:none}.mdc-text-field--textarea .mdc-text-field__input{flex-grow:1;height:auto;min-height:1.5rem;overflow-x:hidden;overflow-y:auto;box-sizing:border-box;resize:none;padding:0 16px;line-height:1.5rem}.mdc-text-field--textarea.mdc-text-field--filled::before{display:none}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-10.25px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-filled 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-filled{0%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-10.25px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-10.25px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--filled .mdc-text-field__input{margin-top:23px;margin-bottom:9px}.mdc-text-field--textarea.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-27.25px) scale(1)}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-24.75px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-24.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-24.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label{top:18px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field__input{margin-bottom:2px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter{align-self:flex-end;padding:0 16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::after{display:inline-block;width:0;height:16px;content:"";vertical-align:-16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::before{display:none}.mdc-text-field__resizer{align-self:stretch;display:inline-flex;flex-direction:column;flex-grow:1;max-height:100%;max-width:100%;min-height:56px;min-width:fit-content;min-width:-moz-available;min-width:-webkit-fill-available;overflow:hidden;resize:both}.mdc-text-field--filled .mdc-text-field__resizer{transform:translateY(-1px)}.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateY(1px)}.mdc-text-field--outlined .mdc-text-field__resizer{transform:translateX(-1px) translateY(-1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer,.mdc-text-field--outlined .mdc-text-field__resizer[dir=rtl]{transform:translateX(1px) translateY(-1px)}.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateX(1px) translateY(1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input[dir=rtl],.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter[dir=rtl]{transform:translateX(-1px) translateY(1px)}.mdc-text-field--with-leading-icon{padding-left:0;padding-right:16px}[dir=rtl] .mdc-text-field--with-leading-icon,.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:16px;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 48px);left:48px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-text-field--with-leading-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake,.mdc-text-field--with-leading-icon.mdc-text-field--outlined[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--with-trailing-icon{padding-left:16px;padding-right:0}[dir=rtl] .mdc-text-field--with-trailing-icon,.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-trailing-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-text-field-helper-line{display:flex;justify-content:space-between;box-sizing:border-box}.mdc-text-field+.mdc-text-field-helper-line{padding-right:16px;padding-left:16px}.mdc-form-field>.mdc-text-field+label{align-self:flex-start}.mdc-text-field--focused:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--focused .mdc-notched-outline__trailing{border-width:2px}.mdc-text-field--focused+.mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg){opacity:1}.mdc-text-field--focused.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-text-field--focused.mdc-text-field--outlined.mdc-text-field--textarea .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid .mdc-text-field__input{caret-color:#b00020;caret-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{opacity:1}.mdc-text-field--disabled{pointer-events:none}.mdc-text-field--disabled .mdc-text-field__input{color:rgba(0, 0, 0, 0.38)}@media all{.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.38)}}@media all{.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.38)}}.mdc-text-field--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-floating-label{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--leading{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:GrayText}}@media screen and (forced-colors: active){.mdc-text-field--disabled .mdc-text-field__input{background-color:Window}.mdc-text-field--disabled .mdc-floating-label{z-index:1}}.mdc-text-field--disabled .mdc-floating-label{cursor:default}.mdc-text-field--disabled.mdc-text-field--filled{background-color:#fafafa}.mdc-text-field--disabled.mdc-text-field--filled .mdc-text-field__ripple{display:none}.mdc-text-field--disabled .mdc-text-field__input{pointer-events:auto}.mdc-text-field--end-aligned .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--end-aligned .mdc-text-field__input[dir=rtl]{text-align:left}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix{direction:ltr}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--leading,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--leading{order:1}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{order:2}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input{order:3}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{order:4}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--trailing,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--trailing{order:5}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--prefix{padding-right:12px}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--suffix{padding-left:2px}.mdc-text-field-helper-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin:0;opacity:0;will-change:opacity;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-text-field-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-text-field-helper-text--persistent{transition:none;opacity:1;will-change:initial}.mdc-text-field-character-counter{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin-left:auto;margin-right:0;padding-left:16px;padding-right:0;white-space:nowrap}.mdc-text-field-character-counter::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{margin-left:0;margin-right:auto}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field__icon{align-self:center;cursor:pointer}.mdc-text-field__icon:not([tabindex]),.mdc-text-field__icon[tabindex="-1"]{cursor:default;pointer-events:none}.mdc-text-field__icon svg{display:block}.mdc-text-field__icon--leading{margin-left:16px;margin-right:8px}[dir=rtl] .mdc-text-field__icon--leading,.mdc-text-field__icon--leading[dir=rtl]{margin-left:8px;margin-right:16px}.mdc-text-field__icon--trailing{padding:12px;margin-left:0px;margin-right:0px}[dir=rtl] .mdc-text-field__icon--trailing,.mdc-text-field__icon--trailing[dir=rtl]{margin-left:0px;margin-right:0px}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-flex;flex-direction:column;outline:none}.mdc-text-field{width:100%}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42))}.mdc-text-field:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-text-field-hover-line-color, rgba(0, 0, 0, 0.87))}.mdc-text-field.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06);border-bottom-color:var(--mdc-text-field-disabled-line-color, rgba(0, 0, 0, 0.06))}.mdc-text-field.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field__input{direction:inherit}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-idle-border-color, rgba(0, 0, 0, 0.38) )}:host(:not([disabled]):hover) :not(.mdc-text-field--invalid):not(.mdc-text-field--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-error-color, var(--mdc-theme-error, #b00020) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-character-counter,:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid .mdc-text-field__icon{color:var(--mdc-text-field-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input{color:var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg),:host(:not([disabled])) .mdc-text-field-helper-line:not(.mdc-text-field--invalid) .mdc-text-field-character-counter{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-text-field.mdc-text-field--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field .mdc-text-field__input,:host([disabled]) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-helper-text,:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-character-counter{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}` + */const Eu=h`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-text-field--filled{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-text-field--filled .mdc-text-field__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-text-field--filled .mdc-text-field__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-text-field--filled.mdc-ripple-upgraded--unbounded .mdc-text-field__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-activation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-deactivation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-text-field__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-text-field{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0;display:inline-flex;align-items:baseline;padding:0 16px;position:relative;box-sizing:border-box;overflow:hidden;will-change:opacity,transform,color}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input{color:rgba(0, 0, 0, 0.87)}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.54)}}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.54)}}.mdc-text-field .mdc-text-field__input{caret-color:#6200ee;caret-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field-character-counter,.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-text-field__input{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);width:100%;min-width:0;border:none;border-radius:0;background:none;appearance:none;padding:0}.mdc-text-field__input::-ms-clear{display:none}.mdc-text-field__input::-webkit-calendar-picker-indicator{display:none}.mdc-text-field__input:focus{outline:none}.mdc-text-field__input:invalid{box-shadow:none}@media all{.mdc-text-field__input::placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field__input:-ms-input-placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field--no-label .mdc-text-field__input::placeholder,.mdc-text-field--focused .mdc-text-field__input::placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}@media all{.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder,.mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}.mdc-text-field__affix{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0;white-space:nowrap}.mdc-text-field--label-floating .mdc-text-field__affix,.mdc-text-field--no-label .mdc-text-field__affix{opacity:1}@supports(-webkit-hyphens: none){.mdc-text-field--outlined .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field__affix--prefix,.mdc-text-field__affix--prefix[dir=rtl]{padding-left:2px;padding-right:0}.mdc-text-field--end-aligned .mdc-text-field__affix--prefix{padding-left:0;padding-right:12px}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--end-aligned .mdc-text-field__affix--prefix[dir=rtl]{padding-left:12px;padding-right:0}.mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field__affix--suffix,.mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:12px}.mdc-text-field--end-aligned .mdc-text-field__affix--suffix{padding-left:2px;padding-right:0}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--end-aligned .mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:2px}.mdc-text-field--filled{height:56px}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-text-field--filled:hover .mdc-text-field__ripple::before,.mdc-text-field--filled.mdc-ripple-surface--hover .mdc-text-field__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-text-field--filled.mdc-ripple-upgraded--background-focused .mdc-text-field__ripple::before,.mdc-text-field--filled:not(.mdc-ripple-upgraded):focus .mdc-text-field__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-text-field--filled::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-text-field--filled:not(.mdc-text-field--disabled){background-color:whitesmoke}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-text-field--filled:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--filled .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-text-field--filled .mdc-floating-label,.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{height:100%}.mdc-text-field--filled.mdc-text-field--no-label .mdc-floating-label{display:none}.mdc-text-field--filled.mdc-text-field--no-label::before{display:none}@supports(-webkit-hyphens: none){.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field--outlined{height:56px;overflow:visible}.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--outlined .mdc-text-field__input{height:100%}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-text-field--outlined{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined{padding-right:max(16px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-right:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-left:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-right:max(16px, var(--mdc-shape-small, 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-right:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-right:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-text-field--outlined .mdc-text-field__ripple::before,.mdc-text-field--outlined .mdc-text-field__ripple::after{background-color:transparent;background-color:var(--mdc-ripple-color, transparent)}.mdc-text-field--outlined .mdc-floating-label{left:4px;right:initial}[dir=rtl] .mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-text-field--outlined .mdc-text-field__input{display:flex;border:none !important;background-color:transparent}.mdc-text-field--outlined .mdc-notched-outline{z-index:1}.mdc-text-field--textarea{flex-direction:column;align-items:center;width:auto;height:auto;padding:0;transition:none}.mdc-text-field--textarea .mdc-floating-label{top:19px}.mdc-text-field--textarea .mdc-floating-label:not(.mdc-floating-label--float-above){transform:none}.mdc-text-field--textarea .mdc-text-field__input{flex-grow:1;height:auto;min-height:1.5rem;overflow-x:hidden;overflow-y:auto;box-sizing:border-box;resize:none;padding:0 16px;line-height:1.5rem}.mdc-text-field--textarea.mdc-text-field--filled::before{display:none}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-10.25px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-filled 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-filled{0%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-10.25px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-10.25px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--filled .mdc-text-field__input{margin-top:23px;margin-bottom:9px}.mdc-text-field--textarea.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-27.25px) scale(1)}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-24.75px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-24.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-24.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label{top:18px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field__input{margin-bottom:2px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter{align-self:flex-end;padding:0 16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::after{display:inline-block;width:0;height:16px;content:"";vertical-align:-16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::before{display:none}.mdc-text-field__resizer{align-self:stretch;display:inline-flex;flex-direction:column;flex-grow:1;max-height:100%;max-width:100%;min-height:56px;min-width:fit-content;min-width:-moz-available;min-width:-webkit-fill-available;overflow:hidden;resize:both}.mdc-text-field--filled .mdc-text-field__resizer{transform:translateY(-1px)}.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateY(1px)}.mdc-text-field--outlined .mdc-text-field__resizer{transform:translateX(-1px) translateY(-1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer,.mdc-text-field--outlined .mdc-text-field__resizer[dir=rtl]{transform:translateX(1px) translateY(-1px)}.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateX(1px) translateY(1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input[dir=rtl],.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter[dir=rtl]{transform:translateX(-1px) translateY(1px)}.mdc-text-field--with-leading-icon{padding-left:0;padding-right:16px}[dir=rtl] .mdc-text-field--with-leading-icon,.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:16px;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 48px);left:48px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-text-field--with-leading-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake,.mdc-text-field--with-leading-icon.mdc-text-field--outlined[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--with-trailing-icon{padding-left:16px;padding-right:0}[dir=rtl] .mdc-text-field--with-trailing-icon,.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-trailing-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-text-field-helper-line{display:flex;justify-content:space-between;box-sizing:border-box}.mdc-text-field+.mdc-text-field-helper-line{padding-right:16px;padding-left:16px}.mdc-form-field>.mdc-text-field+label{align-self:flex-start}.mdc-text-field--focused:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--focused .mdc-notched-outline__trailing{border-width:2px}.mdc-text-field--focused+.mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg){opacity:1}.mdc-text-field--focused.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-text-field--focused.mdc-text-field--outlined.mdc-text-field--textarea .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid .mdc-text-field__input{caret-color:#b00020;caret-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{opacity:1}.mdc-text-field--disabled{pointer-events:none}.mdc-text-field--disabled .mdc-text-field__input{color:rgba(0, 0, 0, 0.38)}@media all{.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.38)}}@media all{.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.38)}}.mdc-text-field--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-floating-label{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--leading{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:GrayText}}@media screen and (forced-colors: active){.mdc-text-field--disabled .mdc-text-field__input{background-color:Window}.mdc-text-field--disabled .mdc-floating-label{z-index:1}}.mdc-text-field--disabled .mdc-floating-label{cursor:default}.mdc-text-field--disabled.mdc-text-field--filled{background-color:#fafafa}.mdc-text-field--disabled.mdc-text-field--filled .mdc-text-field__ripple{display:none}.mdc-text-field--disabled .mdc-text-field__input{pointer-events:auto}.mdc-text-field--end-aligned .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--end-aligned .mdc-text-field__input[dir=rtl]{text-align:left}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix{direction:ltr}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--leading,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--leading{order:1}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{order:2}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input{order:3}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{order:4}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--trailing,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--trailing{order:5}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--prefix{padding-right:12px}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--suffix{padding-left:2px}.mdc-text-field-helper-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin:0;opacity:0;will-change:opacity;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-text-field-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-text-field-helper-text--persistent{transition:none;opacity:1;will-change:initial}.mdc-text-field-character-counter{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin-left:auto;margin-right:0;padding-left:16px;padding-right:0;white-space:nowrap}.mdc-text-field-character-counter::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{margin-left:0;margin-right:auto}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field__icon{align-self:center;cursor:pointer}.mdc-text-field__icon:not([tabindex]),.mdc-text-field__icon[tabindex="-1"]{cursor:default;pointer-events:none}.mdc-text-field__icon svg{display:block}.mdc-text-field__icon--leading{margin-left:16px;margin-right:8px}[dir=rtl] .mdc-text-field__icon--leading,.mdc-text-field__icon--leading[dir=rtl]{margin-left:8px;margin-right:16px}.mdc-text-field__icon--trailing{padding:12px;margin-left:0px;margin-right:0px}[dir=rtl] .mdc-text-field__icon--trailing,.mdc-text-field__icon--trailing[dir=rtl]{margin-left:0px;margin-right:0px}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-flex;flex-direction:column;outline:none}.mdc-text-field{width:100%}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42))}.mdc-text-field:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-text-field-hover-line-color, rgba(0, 0, 0, 0.87))}.mdc-text-field.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06);border-bottom-color:var(--mdc-text-field-disabled-line-color, rgba(0, 0, 0, 0.06))}.mdc-text-field.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field__input{direction:inherit}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-idle-border-color, rgba(0, 0, 0, 0.38) )}:host(:not([disabled]):hover) :not(.mdc-text-field--invalid):not(.mdc-text-field--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-error-color, var(--mdc-theme-error, #b00020) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-character-counter,:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid .mdc-text-field__icon{color:var(--mdc-text-field-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input{color:var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg),:host(:not([disabled])) .mdc-text-field-helper-line:not(.mdc-text-field--invalid) .mdc-text-field-character-counter{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-text-field.mdc-text-field--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field .mdc-text-field__input,:host([disabled]) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-helper-text,:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-character-counter{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}` /** * @license * Copyright 2016 Google Inc. @@ -3218,7 +3494,7 @@ const aa="important",la=" !"+aa,sa=He(class extends Ye{constructor(t){var e;if(s * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. - */;var vu=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),bu={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",INPUT_SELECTOR:".mdc-text-field__input",LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-text-field__icon--leading",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",OUTLINE_SELECTOR:".mdc-notched-outline",PREFIX_SELECTOR:".mdc-text-field__affix--prefix",SUFFIX_SELECTOR:".mdc-text-field__affix--suffix",TRAILING_ICON_SELECTOR:".mdc-text-field__icon--trailing"},yu={DISABLED:"mdc-text-field--disabled",FOCUSED:"mdc-text-field--focused",HELPER_LINE:"mdc-text-field-helper-line",INVALID:"mdc-text-field--invalid",LABEL_FLOATING:"mdc-text-field--label-floating",NO_LABEL:"mdc-text-field--no-label",OUTLINED:"mdc-text-field--outlined",ROOT:"mdc-text-field",TEXTAREA:"mdc-text-field--textarea",WITH_LEADING_ICON:"mdc-text-field--with-leading-icon",WITH_TRAILING_ICON:"mdc-text-field--with-trailing-icon",WITH_INTERNAL_COUNTER:"mdc-text-field--with-internal-counter"},xu={LABEL_SCALE:.75},wu=["pattern","min","max","required","step","minlength","maxlength"],ku=["color","date","datetime-local","month","range","time","week"],Cu=["mousedown","touchstart"],$u=["click","keydown"],Eu=function(t){function e(i,n){void 0===n&&(n={});var r=t.call(this,o(o({},e.defaultAdapter),i))||this;return r.isFocused=!1,r.receivedUserInput=!1,r.valid=!0,r.useNativeValidation=!0,r.validateOnValueChange=!0,r.helperText=n.helperText,r.characterCounter=n.characterCounter,r.leadingIcon=n.leadingIcon,r.trailingIcon=n.trailingIcon,r.inputFocusHandler=function(){r.activateFocus()},r.inputBlurHandler=function(){r.deactivateFocus()},r.inputInputHandler=function(){r.handleInput()},r.setPointerXOffset=function(t){r.setTransformOrigin(t)},r.textFieldInteractionHandler=function(){r.handleTextFieldInteraction()},r.validationAttributeChangeHandler=function(t){r.handleValidationAttributeChange(t)},r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return yu},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return bu},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return xu},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldAlwaysFloat",{get:function(){var t=this.getNativeInput().type;return ku.indexOf(t)>=0},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldFloat",{get:function(){return this.shouldAlwaysFloat||this.isFocused||!!this.getValue()||this.isBadInput()},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldShake",{get:function(){return!this.isFocused&&!this.isValid()&&!!this.getValue()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!0},setInputAttr:function(){},removeInputAttr:function(){},registerTextFieldInteractionHandler:function(){},deregisterTextFieldInteractionHandler:function(){},registerInputInteractionHandler:function(){},deregisterInputInteractionHandler:function(){},registerValidationAttributeChangeHandler:function(){return new MutationObserver((function(){}))},deregisterValidationAttributeChangeHandler:function(){},getNativeInput:function(){return null},isFocused:function(){return!1},activateLineRipple:function(){},deactivateLineRipple:function(){},setLineRippleTransformOrigin:function(){},shakeLabel:function(){},floatLabel:function(){},setLabelRequired:function(){},hasLabel:function(){return!1},getLabelWidth:function(){return 0},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t,e,i,o;this.adapter.hasLabel()&&this.getNativeInput().required&&this.adapter.setLabelRequired(!0),this.adapter.isFocused()?this.inputFocusHandler():this.adapter.hasLabel()&&this.shouldFloat&&(this.notchOutline(!0),this.adapter.floatLabel(!0),this.styleFloating(!0)),this.adapter.registerInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.registerInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.registerInputInteractionHandler("input",this.inputInputHandler);try{for(var n=r(Cu),a=n.next();!a.done;a=n.next()){var l=a.value;this.adapter.registerInputInteractionHandler(l,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{a&&!a.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}try{for(var s=r($u),c=s.next();!c.done;c=s.next()){l=c.value;this.adapter.registerTextFieldInteractionHandler(l,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{c&&!c.done&&(o=s.return)&&o.call(s)}finally{if(i)throw i.error}}this.validationObserver=this.adapter.registerValidationAttributeChangeHandler(this.validationAttributeChangeHandler),this.setcharacterCounter(this.getValue().length)},e.prototype.destroy=function(){var t,e,i,o;this.adapter.deregisterInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.deregisterInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.deregisterInputInteractionHandler("input",this.inputInputHandler);try{for(var n=r(Cu),a=n.next();!a.done;a=n.next()){var l=a.value;this.adapter.deregisterInputInteractionHandler(l,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{a&&!a.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}try{for(var s=r($u),c=s.next();!c.done;c=s.next()){l=c.value;this.adapter.deregisterTextFieldInteractionHandler(l,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{c&&!c.done&&(o=s.return)&&o.call(s)}finally{if(i)throw i.error}}this.adapter.deregisterValidationAttributeChangeHandler(this.validationObserver)},e.prototype.handleTextFieldInteraction=function(){var t=this.adapter.getNativeInput();t&&t.disabled||(this.receivedUserInput=!0)},e.prototype.handleValidationAttributeChange=function(t){var e=this;t.some((function(t){return wu.indexOf(t)>-1&&(e.styleValidity(!0),e.adapter.setLabelRequired(e.getNativeInput().required),!0)})),t.indexOf("maxlength")>-1&&this.setcharacterCounter(this.getValue().length)},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()&&this.adapter.hasLabel())if(t){var e=this.adapter.getLabelWidth()*xu.LABEL_SCALE;this.adapter.notchOutline(e)}else this.adapter.closeOutline()},e.prototype.activateFocus=function(){this.isFocused=!0,this.styleFocused(this.isFocused),this.adapter.activateLineRipple(),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),!this.helperText||!this.helperText.isPersistent()&&this.helperText.isValidation()&&this.valid||this.helperText.showToScreenReader()},e.prototype.setTransformOrigin=function(t){if(!this.isDisabled()&&!this.adapter.hasOutline()){var e=t.touches,i=e?e[0]:t,o=i.target.getBoundingClientRect(),n=i.clientX-o.left;this.adapter.setLineRippleTransformOrigin(n)}},e.prototype.handleInput=function(){this.autoCompleteFocus(),this.setcharacterCounter(this.getValue().length)},e.prototype.autoCompleteFocus=function(){this.receivedUserInput||this.activateFocus()},e.prototype.deactivateFocus=function(){this.isFocused=!1,this.adapter.deactivateLineRipple();var t=this.isValid();this.styleValidity(t),this.styleFocused(this.isFocused),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),this.shouldFloat||(this.receivedUserInput=!1)},e.prototype.getValue=function(){return this.getNativeInput().value},e.prototype.setValue=function(t){if(this.getValue()!==t&&(this.getNativeInput().value=t),this.setcharacterCounter(t.length),this.validateOnValueChange){var e=this.isValid();this.styleValidity(e)}this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.validateOnValueChange&&this.adapter.shakeLabel(this.shouldShake))},e.prototype.isValid=function(){return this.useNativeValidation?this.isNativeInputValid():this.valid},e.prototype.setValid=function(t){this.valid=t,this.styleValidity(t);var e=!t&&!this.isFocused&&!!this.getValue();this.adapter.hasLabel()&&this.adapter.shakeLabel(e)},e.prototype.setValidateOnValueChange=function(t){this.validateOnValueChange=t},e.prototype.getValidateOnValueChange=function(){return this.validateOnValueChange},e.prototype.setUseNativeValidation=function(t){this.useNativeValidation=t},e.prototype.isDisabled=function(){return this.getNativeInput().disabled},e.prototype.setDisabled=function(t){this.getNativeInput().disabled=t,this.styleDisabled(t)},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.setTrailingIconAriaLabel=function(t){this.trailingIcon&&this.trailingIcon.setAriaLabel(t)},e.prototype.setTrailingIconContent=function(t){this.trailingIcon&&this.trailingIcon.setContent(t)},e.prototype.setcharacterCounter=function(t){if(this.characterCounter){var e=this.getNativeInput().maxLength;if(-1===e)throw new Error("MDCTextFieldFoundation: Expected maxlength html property on text input or textarea.");this.characterCounter.setCounterValue(t,e)}},e.prototype.isBadInput=function(){return this.getNativeInput().validity.badInput||!1},e.prototype.isNativeInputValid=function(){return this.getNativeInput().validity.valid},e.prototype.styleValidity=function(t){var i=e.cssClasses.INVALID;if(t?this.adapter.removeClass(i):this.adapter.addClass(i),this.helperText){if(this.helperText.setValidity(t),!this.helperText.isValidation())return;var o=this.helperText.isVisible(),n=this.helperText.getId();o&&n?this.adapter.setInputAttr(bu.ARIA_DESCRIBEDBY,n):this.adapter.removeInputAttr(bu.ARIA_DESCRIBEDBY)}},e.prototype.styleFocused=function(t){var i=e.cssClasses.FOCUSED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.styleDisabled=function(t){var i=e.cssClasses,o=i.DISABLED,n=i.INVALID;t?(this.adapter.addClass(o),this.adapter.removeClass(n)):this.adapter.removeClass(o),this.leadingIcon&&this.leadingIcon.setDisabled(t),this.trailingIcon&&this.trailingIcon.setDisabled(t)},e.prototype.styleFloating=function(t){var i=e.cssClasses.LABEL_FLOATING;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.getNativeInput=function(){return(this.adapter?this.adapter.getNativeInput():null)||{disabled:!1,maxLength:-1,required:!1,type:"input",validity:{badInput:!1,valid:!0},value:""}},e}(vu); + */;var Au=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),Su={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",INPUT_SELECTOR:".mdc-text-field__input",LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-text-field__icon--leading",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",OUTLINE_SELECTOR:".mdc-notched-outline",PREFIX_SELECTOR:".mdc-text-field__affix--prefix",SUFFIX_SELECTOR:".mdc-text-field__affix--suffix",TRAILING_ICON_SELECTOR:".mdc-text-field__icon--trailing"},Iu={DISABLED:"mdc-text-field--disabled",FOCUSED:"mdc-text-field--focused",HELPER_LINE:"mdc-text-field-helper-line",INVALID:"mdc-text-field--invalid",LABEL_FLOATING:"mdc-text-field--label-floating",NO_LABEL:"mdc-text-field--no-label",OUTLINED:"mdc-text-field--outlined",ROOT:"mdc-text-field",TEXTAREA:"mdc-text-field--textarea",WITH_LEADING_ICON:"mdc-text-field--with-leading-icon",WITH_TRAILING_ICON:"mdc-text-field--with-trailing-icon",WITH_INTERNAL_COUNTER:"mdc-text-field--with-internal-counter"},Tu={LABEL_SCALE:.75},zu=["pattern","min","max","required","step","minlength","maxlength"],Ou=["color","date","datetime-local","month","range","time","week"],Mu=["mousedown","touchstart"],ju=["click","keydown"],Du=function(t){function e(i,n){void 0===n&&(n={});var r=t.call(this,o(o({},e.defaultAdapter),i))||this;return r.isFocused=!1,r.receivedUserInput=!1,r.valid=!0,r.useNativeValidation=!0,r.validateOnValueChange=!0,r.helperText=n.helperText,r.characterCounter=n.characterCounter,r.leadingIcon=n.leadingIcon,r.trailingIcon=n.trailingIcon,r.inputFocusHandler=function(){r.activateFocus()},r.inputBlurHandler=function(){r.deactivateFocus()},r.inputInputHandler=function(){r.handleInput()},r.setPointerXOffset=function(t){r.setTransformOrigin(t)},r.textFieldInteractionHandler=function(){r.handleTextFieldInteraction()},r.validationAttributeChangeHandler=function(t){r.handleValidationAttributeChange(t)},r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Iu},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Su},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return Tu},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldAlwaysFloat",{get:function(){var t=this.getNativeInput().type;return Ou.indexOf(t)>=0},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldFloat",{get:function(){return this.shouldAlwaysFloat||this.isFocused||!!this.getValue()||this.isBadInput()},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldShake",{get:function(){return!this.isFocused&&!this.isValid()&&!!this.getValue()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!0},setInputAttr:function(){},removeInputAttr:function(){},registerTextFieldInteractionHandler:function(){},deregisterTextFieldInteractionHandler:function(){},registerInputInteractionHandler:function(){},deregisterInputInteractionHandler:function(){},registerValidationAttributeChangeHandler:function(){return new MutationObserver((function(){}))},deregisterValidationAttributeChangeHandler:function(){},getNativeInput:function(){return null},isFocused:function(){return!1},activateLineRipple:function(){},deactivateLineRipple:function(){},setLineRippleTransformOrigin:function(){},shakeLabel:function(){},floatLabel:function(){},setLabelRequired:function(){},hasLabel:function(){return!1},getLabelWidth:function(){return 0},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t,e,i,o;this.adapter.hasLabel()&&this.getNativeInput().required&&this.adapter.setLabelRequired(!0),this.adapter.isFocused()?this.inputFocusHandler():this.adapter.hasLabel()&&this.shouldFloat&&(this.notchOutline(!0),this.adapter.floatLabel(!0),this.styleFloating(!0)),this.adapter.registerInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.registerInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.registerInputInteractionHandler("input",this.inputInputHandler);try{for(var n=r(Mu),a=n.next();!a.done;a=n.next()){var s=a.value;this.adapter.registerInputInteractionHandler(s,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{a&&!a.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}try{for(var l=r(ju),c=l.next();!c.done;c=l.next()){s=c.value;this.adapter.registerTextFieldInteractionHandler(s,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{c&&!c.done&&(o=l.return)&&o.call(l)}finally{if(i)throw i.error}}this.validationObserver=this.adapter.registerValidationAttributeChangeHandler(this.validationAttributeChangeHandler),this.setcharacterCounter(this.getValue().length)},e.prototype.destroy=function(){var t,e,i,o;this.adapter.deregisterInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.deregisterInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.deregisterInputInteractionHandler("input",this.inputInputHandler);try{for(var n=r(Mu),a=n.next();!a.done;a=n.next()){var s=a.value;this.adapter.deregisterInputInteractionHandler(s,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{a&&!a.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}try{for(var l=r(ju),c=l.next();!c.done;c=l.next()){s=c.value;this.adapter.deregisterTextFieldInteractionHandler(s,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{c&&!c.done&&(o=l.return)&&o.call(l)}finally{if(i)throw i.error}}this.adapter.deregisterValidationAttributeChangeHandler(this.validationObserver)},e.prototype.handleTextFieldInteraction=function(){var t=this.adapter.getNativeInput();t&&t.disabled||(this.receivedUserInput=!0)},e.prototype.handleValidationAttributeChange=function(t){var e=this;t.some((function(t){return zu.indexOf(t)>-1&&(e.styleValidity(!0),e.adapter.setLabelRequired(e.getNativeInput().required),!0)})),t.indexOf("maxlength")>-1&&this.setcharacterCounter(this.getValue().length)},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()&&this.adapter.hasLabel())if(t){var e=this.adapter.getLabelWidth()*Tu.LABEL_SCALE;this.adapter.notchOutline(e)}else this.adapter.closeOutline()},e.prototype.activateFocus=function(){this.isFocused=!0,this.styleFocused(this.isFocused),this.adapter.activateLineRipple(),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),!this.helperText||!this.helperText.isPersistent()&&this.helperText.isValidation()&&this.valid||this.helperText.showToScreenReader()},e.prototype.setTransformOrigin=function(t){if(!this.isDisabled()&&!this.adapter.hasOutline()){var e=t.touches,i=e?e[0]:t,o=i.target.getBoundingClientRect(),n=i.clientX-o.left;this.adapter.setLineRippleTransformOrigin(n)}},e.prototype.handleInput=function(){this.autoCompleteFocus(),this.setcharacterCounter(this.getValue().length)},e.prototype.autoCompleteFocus=function(){this.receivedUserInput||this.activateFocus()},e.prototype.deactivateFocus=function(){this.isFocused=!1,this.adapter.deactivateLineRipple();var t=this.isValid();this.styleValidity(t),this.styleFocused(this.isFocused),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),this.shouldFloat||(this.receivedUserInput=!1)},e.prototype.getValue=function(){return this.getNativeInput().value},e.prototype.setValue=function(t){if(this.getValue()!==t&&(this.getNativeInput().value=t),this.setcharacterCounter(t.length),this.validateOnValueChange){var e=this.isValid();this.styleValidity(e)}this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.validateOnValueChange&&this.adapter.shakeLabel(this.shouldShake))},e.prototype.isValid=function(){return this.useNativeValidation?this.isNativeInputValid():this.valid},e.prototype.setValid=function(t){this.valid=t,this.styleValidity(t);var e=!t&&!this.isFocused&&!!this.getValue();this.adapter.hasLabel()&&this.adapter.shakeLabel(e)},e.prototype.setValidateOnValueChange=function(t){this.validateOnValueChange=t},e.prototype.getValidateOnValueChange=function(){return this.validateOnValueChange},e.prototype.setUseNativeValidation=function(t){this.useNativeValidation=t},e.prototype.isDisabled=function(){return this.getNativeInput().disabled},e.prototype.setDisabled=function(t){this.getNativeInput().disabled=t,this.styleDisabled(t)},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.setTrailingIconAriaLabel=function(t){this.trailingIcon&&this.trailingIcon.setAriaLabel(t)},e.prototype.setTrailingIconContent=function(t){this.trailingIcon&&this.trailingIcon.setContent(t)},e.prototype.setcharacterCounter=function(t){if(this.characterCounter){var e=this.getNativeInput().maxLength;if(-1===e)throw new Error("MDCTextFieldFoundation: Expected maxlength html property on text input or textarea.");this.characterCounter.setCounterValue(t,e)}},e.prototype.isBadInput=function(){return this.getNativeInput().validity.badInput||!1},e.prototype.isNativeInputValid=function(){return this.getNativeInput().validity.valid},e.prototype.styleValidity=function(t){var i=e.cssClasses.INVALID;if(t?this.adapter.removeClass(i):this.adapter.addClass(i),this.helperText){if(this.helperText.setValidity(t),!this.helperText.isValidation())return;var o=this.helperText.isVisible(),n=this.helperText.getId();o&&n?this.adapter.setInputAttr(Su.ARIA_DESCRIBEDBY,n):this.adapter.removeInputAttr(Su.ARIA_DESCRIBEDBY)}},e.prototype.styleFocused=function(t){var i=e.cssClasses.FOCUSED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.styleDisabled=function(t){var i=e.cssClasses,o=i.DISABLED,n=i.INVALID;t?(this.adapter.addClass(o),this.adapter.removeClass(n)):this.adapter.removeClass(o),this.leadingIcon&&this.leadingIcon.setDisabled(t),this.trailingIcon&&this.trailingIcon.setDisabled(t)},e.prototype.styleFloating=function(t){var i=e.cssClasses.LABEL_FLOATING;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.getNativeInput=function(){return(this.adapter?this.adapter.getNativeInput():null)||{disabled:!1,maxLength:-1,required:!1,type:"input",validity:{badInput:!1,valid:!0},value:""}},e}(Au); /** * @license * Copyright 2016 Google Inc. @@ -3246,13 +3522,13 @@ const aa="important",la=" !"+aa,sa=He(class extends Ye{constructor(t){var e;if(s * Copyright 2020 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ -const Au={},Su=He(class extends Ye{constructor(t){if(super(t),t.type!==Be&&t.type!==Ve&&t.type!==Ue)throw Error("The `live` directive is not allowed on child or event bindings");if(!(t=>void 0===t.strings)(t))throw Error("`live` bindings can only contain a single expression")}render(t){return t}update(t,[e]){if(e===X||e===K)return e;const i=t.element,o=t.name;if(t.type===Be){if(e===i[o])return X}else if(t.type===Ue){if(!!e===i.hasAttribute(o))return X}else if(t.type===Ve&&i.getAttribute(o)===e+"")return X;return((t,e=Au)=>{t._$AH=e; +const Lu={},Pu=He(class extends Ye{constructor(t){if(super(t),t.type!==Be&&t.type!==Ve&&t.type!==Ue)throw Error("The `live` directive is not allowed on child or event bindings");if(!(t=>void 0===t.strings)(t))throw Error("`live` bindings can only contain a single expression")}render(t){return t}update(t,[e]){if(e===X||e===K)return e;const i=t.element,o=t.name;if(t.type===Be){if(e===i[o])return X}else if(t.type===Ue){if(!!e===i.hasAttribute(o))return X}else if(t.type===Ve&&i.getAttribute(o)===e+"")return X;return((t,e=Lu)=>{t._$AH=e; /** * @license * Copyright 2020 Google LLC * SPDX-License-Identifier: BSD-3-Clause - */})(t),e}}),Iu=["touchstart","touchmove","scroll","mousewheel"],Tu=(t={})=>{const e={};for(const i in t)e[i]=t[i];return Object.assign({badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:!0,valueMissing:!1},e)};class Ou extends tr{constructor(){super(...arguments),this.mdcFoundationClass=Eu,this.value="",this.type="text",this.placeholder="",this.label="",this.icon="",this.iconTrailing="",this.disabled=!1,this.required=!1,this.minLength=-1,this.maxLength=-1,this.outlined=!1,this.helper="",this.validateOnInitialRender=!1,this.validationMessage="",this.autoValidate=!1,this.pattern="",this.min="",this.max="",this.step=null,this.size=null,this.helperPersistent=!1,this.charCounter=!1,this.endAligned=!1,this.prefix="",this.suffix="",this.name="",this.readOnly=!1,this.autocapitalize="",this.outlineOpen=!1,this.outlineWidth=0,this.isUiValid=!0,this.focused=!1,this._validity=Tu(),this.validityTransform=null}get validity(){return this._checkValidity(this.value),this._validity}get willValidate(){return this.formElement.willValidate}get selectionStart(){return this.formElement.selectionStart}get selectionEnd(){return this.formElement.selectionEnd}focus(){const t=new CustomEvent("focus");this.formElement.dispatchEvent(t),this.formElement.focus()}blur(){const t=new CustomEvent("blur");this.formElement.dispatchEvent(t),this.formElement.blur()}select(){this.formElement.select()}setSelectionRange(t,e,i){this.formElement.setSelectionRange(t,e,i)}update(t){t.has("autoValidate")&&this.mdcFoundation&&this.mdcFoundation.setValidateOnValueChange(this.autoValidate),t.has("value")&&"string"!=typeof this.value&&(this.value=`${this.value}`),super.update(t)}setFormData(t){this.name&&t.append(this.name,this.value)}render(){const t=this.charCounter&&-1!==this.maxLength,e=!!this.helper||!!this.validationMessage||t,i={"mdc-text-field--disabled":this.disabled,"mdc-text-field--no-label":!this.label,"mdc-text-field--filled":!this.outlined,"mdc-text-field--outlined":this.outlined,"mdc-text-field--with-leading-icon":this.icon,"mdc-text-field--with-trailing-icon":this.iconTrailing,"mdc-text-field--end-aligned":this.endAligned};return Y` -