Repository: JuanMTech/Home_Assistant_files Branch: master Commit: 9461eaf7818e Files: 170 Total size: 6.3 MB Directory structure: gitextract_4o_zm6x8/ ├── .vscode/ │ └── settings.json ├── README.md ├── automations.yaml ├── cloud_config.yaml ├── configuration.yaml ├── custom_components/ │ ├── alarmo/ │ │ ├── __init__.py │ │ ├── alarm_control_panel.py │ │ ├── automations.py │ │ ├── card.py │ │ ├── config_flow.py │ │ ├── const.py │ │ ├── event.py │ │ ├── frontend/ │ │ │ └── dist/ │ │ │ └── alarm-panel.js │ │ ├── helpers.py │ │ ├── manifest.json │ │ ├── mqtt.py │ │ ├── panel.py │ │ ├── sensors.py │ │ ├── services.yaml │ │ ├── store.py │ │ └── websockets.py │ └── hacs/ │ ├── __init__.py │ ├── base.py │ ├── config_flow.py │ ├── const.py │ ├── diagnostics.py │ ├── entity.py │ ├── enums.py │ ├── exceptions.py │ ├── frontend.py │ ├── hacs_frontend/ │ │ ├── __init__.py │ │ ├── c.004a7b01.js │ │ ├── c.01f18260.js │ │ ├── c.063631e8.js │ │ ├── c.0681dd5f.js │ │ ├── c.07e1d870.js │ │ ├── c.18158037.js │ │ ├── c.21c042d4.js │ │ ├── c.2442b973.js │ │ ├── c.24bd2446.js │ │ ├── c.262db200.js │ │ ├── c.2a7cd87a.js │ │ ├── c.2aa297ae.js │ │ ├── c.3507790e.js │ │ ├── c.38b86040.js │ │ ├── c.46ab4f2a.js │ │ ├── c.487362b0.js │ │ ├── c.4a97632a.js │ │ ├── c.4d3573a6.js │ │ ├── c.50bfd408.js │ │ ├── c.55084342.js │ │ ├── c.5d9598b2.js │ │ ├── c.5ec2d281.js │ │ ├── c.656daa82.js │ │ ├── c.6711bf6c.js │ │ ├── c.6764b15a.js │ │ ├── c.67735e63.js │ │ ├── c.743a15a1.js │ │ ├── c.80122d23.js │ │ ├── c.8e28b461.js │ │ ├── c.90083196.js │ │ ├── c.91b5f3b9.js │ │ ├── c.9475214f.js │ │ ├── c.962e71e3.js │ │ ├── c.99c1c4ba.js │ │ ├── c.9a1f96ed.js │ │ ├── c.a1d7c963.js │ │ ├── c.a9ee435e.js │ │ ├── c.abbcb4be.js │ │ ├── c.ad739743.js │ │ ├── c.b27925d9.js │ │ ├── c.bad50d60.js │ │ ├── c.bc53dda1.js │ │ ├── c.bd21ff9b.js │ │ ├── c.c28253df.js │ │ ├── c.c4f30bcd.js │ │ ├── c.cf66b923.js │ │ ├── c.d5d0a5c5.js │ │ ├── c.d9dcade0.js │ │ ├── c.da8de431.js │ │ ├── c.e0e56ec4.js │ │ ├── c.e23b0d0b.js │ │ ├── c.e431cd4c.js │ │ ├── c.e4d174e7.js │ │ ├── c.e6921cf4.js │ │ ├── c.eb4f9da9.js │ │ ├── c.ecb39554.js │ │ ├── c.f4178207.js │ │ ├── c.f6d57c77.js │ │ ├── c.fb6ed4e2.js │ │ ├── c.fb76e5d5.js │ │ ├── c.fc1fab2b.js │ │ ├── c.fe747ba2.js │ │ ├── c.ff22e505.js │ │ ├── entrypoint.js │ │ ├── main-7bc9a818.js │ │ ├── manifest.json │ │ └── version.py │ ├── iconset.js │ ├── manifest.json │ ├── repositories/ │ │ ├── __init__.py │ │ ├── appdaemon.py │ │ ├── base.py │ │ ├── integration.py │ │ ├── netdaemon.py │ │ ├── plugin.py │ │ ├── python_script.py │ │ └── theme.py │ ├── sensor.py │ ├── system_health.py │ ├── translations/ │ │ └── en.json │ ├── update.py │ ├── utils/ │ │ ├── __init__.py │ │ ├── backup.py │ │ ├── configuration_schema.py │ │ ├── data.py │ │ ├── decode.py │ │ ├── decorator.py │ │ ├── default.repositories │ │ ├── filters.py │ │ ├── json.py │ │ ├── logger.py │ │ ├── path.py │ │ ├── platform_setup.py │ │ ├── queue_manager.py │ │ ├── regex.py │ │ ├── store.py │ │ ├── template.py │ │ ├── validate.py │ │ ├── version.py │ │ └── workarounds.py │ ├── validate/ │ │ ├── README.md │ │ ├── __init__.py │ │ ├── archived.py │ │ ├── base.py │ │ ├── brands.py │ │ ├── description.py │ │ ├── hacsjson.py │ │ ├── images.py │ │ ├── information.py │ │ ├── integration_manifest.py │ │ ├── issues.py │ │ ├── manager.py │ │ └── topics.py │ └── websocket/ │ ├── __init__.py │ ├── critical.py │ ├── repositories.py │ └── repository.py ├── lovelace_ui_raw_config_file.yaml ├── scenes.yaml ├── scripts.yaml ├── switches/ │ ├── antonettes_work_laptop.yaml │ ├── br_fireplace.yaml │ ├── br_tv.yaml │ ├── juans_work_laptop.yaml │ ├── lr_fireplace.yaml │ ├── lr_tv.yaml │ └── mac_studio_wol.yaml ├── themes/ │ ├── google_dark_theme.yaml │ ├── google_light_theme.yaml │ ├── google_theme.yaml │ ├── ios_dark_mode.yaml │ ├── ios_light_mode.yaml │ ├── ios_theme.yaml │ └── macos.yaml └── www/ └── community/ ├── alarmo-card/ │ └── alarmo-card.js ├── kiosk-mode/ │ └── kiosk-mode.js ├── lovelace-mushroom/ │ └── mushroom.js ├── mini-graph-card/ │ └── mini-graph-card-bundle.js └── vertical-stack-in-card/ └── vertical-stack-in-card.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .vscode/settings.json ================================================ { "files.associations": { "*.yaml": "home-assistant" } } ================================================ FILE: README.md ================================================


![GitHub Watchers][watchers] ![GitHub Stars][stars] ![GitHub Forks][forks]
[![Subscribe to YouTube channel][youtube-sub-shield]][youtubesubscribe] [![Become a Patron][become-a-patron-shield]][becomeapatron] [![Buy me a coffee][buymeacoffee-shield]][buymeacoffee]
# About Hey guys, Juan here from [juanmtech.com](https://www.juanmtech.com). These are the Home Assistant configuration files I use in my personal Home Assistant set up. Hopefully, this repository can help you get started with Home Assistant and give you some ideas for your own configuration. If you are new to Home Assistant, I recommend visiting my [YouTube channel](https://www.youtube.com/JuanMTech) and [website](https://www.juanmtech.com) where you can find easy to follow tutorials on how to get started.

## Lovelace set up


## Home Assistant tutorials Here are some of the tutorials that can get you started with Home Assistant:










## Video tutorials and written guides * [YouTube Playlist](https://www.youtube.com/playlist?list=PLLydq6ff7NvJ1ioQSVRCt2FJK9EFzRKWr) - Easy to follow tutorials to get you started with Home Assistant * [Written guides](https://www.juanmtech.com/tags/home-assistant/) - Companion writtent guides for all video tutorials ## Themes I made for Home Assistant *(Available via HACS)* * [macOS-Theme](https://github.com/JuanMTech/macOS-Theme) (Dark/Light) - ⭐ * [iOS Theme](https://github.com/JuanMTech/ios-theme) (Dark/Light) - ⭐ * [Google Theme](https://github.com/JuanMTech/google-theme) (Dark/Light) - ⭐ * [Google Dark Theme](https://github.com/JuanMTech/google_dark_theme) * [Google Light Theme](https://github.com/JuanMTech/google_light_theme) * [iOS Dark Mode Theme](https://github.com/JuanMTech/ios_dark_mode_theme) * [iOS Light Mode Theme](https://github.com/JuanMTech/ios_light_mode_theme) * [AMOLED Blue Theme](https://github.com/JuanMTech/amoled_blue) * [Green Dark mode Theme](https://github.com/JuanMTech/green_dark_mode) * [Green Light mode Theme](https://github.com/JuanMTech/green_light_mode) * [Orange Dark Theme](https://github.com/JuanMTech/orange_dark) * [Orange Light Theme](https://github.com/JuanMTech/orange_light) ## Devices running Home Assistant * Server running [Unraid](https://unraid.net) with Home Assistant in a virtual machine ## Common equipment to run Home Assistant * [Raspberry Pi 4](https://amzn.to/2Q3UwVi) * [Raspberry Pi 4 case](https://amzn.to/2Q5HSoI) - *Best case for the Raspberry Pi 4* * [Samsung microSD card 32GB](https://amzn.to/313ZUhu) * [Intel NUC](https://amzn.to/34busQD) - *For extra performance* ## Devices to use with Home Assistant to automate your home 1. **Smart Speakers** * [Google Home](https://store.google.com/us/product/google_home?hl=en-US) * [Google Home Mini](https://store.google.com/product/google_home_mini?43700033967804248&gclid=Cj0KCQjwquTbBRCSARIsADzW88zhzmT6J3-arWJHTvvj4tbvnhIOjxhwb2yICOB36OtowI4cXF59k0kaAuF-EALw_wcB&gclsrc=aw.ds&dclid=CM7vqqfd-dwCFcS7swodddECDA) * [Amazon Echo](https://amzn.to/2XzITuC) * [Amazon Echo Dot](https://amzn.to/2N7nHYW)
2. **InfraRed Remote** * [BroadLink RM4 pro](https://amzn.to/3QdzgJS) * [BroadLink RM4 Mini](https://amzn.to/3zuwBVt)
3. **Zigbee Devices** * [ConBee II - Zigbee USB Gateway](https://amzn.to/324zSdd) * [Aqara Door and Window Sensor](https://amzn.to/313ZYxO) * [Aqara Smart Plug](https://amzn.to/3h8odkb) * [Aqara Wireless Mini Switch](https://amzn.to/3g2n5wV) * [Aqara Temperature and Humidity Sensor](https://amzn.to/2Eg6fgN) * [Philips Hue Play Black & Color Smart Light](https://amzn.to/3iR9Ktj) * [Philips Hue Smart Dimmer Switch](https://amzn.to/3aBKd4c)
4. **Z-Wave Devices** * [Aeotec Z-Stick 7 Plus](https://amzn.to/3BEMawk) * [First Alert Z-Wave Smoke Detector & Carbon Monoxide Alarm](https://amzn.to/3oPbNmL)

> ⚠️ ***DISCLOSURE:** Some of the links here, are **affiliate links**, meaning, at no additional cost to you, I will earn a commission if you click through and make a purchase.*** [watchers]: https://img.shields.io/github/watchers/JuanMTech/Home_Assistant_files.svg?style=social&label=Watchers [stars]: https://img.shields.io/github/stars/JuanMTech/Home_Assistant_files.svg?style=social&label=Stars [forks]: https://img.shields.io/github/forks/JuanMTech/Home_Assistant_files.svg?style=social&label=Forks [buymeacoffee-shield]: https://raw.githubusercontent.com/JuanMTech/Home_Assistant_files/master/images/Support_buttons/Buy_me_a_coffee.png [buymeacoffee]: https://www.buymeacoffee.com/JuanMTech [become-a-patron-shield]: https://raw.githubusercontent.com/JuanMTech/Home_Assistant_files/master/images/Support_buttons/Patreon.png [becomeapatron]: https://www.patreon.com/JuanMTech [youtube-sub-shield]: https://raw.githubusercontent.com/JuanMTech/Home_Assistant_files/master/images/Support_buttons/YouTube_Subscribe.png [youtubesubscribe]: https://www.youtube.com/c/JuanMTech?sub_confirmation=1 ================================================ FILE: automations.yaml ================================================ - id: '1609360640751' alias: Ofc Desk light toggle description: '' trigger: - device_id: 9a4868dc7e530466fc51a82a domain: zha platform: device type: remote_button_double_press subtype: remote_button_double_press condition: [] action: - type: toggle device_id: 98a6f3f2f6065c6593d7a4e8ccc38fca entity_id: light.ofc_desk_light domain: light mode: single - id: '1609360756657' alias: Lr light toggle description: Toggle living room light with living room switch trigger: - device_id: 3966ae998ca93d88e8dc1fa3 domain: zha platform: device type: remote_button_short_press subtype: remote_button_short_press condition: [] action: - service: light.toggle data: {} target: entity_id: light.lr_light mode: single - id: '1609360846832' alias: Br light toggle with bed switch description: '' trigger: - device_id: 1a9d25890f90f9adfd8001a68 domain: zha platform: device type: remote_button_short_press subtype: remote_button_short_press condition: [] action: - service: light.toggle data: {} target: entity_id: light.br_light mode: single - id: '1609361264184' alias: Lr TV lights toggle description: '' trigger: - device_id: 3966ae998ca93d88e8dc1fa3 domain: zha platform: device type: remote_button_double_press subtype: remote_button_double_press condition: [] action: - service: light.toggle target: entity_id: light.lr_tv_lights data: {} mode: single - id: '1609361366516' alias: Lr Toggle Fireplace light with living room switch description: '' trigger: - device_id: 3966ae998ca93d88e8dc1fa3 domain: zha platform: device type: remote_button_long_press subtype: remote_button_long_press condition: [] action: - service: switch.toggle data: {} target: entity_id: switch.lr_fireplace mode: single - id: '1609361438749' alias: Br Toggle bedroom AC/Fan with bed switch description: '' trigger: - device_id: 1a9d25890f90f9adfd8001a68 domain: zha platform: device type: remote_button_long_press subtype: remote_button_long_press condition: [] action: - service: script.br_air_conditioner data: {} - service: script.br_fan data: {} enabled: false mode: single - id: '1609712886475' alias: Ofc Monitor light modes description: '' trigger: - platform: state entity_id: input_select.ofc_monitor_light_mode to: Soft light - platform: state entity_id: input_select.ofc_monitor_light_mode to: YouTube light condition: [] action: - choose: - conditions: - condition: state entity_id: input_select.ofc_monitor_light_mode state: Soft light sequence: - scene: scene.soft_light - conditions: - condition: state entity_id: input_select.ofc_monitor_light_mode state: YouTube light sequence: - scene: scene.youtube_light default: - service: light.toggle data: {} target: entity_id: light.ofc_monitor_lights mode: single - id: '1609717531423' alias: Ofc Monitors lights toggle description: '' trigger: - device_id: 9a4868dc7e530466fc51a82a domain: zha platform: device type: remote_button_short_press subtype: remote_button_short_press condition: [] action: - service: light.toggle data: {} target: entity_id: light.ofc_monitor_lights mode: single - id: '1609728017637' alias: Lr TV lights mode dropdown set to Selec description: '' trigger: - platform: state entity_id: input_select.lr_tv_light_mode from: Select condition: [] action: - service: input_select.select_option data: option: Select target: entity_id: input_select.lr_tv_light_mode mode: single - id: '1609783393518' alias: Ofc Turn off monitor lights when Mac Studio turns off description: '' trigger: - platform: state entity_id: - switch.mac_studio to: 'off' condition: - condition: state entity_id: light.ofc_monitor_lights state: 'on' action: - service: light.turn_off target: entity_id: light.ofc_monitor_lights data: {} mode: single - id: '1623453911792' alias: Lr TV light modes description: Change TV lights to different scenes trigger: - platform: state to: White entity_id: input_select.lr_tv_light_mode - platform: state entity_id: input_select.lr_tv_light_mode to: Warm - platform: state to: Blue entity_id: input_select.lr_tv_light_mode - platform: state entity_id: input_select.lr_tv_light_mode to: Purple condition: - condition: state entity_id: light.lr_tv_lights state: 'on' action: - choose: - conditions: - condition: state entity_id: input_select.lr_tv_light_mode state: White sequence: - scene: scene.tv_light_white - conditions: - condition: state entity_id: input_select.lr_tv_light_mode state: Warm sequence: - scene: scene.tv_light_warm - conditions: - condition: state entity_id: input_select.lr_tv_light_mode state: Blue sequence: - scene: scene.tv_light_blue - conditions: - condition: state entity_id: input_select.lr_tv_light_mode state: Purple sequence: - scene: scene.tv_light_purple default: - service: light.toggle data: {} target: entity_id: - light.lr_tv_light_left - light.lr_tv_light_right mode: single - id: '1623454516597' alias: Ofc Monitor light mode dropdown set to Select description: '' trigger: - platform: state entity_id: input_select.ofc_monitor_light_mode from: Select condition: [] action: - service: input_select.select_option data: option: Select target: entity_id: input_select.ofc_monitor_light_mode mode: single - id: '1626477608097' alias: Ofc Set monitors lights to soft white when turn on during day light description: '' trigger: - platform: state entity_id: light.ofc_monitor_lights from: 'off' to: 'on' condition: - condition: time after: 08:00:00 before: '23:00:00' action: - scene: scene.soft_light mode: single - id: '1628409747827' alias: Br Fireplace toggle with bed switch description: '' trigger: - device_id: 1a9d25890f90f9adfd8001a68 domain: zha platform: device type: remote_button_double_press subtype: remote_button_double_press condition: [] action: - service: switch.toggle target: entity_id: switch.br_fireplace data: {} mode: single - id: '1628833569186' alias: Ofc Turn off living room light with office switch description: '' trigger: - device_id: 9a4868dc7e530466fc51a82a domain: zha platform: device type: remote_button_long_press subtype: remote_button_long_press condition: [] action: - service: light.toggle data: {} target: entity_id: light.lr_light mode: single - id: '1631293897934' alias: Lr Cam Privacy Mode description: '' trigger: - platform: state entity_id: switch.lr_camera_privacy_mode to: 'on' - platform: state entity_id: switch.lr_camera_privacy_mode to: 'off' condition: [] action: - delay: hours: 0 minutes: 0 seconds: 0 milliseconds: 500 - if: - condition: state entity_id: switch.lr_camera_privacy_mode state: 'on' then: - service: switch.turn_on data: {} target: entity_id: switch.lr_camera_status_led else: - service: switch.turn_off data: {} target: entity_id: switch.lr_camera_status_led mode: single - id: '1632442030624' alias: Br Turn off bedroom fireplace at 3:00 AM description: '' trigger: - platform: time at: 03:00:00 condition: - condition: state entity_id: switch.br_fireplace state: 'on' action: - service: switch.turn_off data: {} target: entity_id: switch.br_fireplace mode: single - id: '1634356553136' alias: Kit Motion detected in litter box description: '' trigger: - platform: state entity_id: binary_sensor.kit_litter_sensor_zone from: 'off' to: 'on' condition: [] action: - service: notify.mobile_app_juans_iphone data: message: Einstein is using the litter box data: group: cat-litter-box - service: notify.mobile_app_antonettes_iphone data: message: Einstein is using the litter box data: group: cat-litter-box mode: single - id: '1643825385976' alias: Home Assistant Backups Issue Notification description: Send a mobile notification when there is an issue with the automatic backups from the HA Google Drive backups add-on trigger: - platform: state entity_id: binary_sensor.backups_stale from: 'off' to: 'on' condition: [] action: - service: notify.mobile_app_juans_iphone data: title: There is an issue with the backups message: Access the Home Assistant Google Drive Backup add-on for details data: url: /cebe7a76_hassio_google_drive_backup/dashboard group: backups-alerts mode: single - id: '1643863189258' alias: Br Cam Privacy Mode description: '' trigger: - platform: state entity_id: - switch.br_camera_privacy_mode to: 'on' - platform: state entity_id: - switch.br_camera_privacy_mode to: 'off' condition: [] action: - delay: hours: 0 minutes: 0 seconds: 0 milliseconds: 500 - if: - condition: state entity_id: switch.br_camera_privacy_mode state: 'on' then: - service: switch.turn_on data: {} target: entity_id: switch.br_camera_status_led else: - service: switch.turn_off data: {} target: entity_id: switch.br_camera_status_led mode: single - id: '1643863274039' alias: Kit Cam Privacy Mode description: '' trigger: - platform: state entity_id: switch.kit_camera_privacy_mode to: 'on' - platform: state entity_id: switch.kit_camera_privacy_mode to: 'off' condition: [] action: - delay: hours: 0 minutes: 0 seconds: 0 milliseconds: 500 - if: - condition: state entity_id: switch.kit_camera_privacy_mode state: 'on' then: - service: switch.turn_on data: {} target: entity_id: switch.kit_camera_status_led else: - service: switch.turn_off data: {} target: entity_id: switch.kit_camera_status_led mode: single - id: '1644549482846' alias: Sensors Low Battery Notification description: '' trigger: - platform: numeric_state entity_id: - sensor.br_weather_sensor_battery - sensor.entrance_door_sensor_battery - sensor.lr_door_sensor_battery - sensor.lr_weather_sensor_battery - sensor.kit_weather_sensor_battery - sensor.ofc_switch_battery - sensor.lr_switch_battery - sensor.kit_litter_sensor_battery - sensor.br_switch_battery below: '10' condition: [] action: - service: notify.mobile_app_juans_iphone data: message: '{{ trigger.from_state.attributes.friendly_name }} sensor is low in battery' title: Low Battery alert data: group: sensors_battery_alert mode: single - id: '1647454398605' alias: Notify when there is smoke or CO detected description: '' trigger: - platform: state entity_id: - binary_sensor.lr_smoke_detected - binary_sensor.br_smoke_detected - binary_sensor.lr_co_detected - binary_sensor.br_co_detected from: 'off' to: 'on' condition: [] action: - choose: - conditions: - condition: state entity_id: binary_sensor.lr_smoke_detected state: 'on' sequence: - service: camera.snapshot data: filename: /config/www/living_room_image_smoke_alarm.jpg target: entity_id: camera.lr_camera - service: notify.mobile_app_juans_iphone data: message: Smoke detected in the living room title: Smoke Alert data: image: /local/living_room_image_smoke_alarm.jpg url: shortcuts://run-shortcut?name=UniFiProtect group: smoke-alarm - conditions: - condition: state entity_id: binary_sensor.br_smoke_detected state: 'on' sequence: - service: camera.snapshot data: filename: /config/www/bedroom_image_smoke_alarm.jpg target: entity_id: camera.br_camera - service: notify.mobile_app_juans_iphone data: message: Smoke detected in the bedroom title: Smoke Alert data: image: /local/bedroom_image_smoke_alarm.jpg url: shortcuts://run-shortcut?name=UniFiProtect group: smoke-alarm - conditions: - condition: state entity_id: binary_sensor.lr_co_detected state: 'on' sequence: - service: camera.snapshot data: filename: /config/www/living_room_image_co_alarm.jpg target: entity_id: camera.lr_camera - service: notify.mobile_app_juans_iphone data: message: Carbon monoxide detected in the living room title: CO Alert data: image: /local/living_room_image_co_alarm.jpg url: shortcuts://run-shortcut?name=UniFiProtect group: co-alarm - conditions: - condition: state entity_id: binary_sensor.br_co_detected state: 'on' sequence: - service: camera.snapshot data: filename: /config/www/bedroom_image_co_alarm.jpg target: entity_id: camera.br_camera - service: notify.mobile_app_juans_iphone data: message: Carbon monoxide detected in the bedroom title: CO Alert data: image: /local/bedroom_image_co_alarm.jpg url: shortcuts://run-shortcut?name=UniFiProtect group: co-alarm default: [] mode: single - id: '1649984320476' alias: Notify when updates are available description: '' trigger: - platform: state entity_id: - update.home_assistant_core_update - update.home_assistant_operating_system_update - update.home_assistant_supervisor_update - update.home_assistant_google_drive_backup_update - update.studio_code_server_update - update.z_wave_js_update - update.samba_share_update - update.esphome_update from: 'off' to: 'on' condition: [] action: - service: notify.mobile_app_juans_iphone data: title: Update available data: group: updates message: "{% if is_state('update.home_assistant_core_update', 'on') %}\n H.A. Core Update {{ state_attr('update.home_assistant_core_update', 'latest_version') }} available\n{% elif is_state('update.home_assistant_operating_system_update', 'on') %}\n H.A. OS Update {{ state_attr('update.home_assistant_operating_system_update', 'latest_version') }} available\n{% elif is_state('update.home_assistant_supervisor_update', 'on') %}\n H.A. Supervisor Update {{ state_attr('update.home_assistant_supervisor_update', 'latest_version') }} available\n{% elif is_state('update.home_assistant_google_drive_backup_update', 'on') %}\n Google Drive Backup Update {{ state_attr('update.home_assistant_google_drive_backup_update', 'latest_version') }} available\n{% elif is_state('update.studio_code_server_update', 'on') %}\n Studio Code Server Update {{ state_attr('update.studio_code_server_update', 'latest_version') }} available\n{% elif is_state('update.z_wave_js_update', 'on') %}\n Z-Wave JS Update {{ state_attr('update.z_wave_js_update', 'latest_version') }} available\n{% elif is_state('update.samba_share_update', 'on') %}\n Samba Share Update {{ state_attr('update.samba_share_update', 'latest_version') }} available\n{% elif is_state('update.esphome_update', 'on') %}\n ESPHome Update {{ state_attr('update.esphome_update', 'latest_version') }} available\n{% endif %}" mode: single - id: '1649990587776' alias: Ofc Turn off monitor lights when Mac is idle description: '' trigger: - platform: state entity_id: binary_sensor.mac_studio_active attribute: Screensaver from: false to: true - platform: state entity_id: binary_sensor.mac_studio_active attribute: Screensaver from: true to: false condition: [] action: - choose: - conditions: - condition: state entity_id: binary_sensor.mac_studio_active state: true attribute: Screensaver - condition: state entity_id: light.ofc_monitor_lights state: 'on' sequence: - service: light.turn_off data: {} target: entity_id: light.ofc_monitor_lights - conditions: - condition: state entity_id: binary_sensor.mac_studio_active attribute: Screensaver state: false - condition: state entity_id: light.ofc_monitor_lights state: 'off' sequence: - service: light.turn_on data: {} target: entity_id: light.ofc_monitor_lights default: [] mode: single - id: '1651718902759' alias: Home Assistant Remote access description: '' trigger: - platform: state entity_id: - device_tracker.juans_iphone to: not_home - platform: state entity_id: - device_tracker.juans_iphone to: home condition: [] action: - if: - condition: state entity_id: device_tracker.juans_iphone state: not_home then: - service: cloud.remote_connect data: {} else: - service: cloud.remote_disconnect data: {} mode: single - id: '1651720688481' alias: 'Ofc Toggle automation: Turn off monitor lights when Mac is idle' description: '' trigger: - platform: state entity_id: - light.ofc_monitor_lights to: 'on' - platform: state entity_id: - light.ofc_monitor_lights - switch.mac_studio to: 'off' condition: [] action: - choose: - conditions: - condition: state entity_id: light.ofc_monitor_lights state: 'on' - condition: state entity_id: automation.turn_off_monitor_lights_when_mac_is_idle state: 'off' sequence: - service: automation.turn_on data: {} target: entity_id: automation.turn_off_monitor_lights_when_mac_is_idle - conditions: - condition: state entity_id: light.ofc_monitor_lights state: 'off' - condition: and conditions: - condition: state entity_id: switch.mac_studio state: 'off' sequence: - service: automation.turn_off data: {} target: entity_id: automation.turn_off_monitor_lights_when_mac_is_idle default: [] mode: single - id: '1652138178341' alias: Turn off cable boxes at 2AM if on description: '' trigger: - platform: time at: 02:00:00 condition: - condition: state entity_id: switch.all_cable_boxes state: 'on' action: - service: switch.turn_off data: {} target: entity_id: switch.all_cable_boxes mode: single - id: '1652220700080' alias: Air purifiers power on schedule 1AM description: '' trigger: - platform: time at: 01:00:00 condition: [] action: - choose: - conditions: - condition: state entity_id: switch.lr_air_purifier state: 'on' sequence: - service: switch.turn_off data: {} target: entity_id: switch.lr_air_purifier default: [] - choose: - conditions: - condition: state entity_id: switch.br_air_purifier state: 'off' sequence: - service: switch.turn_on data: {} target: entity_id: switch.br_air_purifier default: [] mode: single - id: '1652221595750' alias: Air purifiers power on schedule 8AM description: '' trigger: - platform: time at: 08:00:00 condition: [] action: - choose: - conditions: - condition: state entity_id: switch.br_air_purifier state: 'on' sequence: - service: switch.turn_off data: {} target: entity_id: switch.br_air_purifier default: [] - choose: - conditions: - condition: state entity_id: switch.lr_air_purifier state: 'off' sequence: - service: switch.turn_on data: {} target: entity_id: switch.lr_air_purifier default: [] mode: single - id: '1652839830104' alias: 'Ofc Tag 1: Toggle monitor lights' description: '' trigger: - platform: event event_type: monitor_lights condition: [] action: - service: light.toggle data: {} target: entity_id: light.ofc_monitor_lights mode: single - id: '1652839887141' alias: 'Ofc Tag 2: Toggle desk light' description: '' trigger: - platform: event event_type: desk_light condition: [] action: - service: light.toggle data: {} target: entity_id: light.ofc_desk_light mode: single - id: '1652839998997' alias: 'Kit Tag 3: Toggle kitchen cable box plug' description: '' trigger: - platform: event event_type: kit_cable_box_plug_toggle condition: [] action: - service: switch.toggle data: {} target: entity_id: switch.kit_cable_box_plug mode: single - id: '1652840047433' alias: 'Lr Tag 4: Toggle living room cable box plug' description: '' trigger: - platform: event event_type: lr_cable_box_plug_toggle condition: [] action: - service: switch.toggle data: {} target: entity_id: switch.lr_cable_box_plug mode: single - id: '1652840074185' alias: 'Br Tag 5: Toggle bedroom cable box plug' description: '' trigger: - platform: event event_type: br_cable_box_plug_toggle condition: [] action: - service: switch.toggle data: {} target: entity_id: switch.br_cable_box_plug mode: single - id: '1657847851936' alias: Enable recording on cameras after privacy mode disable description: '' trigger: - platform: state entity_id: - switch.lr_camera_privacy_mode - switch.br_camera_privacy_mode - switch.kit_camera_privacy_mode to: 'off' condition: [] action: - delay: hours: 0 minutes: 1 seconds: 0 milliseconds: 0 - choose: - conditions: - condition: state entity_id: switch.lr_camera_privacy_mode state: 'off' sequence: - service: select.select_option data: option: Always target: entity_id: select.lr_camera_recording_mode - conditions: - condition: state entity_id: switch.br_camera_privacy_mode state: 'off' sequence: - service: select.select_option data: option: Always target: entity_id: select.br_camera_recording_mode - conditions: - condition: state entity_id: switch.kit_camera_privacy_mode state: 'off' sequence: - service: select.select_option data: option: Always target: entity_id: select.kit_camera_recording_mode default: [] mode: single - id: '1657898788107' alias: Br light toggle description: '' trigger: - device_id: 665cf85a1d2acea4db834 domain: zha platform: device type: remote_button_short_press subtype: remote_button_short_press condition: [] action: - service: light.toggle data: {} target: entity_id: light.br_light mode: single - id: '1659476740186' alias: Br Fireplace toggle with bedroom switch description: '' trigger: - device_id: 665cf85a1d2acea4db834 domain: zha platform: device type: remote_button_double_press subtype: remote_button_double_press condition: [] action: - service: switch.toggle target: entity_id: switch.br_fireplace data: {} mode: single - id: '1659476820610' alias: Br Toggle bedroom AC/Fan with bedroom switch description: '' trigger: - device_id: 665cf85a1d2acea4db834 domain: zha platform: device type: remote_button_long_press subtype: remote_button_long_press condition: [] action: - service: script.br_air_conditioner data: {} - service: script.br_fan data: {} enabled: false mode: single - id: '1659489805267' alias: Notify when entrance lock is left unlocked for over 5 minutes description: '' trigger: - platform: state entity_id: - lock.entrance_door_lock to: unlocked for: hours: 0 minutes: 5 seconds: 0 condition: [] action: - if: - condition: state entity_id: lock.entrance_door_lock state: unlocked for: hours: 0 minutes: 0 seconds: 0 then: - service: notify.mobile_app_juans_iphone data: title: Entrance Unlocked message: The entrance door was left unlocked data: actions: - action: lock title: Lock door mode: single - id: '1659491009388' alias: Lock entrance door with notification event description: '' trigger: - platform: event event_type: mobile_app_notification_action event_data: action: lock condition: [] action: - service: lock.lock data: {} target: entity_id: lock.entrance_door_lock mode: single ================================================ FILE: cloud_config.yaml ================================================ google_actions: entity_config: script.lr_air_conditioner: room: Living room aliases: - Living room air conditioner - Living room air - Air - AC - Air conditioner script.br_air_conditioner: room: Bedroom aliases: - Bedroom air conditioner - Bedroom air - Air - AC - Air conditioner switch.lr_air_purifier_plug: aliases: - Purifier - Air purifier - AP ================================================ FILE: configuration.yaml ================================================ # Configure a default setup of Home Assistant (frontend, api, etc) default_config: frontend: themes: !include_dir_merge_named themes tts: - platform: google_translate sun: cloud: !include cloud_config.yaml scene: !include scenes.yaml group: !include groups.yaml script: !include scripts.yaml switch: !include_dir_list switches stream: automation: !include automations.yaml wake_on_lan: ================================================ FILE: custom_components/alarmo/__init__.py ================================================ """The Alarmo Integration.""" import logging import bcrypt import base64 from homeassistant.core import ( callback, ) from homeassistant.components.alarm_control_panel import DOMAIN as PLATFORM from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( ATTR_CODE, ATTR_NAME, ) from homeassistant.core import HomeAssistant, asyncio from homeassistant.helpers import device_registry as dr from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) from homeassistant.helpers.service import ( async_register_admin_service, ) from . import const from .store import async_get_registry from .panel import ( async_register_panel, async_unregister_panel, ) from .card import async_register_card from .websockets import async_register_websockets from .sensors import ( SensorHandler, ATTR_GROUP, ATTR_ENTITIES ) from .automations import AutomationHandler from .mqtt import MqttHandler from .event import EventHandler _LOGGER = logging.getLogger(__name__) async def async_setup(hass, config): """Track states and offer events for sensors.""" return True async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry): """Set up Alarmo integration from a config entry.""" session = async_get_clientsession(hass) store = await async_get_registry(hass) coordinator = AlarmoCoordinator(hass, session, entry, store) device_registry = dr.async_get(hass) device_registry.async_get_or_create( config_entry_id=entry.entry_id, identifiers={(const.DOMAIN, coordinator.id)}, name=const.NAME, model=const.NAME, sw_version=const.VERSION, manufacturer=const.MANUFACTURER, ) hass.data.setdefault(const.DOMAIN, {}) hass.data[const.DOMAIN] = { "coordinator": coordinator, "areas": {}, "master": None } if entry.unique_id is None: hass.config_entries.async_update_entry(entry, unique_id=coordinator.id, data={}) hass.async_create_task( hass.config_entries.async_forward_entry_setup(entry, PLATFORM) ) # Register the panel (frontend) await async_register_panel(hass) await async_register_card(hass) # Websocket support await async_register_websockets(hass) # Register custom services register_services(hass) return True async def async_unload_entry(hass, entry): """Unload Alarmo config entry.""" unload_ok = all( await asyncio.gather( *[hass.config_entries.async_forward_entry_unload(entry, PLATFORM)] ) ) if not unload_ok: return False async_unregister_panel(hass) coordinator = hass.data[const.DOMAIN]["coordinator"] await coordinator.async_unload() return True async def async_remove_entry(hass, entry): """Remove Alarmo config entry.""" async_unregister_panel(hass) coordinator = hass.data[const.DOMAIN]["coordinator"] await coordinator.async_delete_config() del hass.data[const.DOMAIN] class AlarmoCoordinator(DataUpdateCoordinator): """Define an object to hold Alarmo device.""" def __init__(self, hass, session, entry, store): """Initialize.""" self.id = entry.unique_id self.hass = hass self.entry = entry self.store = store self._subscriptions = [] self._subscriptions.append( async_dispatcher_connect( hass, "alarmo_platform_loaded", self.setup_alarm_entities ) ) self.register_events() super().__init__(hass, _LOGGER, name=const.DOMAIN) @callback def setup_alarm_entities(self): self.hass.data[const.DOMAIN]["sensor_handler"] = SensorHandler(self.hass) self.hass.data[const.DOMAIN]["automation_handler"] = AutomationHandler(self.hass) self.hass.data[const.DOMAIN]["mqtt_handler"] = MqttHandler(self.hass) self.hass.data[const.DOMAIN]["event_handler"] = EventHandler(self.hass) areas = self.store.async_get_areas() config = self.store.async_get_config() for item in areas.values(): async_dispatcher_send(self.hass, "alarmo_register_entity", item) if len(areas) > 1 and config["master"]["enabled"]: async_dispatcher_send(self.hass, "alarmo_register_master", config["master"]) async def async_update_config(self, data): if "master" in data: old_config = self.store.async_get_config() if old_config[const.ATTR_MASTER] != data["master"]: if self.hass.data[const.DOMAIN]["master"]: await self.async_remove_entity("master") if data["master"]["enabled"]: async_dispatcher_send(self.hass, "alarmo_register_master", data["master"]) else: automations = self.hass.data[const.DOMAIN]["automation_handler"].get_automations_by_area(None) if len(automations): for el in automations: self.store.async_delete_automation(el) async_dispatcher_send(self.hass, "alarmo_automations_updated") self.store.async_update_config(data) async_dispatcher_send(self.hass, "alarmo_config_updated") async def async_update_area_config(self, area_id: str = None, data: dict = {}): if const.ATTR_REMOVE in data: # delete an area res = self.store.async_get_area(area_id) if not res: return sensors = self.store.async_get_sensors() sensors = dict(filter(lambda el: el[1]["area"] == area_id, sensors.items())) if sensors: for el in sensors.keys(): self.store.async_delete_sensor(el) async_dispatcher_send(self.hass, "alarmo_sensors_updated") automations = self.hass.data[const.DOMAIN]["automation_handler"].get_automations_by_area(area_id) if len(automations): for el in automations: self.store.async_delete_automation(el) async_dispatcher_send(self.hass, "alarmo_automations_updated") self.store.async_delete_area(area_id) await self.async_remove_entity(area_id) if len(self.store.async_get_areas()) == 1 and self.hass.data[const.DOMAIN]["master"]: await self.async_remove_entity("master") elif self.store.async_get_area(area_id): # modify an area entry = self.store.async_update_area(area_id, data) if "name" not in data: async_dispatcher_send(self.hass, "alarmo_config_updated", area_id) else: await self.async_remove_entity(area_id) async_dispatcher_send(self.hass, "alarmo_register_entity", entry) else: # create an area entry = self.store.async_create_area(data) async_dispatcher_send(self.hass, "alarmo_register_entity", entry) config = self.store.async_get_config() if len(self.store.async_get_areas()) == 2 and config["master"]["enabled"]: async_dispatcher_send(self.hass, "alarmo_register_master", config["master"]) def async_update_sensor_config(self, entity_id: str, data: dict): group = None if ATTR_GROUP in data: group = data[ATTR_GROUP] del data[ATTR_GROUP] if const.ATTR_REMOVE in data: self.store.async_delete_sensor(entity_id) self.assign_sensor_to_group(entity_id, None) elif self.store.async_get_sensor(entity_id): self.store.async_update_sensor(entity_id, data) self.assign_sensor_to_group(entity_id, group) else: self.store.async_create_sensor(entity_id, data) self.assign_sensor_to_group(entity_id, group) async_dispatcher_send(self.hass, "alarmo_sensors_updated") def async_update_user_config(self, user_id: str = None, data: dict = {}): if const.ATTR_REMOVE in data: self.store.async_delete_user(user_id) return if ATTR_CODE in data and data[ATTR_CODE]: data[const.ATTR_CODE_FORMAT] = "number" if data[ATTR_CODE].isdigit() else "text" data[const.ATTR_CODE_LENGTH] = len(data[ATTR_CODE]) hashed = bcrypt.hashpw( data[ATTR_CODE].encode("utf-8"), bcrypt.gensalt(rounds=12) ) hashed = base64.b64encode(hashed) data[ATTR_CODE] = hashed.decode() if not user_id: self.store.async_create_user(data) else: if ATTR_CODE in data: if const.ATTR_OLD_CODE not in data: return False elif not self.async_authenticate_user(data[const.ATTR_OLD_CODE], user_id): return False else: del data[const.ATTR_OLD_CODE] self.store.async_update_user(user_id, data) else: self.store.async_update_user(user_id, data) def async_authenticate_user(self, code: str, user_id: str = None): if not user_id: users = self.store.async_get_users() else: users = { user_id: self.store.async_get_user(user_id) } for (user_id, user) in users.items(): if not user[const.ATTR_ENABLED]: continue elif not user[ATTR_CODE] and not code: return user elif user[ATTR_CODE]: hash = base64.b64decode(user[ATTR_CODE]) if bcrypt.checkpw(code.encode("utf-8"), hash): return user return def async_update_automation_config(self, automation_id: str = None, data: dict = {}): if const.ATTR_REMOVE in data: self.store.async_delete_automation(automation_id) elif not automation_id: self.store.async_create_automation(data) else: self.store.async_update_automation(automation_id, data) async_dispatcher_send(self.hass, "alarmo_automations_updated") def register_events(self): # handle push notifications with action buttons @callback async def async_handle_push_event(event): if not event.data: return action = event.data.get("actionName") if "actionName" in event.data else event.data.get("action") if action not in [ const.EVENT_ACTION_FORCE_ARM, const.EVENT_ACTION_RETRY_ARM, const.EVENT_ACTION_DISARM ]: return if self.hass.data[const.DOMAIN]["master"]: alarm_entity = self.hass.data[const.DOMAIN]["master"] elif len(self.hass.data[const.DOMAIN]["areas"]) == 1: alarm_entity = list(self.hass.data[const.DOMAIN]["areas"].values())[0] else: _LOGGER.info("Cannot process the push action, since there are multiple areas.") return arm_mode = alarm_entity._arm_mode if not arm_mode: _LOGGER.info("Cannot process the push action, since the arm mode is not known.") return if action == const.EVENT_ACTION_FORCE_ARM: _LOGGER.info("Received request for force arming") await alarm_entity.async_handle_arm_request(arm_mode, skip_code=True, bypass_open_sensors=True) elif action == const.EVENT_ACTION_RETRY_ARM: _LOGGER.info("Received request for retry arming") await alarm_entity.async_handle_arm_request(arm_mode, skip_code=True) elif action == const.EVENT_ACTION_DISARM: _LOGGER.info("Received request for disarming") await alarm_entity.async_alarm_disarm(code=None, skip_code=True) self._subscriptions.append( self.hass.bus.async_listen(const.PUSH_EVENT, async_handle_push_event) ) async def async_remove_entity(self, area_id: str): entity_registry = self.hass.helpers.entity_registry.async_get(self.hass) if area_id == "master": entity = self.hass.data[const.DOMAIN]["master"] entity_registry.async_remove(entity.entity_id) self.hass.data[const.DOMAIN]["master"] = None else: entity = self.hass.data[const.DOMAIN]["areas"][area_id] entity_registry.async_remove(entity.entity_id) self.hass.data[const.DOMAIN]["areas"].pop(area_id, None) def async_get_sensor_groups(self): """fetch a list of sensor groups (websocket API hook)""" groups = self.store.async_get_sensor_groups() return list(groups.values()) def async_get_group_for_sensor(self, entity_id: str): groups = self.async_get_sensor_groups() result = next((el for el in groups if entity_id in el[ATTR_ENTITIES]), None) return result["group_id"] if result else None def assign_sensor_to_group(self, entity_id: str, group_id: str): old_group = self.async_get_group_for_sensor(entity_id) if old_group and group_id != old_group: # remove sensor from group el = self.store.async_get_sensor_group(old_group) if len(el[ATTR_ENTITIES]) > 2: self.store.async_update_sensor_group(old_group, { ATTR_ENTITIES: [x for x in el[ATTR_ENTITIES] if x != entity_id] }) else: self.store.async_delete_sensor_group(old_group) if group_id: # add sensor to group el = self.store.async_get_sensor_group(group_id) if not el: _LOGGER.error("Failed to assign entity {} to group {}".format(entity_id, group_id)) return self.store.async_update_sensor_group(group_id, { ATTR_ENTITIES: el[ATTR_ENTITIES] + [entity_id] }) async_dispatcher_send(self.hass, "alarmo_sensors_updated") def async_update_sensor_group_config(self, group_id: str = None, data: dict = {}): if const.ATTR_REMOVE in data: self.store.async_delete_sensor_group(group_id) elif not group_id: self.store.async_create_sensor_group(data) else: self.store.async_update_sensor_group(group_id, data) async_dispatcher_send(self.hass, "alarmo_sensors_updated") async def async_unload(self): """remove all alarmo objects""" # remove alarm_control_panel entities areas = list(self.hass.data[const.DOMAIN]["areas"].keys()) for area in areas: await self.async_remove_entity(area) if self.hass.data[const.DOMAIN]["master"]: await self.async_remove_entity("master") del self.hass.data[const.DOMAIN]["sensor_handler"] del self.hass.data[const.DOMAIN]["automation_handler"] del self.hass.data[const.DOMAIN]["mqtt_handler"] del self.hass.data[const.DOMAIN]["event_handler"] # remove subscriptions for coordinator while len(self._subscriptions): self._subscriptions.pop()() async def async_delete_config(self): """wipe alarmo storage""" await self.store.async_delete() @callback def register_services(hass): """Register services used by alarmo component.""" coordinator = hass.data[const.DOMAIN]["coordinator"] async def async_srv_toggle_user(call): """Enable a user by service call""" name = call.data.get(ATTR_NAME) enable = True if call.service == const.SERVICE_ENABLE_USER else False users = coordinator.store.async_get_users() user = next((item for item in list(users.values()) if item[ATTR_NAME] == name), None) if user is None: _LOGGER.warning("Failed to {} user, no match for name '{}'".format("enable" if enable else "disable", name)) return coordinator.store.async_update_user(user[const.ATTR_USER_ID], {const.ATTR_ENABLED: enable}) _LOGGER.debug("User user '{}' was {}".format(name, "enabled" if enable else "disabled")) async_register_admin_service( hass, const.DOMAIN, const.SERVICE_ENABLE_USER, async_srv_toggle_user, schema=const.SERVICE_TOGGLE_USER_SCHEMA ) async_register_admin_service( hass, const.DOMAIN, const.SERVICE_DISABLE_USER, async_srv_toggle_user, schema=const.SERVICE_TOGGLE_USER_SCHEMA ) ================================================ FILE: custom_components/alarmo/alarm_control_panel.py ================================================ """Initialization of Alarmo alarm_control_panel platform.""" import datetime import logging import functools import operator # from homeassistant.components.alarm_control_panel import DOMAIN as PLATFORM from homeassistant.core import ( HomeAssistant, callback, ) from homeassistant.helpers.event import ( async_track_point_in_time, async_call_later, ) from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) from homeassistant.helpers import entity_platform from homeassistant.helpers.restore_state import RestoreEntity from homeassistant.util import slugify import homeassistant.util.dt as dt_util from homeassistant.components.alarm_control_panel import ( AlarmControlPanelEntity, ATTR_CODE_ARM_REQUIRED, DOMAIN as PLATFORM, AlarmControlPanelEntityFeature, ) from homeassistant.const import ( ATTR_CODE_FORMAT, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, STATE_ALARM_ARMING, ATTR_NAME, ) from . import const _LOGGER = logging.getLogger(__name__) async def async_setup(hass, config): """Track states and offer events for alarm_control_panel.""" return True async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the platform from config.""" return True async def async_setup_entry(hass, config_entry, async_add_devices): """Set up the Alarmo entities. """ @callback def async_add_alarm_entity(config: dict): """Add each entity as Alarm Control Panel.""" entity_id = "{}.{}".format(PLATFORM, slugify(config["name"])) alarm_entity = AlarmoAreaEntity( hass=hass, entity_id=entity_id, name=config["name"], area_id=config["area_id"], ) hass.data[const.DOMAIN]["areas"][config["area_id"]] = alarm_entity async_add_devices([alarm_entity]) async_dispatcher_connect(hass, "alarmo_register_entity", async_add_alarm_entity) @callback def async_add_alarm_master(config: dict): """Add each entity as Alarm Control Panel.""" entity_id = "{}.{}".format(PLATFORM, slugify(config["name"])) alarm_entity = AlarmoMasterEntity( hass=hass, entity_id=entity_id, name=config["name"], ) hass.data[const.DOMAIN]["master"] = alarm_entity async_add_devices([alarm_entity]) async_dispatcher_connect(hass, "alarmo_register_master", async_add_alarm_master) async_dispatcher_send(hass, "alarmo_platform_loaded") # Register services platform = entity_platform.current_platform.get() platform.async_register_entity_service( const.SERVICE_ARM, const.SERVICE_ARM_SCHEMA, "async_service_arm_handler", ) platform.async_register_entity_service( const.SERVICE_DISARM, const.SERVICE_DISARM_SCHEMA, "async_service_disarm_handler", ) class AlarmoBaseEntity(AlarmControlPanelEntity, RestoreEntity): def __init__(self, hass: HomeAssistant, name: str, entity_id: str) -> None: """Initialize the alarm_control_panel entity.""" self.entity_id = entity_id self._name = name self._state = None self.hass = hass self._config = {} self._arm_mode = None self._changed_by = None self._open_sensors = {} self._bypassed_sensors = [] self._delay = None self.expiration = None self.area_id = None self._revert_state = None @property def device_info(self) -> dict: """Return info for device registry.""" return { "identifiers": {(const.DOMAIN, self.hass.data[const.DOMAIN]["coordinator"].id)}, "name": const.NAME, "model": const.NAME, "sw_version": const.VERSION, "manufacturer": const.MANUFACTURER, } @property def unique_id(self): """Return a unique ID to use for this entity.""" return f"{self.entity_id}" @property def icon(self): """Return icon.""" return "mdi:shield-home" @property def name(self): """Return the friendly name to use for this entity.""" return self._name @property def should_poll(self) -> bool: """Return the polling state.""" return False @property def code_format(self): """Return whether code consists of digits or characters.""" if ( self._state == STATE_ALARM_DISARMED and self.code_arm_required ): return self._config[ATTR_CODE_FORMAT] elif ( self._state != STATE_ALARM_DISARMED and self._config and const.ATTR_CODE_DISARM_REQUIRED in self._config and self._config[const.ATTR_CODE_DISARM_REQUIRED] ): return self._config[ATTR_CODE_FORMAT] else: return None @property def changed_by(self): """Last change triggered by.""" return self._changed_by @property def state(self): """Return the state of the device.""" return self._state @property def supported_features(self) -> int: """Return the list of supported features.""" return 0 @property def code_arm_required(self): """Whether the code is required for arm actions.""" if not self._config or ATTR_CODE_ARM_REQUIRED not in self._config: return True # assume code is needed (conservative approach) else: return self._config[ATTR_CODE_ARM_REQUIRED] @property def arm_mode(self): """Return the arm mode.""" return self._arm_mode if self._state != STATE_ALARM_DISARMED else None @property def open_sensors(self): """Get open sensors.""" if not self._open_sensors: return None else: return self._open_sensors @open_sensors.setter def open_sensors(self, value): """Set open_sensors sensors.""" if type(value) is dict: self._open_sensors = value else: self._open_sensors = {} @property def bypassed_sensors(self): """Get bypassed sensors.""" if not self._bypassed_sensors: return None else: return self._bypassed_sensors @bypassed_sensors.setter def bypassed_sensors(self, value): """Set bypassed sensors.""" if type(value) is list: self._bypassed_sensors = value elif not value: self._bypassed_sensors = None @property def delay(self): """Get delay.""" return self._delay @delay.setter def delay(self, value): """Set delay.""" if type(value) is int: self._delay = value self.expiration = (dt_util.utcnow() + datetime.timedelta(seconds=value)).replace(microsecond=0) else: self._delay = None self.expiration = None @property def extra_state_attributes(self): """Return the data of the entity.""" return { "arm_mode": self.arm_mode, "open_sensors": self.open_sensors, "bypassed_sensors": self.bypassed_sensors, "delay": self.delay, } def _validate_code(self, code, state): """Validate given code.""" if state == STATE_ALARM_DISARMED and not self._config[const.ATTR_CODE_DISARM_REQUIRED]: self._changed_by = None return (True, None) elif state != STATE_ALARM_DISARMED and not self._config[ATTR_CODE_ARM_REQUIRED]: self._changed_by = None return (True, None) elif not code or len(code) < 1: return (False, const.EVENT_NO_CODE_PROVIDED) res = self.hass.data[const.DOMAIN]["coordinator"].async_authenticate_user(code) if not res: # wrong code was entered return (False, const.EVENT_INVALID_CODE_PROVIDED) elif ( res[const.ATTR_AREA_LIMIT] and not all(area in res[const.ATTR_AREA_LIMIT] for area in ([self.area_id] if self.area_id else list(self.hass.data[const.DOMAIN]["areas"].keys()))) ): # user is not allowed to operate this area _LOGGER.debug("User {} has no permission to arm/disarm this area.".format(res[ATTR_NAME])) return (False, const.EVENT_INVALID_CODE_PROVIDED) elif state == STATE_ALARM_DISARMED and not res["can_disarm"]: # user is not allowed to disarm the alarm _LOGGER.debug("User {} has no permission to disarm the alarm.".format(res[ATTR_NAME])) return (False, const.EVENT_INVALID_CODE_PROVIDED) elif state in const.ARM_MODES and not res["can_arm"]: # user is not allowed to arm the alarm _LOGGER.debug("User {} has no permission to arm the alarm.".format(res[ATTR_NAME])) return (False, const.EVENT_INVALID_CODE_PROVIDED) else: self._changed_by = res[ATTR_NAME] return (True, res) async def async_service_disarm_handler(self, code, context_id=None): """handle external disarm request from alarmo.disarm service""" _LOGGER.debug("Service alarmo.disarm was called") await self.async_alarm_disarm( code=code, context_id=context_id ) async def async_alarm_disarm(self, **kwargs): """Send disarm command.""" _LOGGER.debug("alarm_disarm") code = kwargs.get("code", None) skip_code = kwargs.get("skip_code", False) context_id = kwargs.get("context_id", None) if self._state == STATE_ALARM_DISARMED or not self._config: if not self._config: _LOGGER.warning("Cannot process disarm command, alarm is not initialized yet.") else: _LOGGER.warning("Cannot go to state {} from state {}.".format(STATE_ALARM_DISARMED, self._state)) async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_COMMAND_NOT_ALLOWED, self.area_id, { "state": self._state, "command": const.COMMAND_DISARM, const.ATTR_CONTEXT_ID: context_id } ) return (res, info) = self._validate_code(code, STATE_ALARM_DISARMED) if not res and not skip_code: async_dispatcher_send(self.hass, "alarmo_event", info, self.area_id, { const.ATTR_CONTEXT_ID: context_id, "command": const.COMMAND_DISARM, }) _LOGGER.warning("Wrong code provided.") return else: self.open_sensors = None self.bypassed_sensors = None await self.async_update_state(STATE_ALARM_DISARMED) if self.changed_by: _LOGGER.info("Alarm '{}' is disarmed by {}.".format(self.name, self.changed_by)) else: _LOGGER.info("Alarm '{}' is disarmed.".format(self.name)) async_dispatcher_send(self.hass, "alarmo_event", const.EVENT_DISARM, self.area_id, { const.ATTR_CONTEXT_ID: context_id }) return True async def async_service_arm_handler(self, code, mode, skip_delay, force, context_id=None): """handle external arm request from alarmo.arm service""" _LOGGER.debug("Service alarmo.arm was called") if mode in const.ARM_MODE_TO_STATE: mode = const.ARM_MODE_TO_STATE[mode] await self.async_handle_arm_request( mode, code=code, skip_delay=skip_delay, bypass_open_sensors=force, context_id=context_id ) async def async_handle_arm_request(self, arm_mode, **kwargs): """check if conditions are met for starting arm procedure""" code = kwargs.get(const.CONF_CODE, "") skip_code = kwargs.get("skip_code", False) skip_delay = kwargs.get(const.ATTR_SKIP_DELAY, False) bypass_open_sensors = kwargs.get("bypass_open_sensors", False) context_id = kwargs.get("context_id", None) if ( not (const.MODES_TO_SUPPORTED_FEATURES[arm_mode] & self.supported_features) or (self._state != STATE_ALARM_DISARMED and self._state not in const.ARM_MODES) or not self._config ): if not self._config or not self._state: _LOGGER.warning("Cannot process arm command, alarm is not initialized yet.") elif not (const.MODES_TO_SUPPORTED_FEATURES[arm_mode] & self.supported_features): _LOGGER.warning("Mode {} is not supported, ignoring.".format(arm_mode)) else: _LOGGER.warning("Cannot go to state {} from state {}.".format(arm_mode, self._state)) async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_COMMAND_NOT_ALLOWED, self.area_id, { "state": self._state, "command": arm_mode.replace("armed", "arm"), const.ATTR_CONTEXT_ID: context_id } ) return False elif self._state in const.ARM_MODES and self._arm_mode == arm_mode: _LOGGER.debug("Alarm is already set to {}, ignoring command.".format(arm_mode)) return False if not skip_code: (res, info) = self._validate_code(code, arm_mode) if not res: async_dispatcher_send(self.hass, "alarmo_event", info, self.area_id, { "command": arm_mode.replace("armed", "arm"), const.ATTR_CONTEXT_ID: context_id, }) _LOGGER.warning("Wrong code provided.") if self.open_sensors: self.open_sensors = None self.async_write_ha_state() return False elif info and info[const.ATTR_IS_OVERRIDE_CODE]: bypass_open_sensors = True else: self._changed_by = None if self._state in const.ARM_MODES: # we are switching between arm modes self._revert_state = self._state else: self._revert_state = STATE_ALARM_DISARMED self.open_sensors = None self.bypassed_sensors = None await self.async_arm( arm_mode, skip_delay=skip_delay, bypass_open_sensors=bypass_open_sensors, context_id=context_id ) async def async_alarm_arm_away(self, code=None, skip_code=False): """Send arm away command.""" _LOGGER.debug("alarm_arm_away") await self.async_handle_arm_request(STATE_ALARM_ARMED_AWAY, code=code, skip_code=skip_code) async def async_alarm_arm_home(self, code=None, skip_code=False): """Send arm home command.""" _LOGGER.debug("alarm_arm_home") await self.async_handle_arm_request(STATE_ALARM_ARMED_HOME, code=code, skip_code=skip_code) async def async_alarm_arm_night(self, code=None, skip_code=False): """Send arm night command.""" _LOGGER.debug("alarm_arm_night") await self.async_handle_arm_request(STATE_ALARM_ARMED_NIGHT, code=code, skip_code=skip_code) async def async_alarm_arm_custom_bypass(self, code=None, skip_code=False): """Send arm custom_bypass command.""" _LOGGER.debug("alarm_arm_custom_bypass") await self.async_handle_arm_request(STATE_ALARM_ARMED_CUSTOM_BYPASS, code=code, skip_code=skip_code) async def async_alarm_arm_vacation(self, code=None, skip_code=False): """Send arm vacation command.""" _LOGGER.debug("alarm_arm_vacation") await self.async_handle_arm_request(STATE_ALARM_ARMED_VACATION, code=code, skip_code=skip_code) async def async_alarm_trigger(self, code=None) -> None: """Send alarm trigger command.""" _LOGGER.debug("async_alarm_trigger") await self.async_trigger(skip_delay=False) async def async_added_to_hass(self): """Connect to dispatcher listening for entity data notifications.""" _LOGGER.debug("{} is added to hass".format(self.entity_id)) await super().async_added_to_hass() state = await self.async_get_last_state() # restore previous state if state: # restore attributes if "arm_mode" in state.attributes: self._arm_mode = state.attributes["arm_mode"] if "changed_by" in state.attributes: self._changed_by = state.attributes["changed_by"] if "open_sensors" in state.attributes: self.open_sensors = state.attributes["open_sensors"] if "bypassed_sensors" in state.attributes: self._bypassed_sensors = state.attributes["bypassed_sensors"] async def async_will_remove_from_hass(self): await super().async_will_remove_from_hass() _LOGGER.debug("{} is removed from hass".format(self.entity_id)) class AlarmoAreaEntity(AlarmoBaseEntity): """Defines a base alarm_control_panel entity.""" def __init__(self, hass: HomeAssistant, name: str, entity_id: str, area_id: str) -> None: """Initialize the alarm_control_panel entity.""" super().__init__(hass, name, entity_id) self.area_id = area_id self._timer = None coordinator = self.hass.data[const.DOMAIN]["coordinator"] self._config = coordinator.store.async_get_config() self._config.update(coordinator.store.async_get_area(self.area_id)) @property def supported_features(self) -> int: """Return the list of supported features.""" if not self._config or const.ATTR_MODES not in self._config: return 0 else: supported_features = AlarmControlPanelEntityFeature.TRIGGER for (mode, mode_config) in self._config[const.ATTR_MODES].items(): if mode_config[const.ATTR_ENABLED]: supported_features = supported_features | const.MODES_TO_SUPPORTED_FEATURES[mode] return supported_features async def async_added_to_hass(self): """Connect to dispatcher listening for entity data notifications.""" await super().async_added_to_hass() # make sure that the config is reloaded on changes @callback async def async_update_config(area_id: str = None): _LOGGER.debug("async_update_config") coordinator = self.hass.data[const.DOMAIN]["coordinator"] self._config = coordinator.store.async_get_config() self._config.update(coordinator.store.async_get_area(self.area_id)) self.async_write_ha_state() self.async_on_remove( async_dispatcher_connect(self.hass, "alarmo_config_updated", async_update_config) ) # restore previous state state = await self.async_get_last_state() if state: initial_state = state.state _LOGGER.debug("Initial state for {} is {}".format(self.entity_id, initial_state)) if initial_state == STATE_ALARM_ARMING: await self.async_arm(self.arm_mode) elif initial_state == STATE_ALARM_PENDING: await self.async_trigger() elif initial_state == STATE_ALARM_TRIGGERED: await self.async_trigger(skip_delay=True) else: await self.async_update_state(initial_state) else: await self.async_update_state(STATE_ALARM_DISARMED) self.async_write_ha_state() async def async_update_state(self, state: str = None): """update the state or refresh state attributes""" if state == self._state: return old_state = self._state self._state = state _LOGGER.debug("entity {} was updated from {} to {}".format(self.entity_id, old_state, state)) if self._timer: self._timer() if self.state not in [STATE_ALARM_ARMING, STATE_ALARM_PENDING]: self.delay = None if state in const.ARM_MODES: self._arm_mode = state elif old_state == STATE_ALARM_DISARMED and state == STATE_ALARM_TRIGGERED: self._arm_mode = None async_dispatcher_send(self.hass, "alarmo_state_updated", self.area_id, old_state, state) self.async_write_ha_state() async def async_arm_failure(self, open_sensors: dict, context_id=None): """handle arm failure.""" self._open_sensors = open_sensors if self._state != self._revert_state and self._revert_state: await self.async_update_state(self._revert_state) else: # when disarmed, only update the attributes if self._revert_state in const.ARM_MODES: self._arm_mode = self._revert_state self.async_write_ha_state() self._revert_state = None async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_FAILED_TO_ARM, self.area_id, { "open_sensors": open_sensors, const.ATTR_CONTEXT_ID: context_id } ) async def async_arm(self, arm_mode, **kwargs): """Arm the alarm or switch between arm modes.""" skip_delay = kwargs.get("skip_delay", False) bypass_open_sensors = kwargs.get("bypass_open_sensors", False) context_id = kwargs.get("context_id", None) self._arm_mode = arm_mode exit_delay = self._config[const.ATTR_MODES][arm_mode]["exit_time"] if skip_delay or not exit_delay: # immediate arm event (open_sensors, bypassed_sensors) = self.hass.data[const.DOMAIN]["sensor_handler"].validate_arming_event( area_id=self.area_id, target_state=arm_mode, bypass_open_sensors=bypass_open_sensors ) if open_sensors: # there where errors -> abort the arm _LOGGER.info( "Cannot transition from state {} to state {}, there are open sensors".format(self._state, arm_mode) ) await self.async_arm_failure(open_sensors, context_id=context_id) return False else: # proceed the arm if bypassed_sensors: self.bypassed_sensors = bypassed_sensors self.open_sensors = None if self.changed_by: _LOGGER.info("Alarm '{}' is armed ({}) by {}.".format(self.name, arm_mode, self.changed_by)) else: _LOGGER.info("Alarm '{}' is armed ({}).".format(self.name, arm_mode)) if self._state and self._state != STATE_ALARM_ARMING: async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_ARM, self.area_id, { "arm_mode": arm_mode, "delay": 0, const.ATTR_CONTEXT_ID: context_id } ) await self.async_update_state(arm_mode) return True else: # normal arm event (from disarmed via arming) (open_sensors, _bypassed_sensors) = self.hass.data[const.DOMAIN]["sensor_handler"].validate_arming_event( area_id=self.area_id, target_state=arm_mode, use_delay=True, bypass_open_sensors=bypass_open_sensors, ) if open_sensors: # there where errors -> abort the arm _LOGGER.info("Cannot arm right now, there are open sensors") await self.async_arm_failure(open_sensors, context_id=context_id) return False else: # proceed the arm _LOGGER.info("Alarm is now arming. Waiting for {} seconds.".format(exit_delay)) async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_ARM, self.area_id, { "arm_mode": arm_mode, "delay": exit_delay, const.ATTR_CONTEXT_ID: context_id } ) self.delay = exit_delay self.open_sensors = None await self.async_update_state(STATE_ALARM_ARMING) @callback async def async_leave_timer_finished(now): """Update state at a scheduled point in time.""" _LOGGER.debug("async_leave_timer_finished") await self.async_arm( self.arm_mode, bypass_open_sensors=bypass_open_sensors, skip_delay=True ) self.async_set_timer(exit_delay, async_leave_timer_finished) return True async def async_trigger(self, skip_delay: bool = False, open_sensors: dict = None): """Trigger request. Will only be called the first time a sensor trips.""" if self._state == STATE_ALARM_PENDING or skip_delay or not self._arm_mode: entry_delay = 0 else: entry_delay = self._config[const.ATTR_MODES][self._arm_mode]["entry_time"] trigger_time = self._config[const.ATTR_MODES][self._arm_mode]["trigger_time"] if self._arm_mode else 0 if self._state and ( self._state != STATE_ALARM_PENDING or (self._state == STATE_ALARM_PENDING and skip_delay and open_sensors != self.open_sensors) ): # send event on first trigger or consecutive trigger in case it has no entry delay async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_TRIGGER, self.area_id, { "open_sensors": open_sensors if open_sensors else self._open_sensors, "delay": entry_delay, } ) if open_sensors: self.open_sensors = open_sensors if not entry_delay: # countdown finished or immediate trigger event await self.async_update_state(STATE_ALARM_TRIGGERED) if trigger_time: # there is a max. trigger time configured @callback async def async_trigger_timer_finished(now): """Update state at a scheduled point in time.""" _LOGGER.debug("async_trigger_timer_finished") self._changed_by = None if self._config[const.ATTR_DISARM_AFTER_TRIGGER] or not self.arm_mode: self.bypassed_sensors = None await self.async_update_state(STATE_ALARM_DISARMED) else: self.open_sensors = None self.bypassed_sensors = None await self.async_arm(self.arm_mode, bypass_open_sensors=True, skip_delay=True) async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_TRIGGER_TIME_EXPIRED, self.area_id ) self.async_set_timer(trigger_time, async_trigger_timer_finished) _LOGGER.info("Alarm is triggered!") else: # to pending state self.delay = entry_delay await self.async_update_state(STATE_ALARM_PENDING) @callback async def async_entry_timer_finished(now): """Update state at a scheduled point in time.""" _LOGGER.debug("async_entry_timer_finished") await self.async_trigger() self.async_set_timer(entry_delay, async_entry_timer_finished) _LOGGER.info("Alarm will be triggered after {} seconds.".format(entry_delay)) def async_set_timer(self, delay, cb_func): if self._timer: self._timer() now = dt_util.utcnow() if not isinstance(delay, datetime.timedelta): delay = datetime.timedelta(seconds=delay) self._timer = async_track_point_in_time( self.hass, cb_func, now + delay ) class AlarmoMasterEntity(AlarmoBaseEntity): """Defines a base alarm_control_panel entity.""" def __init__(self, hass: HomeAssistant, name: str, entity_id: str) -> None: """Initialize the alarm_control_panel entity.""" super().__init__(hass, name, entity_id) self.area_id = None self._target_state = None @property def supported_features(self) -> int: """Return the list of supported features.""" supported_features = [ item.supported_features or 0 for item in self.hass.data[const.DOMAIN]["areas"].values() ] return functools.reduce(operator.and_, supported_features) async def async_added_to_hass(self): """Connect to dispatcher listening for entity data notifications.""" await super().async_added_to_hass() # load the configuration and make sure that it is reloaded on changes @callback async def async_update_config(area_id=None): if area_id and area_id in self.hass.data[const.DOMAIN]["areas"]: # wait for update of the area entity, to refresh the supported_features async_call_later(self.hass, 1, async_update_config) return coordinator = self.hass.data[const.DOMAIN]["coordinator"] self._config = coordinator.store.async_get_config() await self.async_update_state() self.async_write_ha_state() self.async_on_remove( async_dispatcher_connect(self.hass, "alarmo_config_updated", async_update_config) ) await async_update_config() @callback async def async_alarm_state_changed(area_id: str, old_state: str, new_state: str): if not area_id: return await self.async_update_state() async_dispatcher_connect(self.hass, "alarmo_state_updated", async_alarm_state_changed) @callback async def async_handle_event(event: str, area_id: str, args: dict = {}): if not area_id or event not in [ const.EVENT_FAILED_TO_ARM, const.EVENT_TRIGGER, const.EVENT_TRIGGER_TIME_EXPIRED ]: return if event == const.EVENT_FAILED_TO_ARM and self._target_state is not None: open_sensors = args["open_sensors"] await self.async_arm_failure(open_sensors) if event == const.EVENT_TRIGGER and ( self._state not in [STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING] or ( self._state == STATE_ALARM_PENDING and self.delay and self.delay > args.get("delay", 0) ) ): # only pass initial trigger event or while trigger with shorter entry delay occurs during entry time async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_TRIGGER, self.area_id, args ) if event == const.EVENT_TRIGGER_TIME_EXPIRED: if self.hass.data[const.DOMAIN]["areas"][area_id].state == STATE_ALARM_DISARMED: await self.async_alarm_disarm(skip_code=True) async_dispatcher_connect(self.hass, "alarmo_event", async_handle_event) state = await self.async_get_last_state() if state and state.state: self._state = state.state else: self._state = STATE_ALARM_DISARMED self.async_write_ha_state() async def async_update_state(self, state: str = None): """update the state or refresh state attributes""" if state: # do not allow updating the state directly return states = [ item.state for item in self.hass.data[const.DOMAIN]["areas"].values() ] state = None if STATE_ALARM_TRIGGERED in states: state = STATE_ALARM_TRIGGERED elif STATE_ALARM_PENDING in states: state = STATE_ALARM_PENDING elif STATE_ALARM_ARMING in states and all(el in const.ARM_MODES or el == STATE_ALARM_ARMING for el in states): state = STATE_ALARM_ARMING elif all(el == STATE_ALARM_ARMED_AWAY for el in states): state = STATE_ALARM_ARMED_AWAY elif all(el == STATE_ALARM_ARMED_HOME for el in states): state = STATE_ALARM_ARMED_HOME elif all(el == STATE_ALARM_ARMED_NIGHT for el in states): state = STATE_ALARM_ARMED_NIGHT elif all(el == STATE_ALARM_ARMED_CUSTOM_BYPASS for el in states): state = STATE_ALARM_ARMED_CUSTOM_BYPASS elif all(el == STATE_ALARM_ARMED_VACATION for el in states): state = STATE_ALARM_ARMED_VACATION elif all(el == STATE_ALARM_DISARMED for el in states): state = STATE_ALARM_DISARMED arm_modes = [ item._arm_mode for item in self.hass.data[const.DOMAIN]["areas"].values() ] arm_mode = arm_modes[0] if len(set(arm_modes)) == 1 else None if state == self._target_state: # we are transitioning to an armed state and target state is reached self._target_state = None if state in [STATE_ALARM_ARMING, STATE_ALARM_PENDING]: # one or more areas went to arming/pending state, recalculate the delay time area_filter = dict(filter(lambda el: el[1].state == state, self.hass.data[const.DOMAIN]["areas"].items())) delays = [el.delay for el in area_filter.values()] # use maximum of all areas when arming, minimum of all areas when pending delay = max(delays) if state == STATE_ALARM_ARMING else min(delays) if len(delays) else None else: delay = None # take open sensors by combining areas having same state open_sensors = {} area_filter = dict(filter(lambda el: el[1].state == state, self.hass.data[const.DOMAIN]["areas"].items())) for item in area_filter.values(): if item.open_sensors: open_sensors.update(item.open_sensors) if ( arm_mode == self._arm_mode and (state == self._state or not state) and delay == self.delay and open_sensors == self.open_sensors ): # do not update if state and properties remain unchanged return self._arm_mode = arm_mode self.delay = delay self.open_sensors = open_sensors if state != self._state and state: # state changes old_state = self._state self._state = state _LOGGER.debug("entity {} was updated from {} to {}".format(self.entity_id, old_state, state)) async_dispatcher_send(self.hass, "alarmo_state_updated", None, old_state, state) # take bypassed sensors by combining all areas bypassed_sensors = [] for item in self.hass.data[const.DOMAIN]["areas"].values(): if item.bypassed_sensors: bypassed_sensors.extend(item.bypassed_sensors) self.bypassed_sensors = bypassed_sensors self.async_write_ha_state() async def async_alarm_disarm(self, **kwargs): """Send disarm command.""" code = kwargs.get("code", None) skip_code = kwargs.get("skip_code", False) context_id = kwargs.get("context_id", None) """Send disarm command.""" res = await super().async_alarm_disarm(code=code, skip_code=skip_code) if res: for item in self.hass.data[const.DOMAIN]["areas"].values(): if item.state != STATE_ALARM_DISARMED: await item.async_alarm_disarm(code=code, skip_code=skip_code) async_dispatcher_send(self.hass, "alarmo_event", const.EVENT_DISARM, self.area_id, { const.ATTR_CONTEXT_ID: context_id }) async def async_arm(self, arm_mode, **kwargs): """Arm the alarm or switch between arm modes.""" skip_delay = kwargs.get("skip_delay", False) bypass_open_sensors = kwargs.get("bypass_open_sensors", False) context_id = kwargs.get("context_id", None) self._target_state = arm_mode open_sensors = {} for item in self.hass.data[const.DOMAIN]["areas"].values(): if (item.state in const.ARM_MODES and item.arm_mode != arm_mode) or item.state == STATE_ALARM_DISARMED: res = await item.async_arm( arm_mode, skip_delay=skip_delay, bypass_open_sensors=bypass_open_sensors ) if not res: open_sensors.update(item.open_sensors) if open_sensors: await self.async_arm_failure(open_sensors, context_id=context_id) else: delay = 0 area_config = self.hass.data[const.DOMAIN]["coordinator"].store.async_get_areas() for (area_id, entity) in self.hass.data[const.DOMAIN]["areas"].items(): if entity.state == STATE_ALARM_ARMING: t = area_config[area_id][const.ATTR_MODES][arm_mode]["exit_time"] delay = t if t > delay else delay async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_ARM, self.area_id, { "arm_mode": arm_mode, "delay": delay, const.ATTR_CONTEXT_ID: context_id } ) async def async_arm_failure(self, open_sensors: dict, context_id=None): """handle arm failure.""" self.open_sensors = open_sensors self._target_state = None for item in self.hass.data[const.DOMAIN]["areas"].values(): if item.state != self._revert_state and self._revert_state: await item.async_update_state(self._revert_state) self._revert_state = None async_dispatcher_send( self.hass, "alarmo_event", const.EVENT_FAILED_TO_ARM, None, { "open_sensors": open_sensors, const.ATTR_CONTEXT_ID: context_id } ) self.async_write_ha_state() async def async_trigger(self, skip_delay: bool = False): """handle triggering via service call""" for item in self.hass.data[const.DOMAIN]["areas"].values(): if item.state != self._revert_state: await item.async_trigger(skip_delay=skip_delay) ================================================ FILE: custom_components/alarmo/automations.py ================================================ import logging import copy import re from homeassistant.core import ( HomeAssistant, callback, ) from homeassistant.const import ( ATTR_SERVICE, CONF_SERVICE_DATA, ATTR_ENTITY_ID, CONF_TYPE, # STATE_UNKNOWN, # STATE_OPEN, # STATE_CLOSED, ) from homeassistant.components.notify import ATTR_MESSAGE from homeassistant.helpers.service import async_call_from_config from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.binary_sensor.device_condition import ( ENTITY_CONDITIONS, ) from . import const from .alarm_control_panel import AlarmoBaseEntity from .helpers import ( friendly_name_for_entity_id, ) from .sensors import ( STATE_OPEN, STATE_CLOSED, STATE_UNAVAILABLE, ) _LOGGER = logging.getLogger(__name__) EVENT_ARM_FAILURE = "arm_failure" def validate_area(trigger, area_id): if const.ATTR_AREA not in trigger: return False elif trigger[const.ATTR_AREA]: return trigger[const.ATTR_AREA] == area_id else: return area_id is None def validate_modes(trigger, mode): if const.ATTR_MODES not in trigger: return False elif not trigger[const.ATTR_MODES]: return True else: return mode in trigger[const.ATTR_MODES] def validate_trigger(trigger, to_state, from_state=None): if const.ATTR_EVENT not in trigger: return False elif trigger[const.ATTR_EVENT] == "untriggered" and from_state == "triggered": return True elif trigger[const.ATTR_EVENT] == to_state: return True else: return False class AutomationHandler: def __init__(self, hass: HomeAssistant): self.hass = hass self._config = None self._subscriptions = [] self._sensorTranslationCache = {} self._alarmTranslationCache = {} self._sensorTranslationLang = None self._alarmTranslationLang = None def async_update_config(): """automation config updated, reload the configuration.""" self._config = self.hass.data[const.DOMAIN]["coordinator"].store.async_get_automations() self._subscriptions.append( async_dispatcher_connect(hass, "alarmo_automations_updated", async_update_config) ) async_update_config() @callback async def async_alarm_state_changed(area_id: str, old_state: str, new_state: str): if not old_state: # ignore automations at startup/restoring return if area_id: alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id] else: alarm_entity = self.hass.data[const.DOMAIN]["master"] if not alarm_entity: return _LOGGER.debug("state of {} is updated from {} to {}".format(alarm_entity.entity_id, old_state, new_state)) if new_state in const.ARM_MODES: # we don't distinguish between armed modes for automations, they are handled separately new_state = "armed" for automation_id, config in self._config.items(): if not config[const.ATTR_ENABLED]: continue for trigger in config[const.ATTR_TRIGGERS]: if ( validate_area(trigger, area_id) and validate_modes(trigger, alarm_entity._arm_mode) and validate_trigger(trigger, new_state, old_state) ): await self.async_execute_automation(automation_id, alarm_entity) self._subscriptions.append( async_dispatcher_connect(self.hass, "alarmo_state_updated", async_alarm_state_changed) ) @callback async def async_handle_event(event: str, area_id: str, args: dict = {}): if event != const.EVENT_FAILED_TO_ARM: return if area_id: alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id] else: alarm_entity = self.hass.data[const.DOMAIN]["master"] _LOGGER.debug("{} has failed to arm".format(alarm_entity.entity_id)) for automation_id, config in self._config.items(): if not config[const.ATTR_ENABLED]: continue for trigger in config[const.ATTR_TRIGGERS]: if ( validate_area(trigger, area_id) and validate_modes(trigger, alarm_entity._arm_mode) and validate_trigger(trigger, EVENT_ARM_FAILURE) ): await self.async_execute_automation(automation_id, alarm_entity) self._subscriptions.append( async_dispatcher_connect(self.hass, "alarmo_event", async_handle_event) ) def __del__(self): """prepare for removal""" while len(self._subscriptions): self._subscriptions.pop()() async def async_execute_automation(self, automation_id: str, alarm_entity: AlarmoBaseEntity): # automation is a dict of AutomationEntry _LOGGER.debug("executing automation {}".format(automation_id)) actions = self._config[automation_id][const.ATTR_ACTIONS] for action in actions: service_call = { "service": action[ATTR_SERVICE] } if ATTR_ENTITY_ID in action and action[ATTR_ENTITY_ID]: service_call["entity_id"] = action[ATTR_ENTITY_ID] if ( self._config[automation_id][CONF_TYPE] == const.ATTR_NOTIFICATION and ATTR_MESSAGE in action[CONF_SERVICE_DATA] ): data = copy.copy(action[CONF_SERVICE_DATA]) res = re.search(r'{{open_sensors(\|lang=([^}]+))?(\|format=short)?}}', data[ATTR_MESSAGE]) if res: lang = res.group(2) if res.group(2) else "en" names_only = True if res.group(3) else False open_sensors = "" if alarm_entity.open_sensors: parts = [] for (entity_id, status) in alarm_entity.open_sensors.items(): if names_only: parts.append(friendly_name_for_entity_id(entity_id, self.hass)) else: parts.append(await self.async_get_open_sensor_string(entity_id, status, lang)) open_sensors = ", ".join(parts) data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace(res.group(0), open_sensors) if "{{bypassed_sensors}}" in data[ATTR_MESSAGE]: bypassed_sensors = "" if alarm_entity.bypassed_sensors and len(alarm_entity.bypassed_sensors): parts = [] for entity_id in alarm_entity.bypassed_sensors: name = friendly_name_for_entity_id(entity_id, self.hass) parts.append(name) bypassed_sensors = ", ".join(parts) data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace("{{bypassed_sensors}}", bypassed_sensors) res = re.search(r'{{arm_mode(\|lang=([^}]+))?}}', data[ATTR_MESSAGE]) if res: lang = res.group(2) if res.group(2) else "en" arm_mode = await self.async_get_arm_mode_string(alarm_entity.arm_mode, lang) data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace(res.group(0), arm_mode) if "{{changed_by}}" in data[ATTR_MESSAGE]: changed_by = alarm_entity.changed_by if alarm_entity.changed_by else "" data[ATTR_MESSAGE] = data[ATTR_MESSAGE].replace("{{changed_by}}", changed_by) service_call[CONF_SERVICE_DATA] = data elif CONF_SERVICE_DATA in action: service_call[CONF_SERVICE_DATA] = action[CONF_SERVICE_DATA] await async_call_from_config( self.hass, service_call ) def get_automations_by_area(self, area_id: str): result = [] for (automation_id, config) in self._config.items(): if any(el[const.ATTR_AREA] == area_id for el in config[const.ATTR_TRIGGERS]): result.append(automation_id) return result async def async_get_open_sensor_string(self, entity_id: str, state: str, language: str): """get translation for sensor states""" if ( self._sensorTranslationCache and self._sensorTranslationLang == language ): translations = self._sensorTranslationCache else: translations = await self.hass.helpers.translation.async_get_translations( language, "device_automation", ["binary_sensor"] ) self._sensorTranslationCache = translations self._sensorTranslationLang = language entity = self.hass.states.get(entity_id) device_type = entity.attributes["device_class"] if entity and "device_class" in entity.attributes else None if state == STATE_OPEN: translation_key = "component.binary_sensor.device_automation.condition_type.{}".format( ENTITY_CONDITIONS[device_type][0]["type"] ) if device_type in ENTITY_CONDITIONS else None if translation_key and translation_key in translations: string = translations[translation_key] else: string = "{entity_name} is open" elif state == STATE_CLOSED: translation_key = "component.binary_sensor.device_automation.condition_type.{}".format( ENTITY_CONDITIONS[device_type][1]["type"] ) if device_type in ENTITY_CONDITIONS else None if translation_key and translation_key in translations: string = translations[translation_key] else: string = "{entity_name} is closed" elif state == STATE_UNAVAILABLE: string = "{entity_name} is unavailable" else: string = "{entity_name} is unknown" name = friendly_name_for_entity_id(entity_id, self.hass) string = string.replace("{entity_name}", name) return string async def async_get_arm_mode_string(self, arm_mode: str, language: str): """get translation for alarm arm mode""" if ( self._alarmTranslationCache and self._alarmTranslationLang == language ): translations = self._alarmTranslationCache else: translations = await self.hass.helpers.translation.async_get_translations( language, "state", ["alarm_control_panel"] ) self._alarmTranslationCache = translations self._alarmTranslationLang = language translation_key = "component.alarm_control_panel.state._.{}".format(arm_mode) if arm_mode else None if translation_key and translation_key in translations: return translations[translation_key] elif arm_mode: return " ".join(w.capitalize() for w in arm_mode.split("_")) else: return "" ================================================ FILE: custom_components/alarmo/card.py ================================================ import logging import voluptuous as vol from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.components.websocket_api import ( decorators, async_register_command ) _LOGGER = logging.getLogger(__name__) @callback @decorators.websocket_command({ vol.Required("type"): "alarmo_updated", }) @decorators.async_response async def handle_subscribe_updates(hass, connection, msg): """Handle subscribe updates.""" @callback def async_handle_event(event: str, area_id: str, args: dict = {}): """Forward events to websocket.""" data = dict(**args, **{ "event": event, "area_id": area_id }) connection.send_message({ "id": msg["id"], "type": "event", "event": { "data": data } }) connection.subscriptions[msg["id"]] = async_dispatcher_connect( hass, "alarmo_event", async_handle_event ) connection.send_result(msg["id"]) async def async_register_card(hass): """publish event to lovelace when alarm changes""" async_register_command( hass, handle_subscribe_updates ) ================================================ FILE: custom_components/alarmo/config_flow.py ================================================ """Config flow for the Alarmo component.""" import secrets from homeassistant import config_entries from .const import ( DOMAIN, NAME, ) class AlarmoConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for Alarmo.""" VERSION = "1.0.0" CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL async def async_step_user(self, user_input=None): """Handle a flow initialized by the user.""" # Only a single instance of the integration if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") id = secrets.token_hex(6) await self.async_set_unique_id(id) self._abort_if_unique_id_configured(updates=user_input) return self.async_create_entry(title=NAME, data={}) ================================================ FILE: custom_components/alarmo/const.py ================================================ """Store constants.""" import datetime import voluptuous as vol from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, STATE_ALARM_ARMING, ATTR_ENTITY_ID, CONF_MODE, CONF_CODE, ATTR_NAME, ) from homeassistant.components.alarm_control_panel import AlarmControlPanelEntityFeature from homeassistant.helpers import config_validation as cv VERSION = "1.9.5" NAME = "Alarmo" MANUFACTURER = "@nielsfaber" DOMAIN = "alarmo" CUSTOM_COMPONENTS = "custom_components" INTEGRATION_FOLDER = DOMAIN PANEL_FOLDER = "frontend" PANEL_FILENAME = "dist/alarm-panel.js" PANEL_URL = "/api/panel_custom/alarmo" PANEL_TITLE = NAME PANEL_ICON = "mdi:shield-home" PANEL_NAME = "alarm-panel" INITIALIZATION_TIME = datetime.timedelta(seconds=60) SENSOR_ARM_TIME = datetime.timedelta(seconds=5) STATES = [ STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, STATE_ALARM_ARMING, ] ARM_MODES = [ STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION ] ARM_MODE_TO_STATE = { "away": STATE_ALARM_ARMED_AWAY, "home": STATE_ALARM_ARMED_HOME, "night": STATE_ALARM_ARMED_NIGHT, "custom": STATE_ALARM_ARMED_CUSTOM_BYPASS, "vacation": STATE_ALARM_ARMED_VACATION } STATE_TO_ARM_MODE = { STATE_ALARM_ARMED_AWAY: "away", STATE_ALARM_ARMED_HOME: "home", STATE_ALARM_ARMED_NIGHT: "night", STATE_ALARM_ARMED_CUSTOM_BYPASS: "custom", STATE_ALARM_ARMED_VACATION: "vacation" } COMMAND_ARM_NIGHT = "arm_night" COMMAND_ARM_AWAY = "arm_away" COMMAND_ARM_HOME = "arm_home" COMMAND_ARM_CUSTOM_BYPASS = "arm_custom_bypass" COMMAND_ARM_VACATION = "arm_vacation" COMMAND_DISARM = "disarm" COMMANDS = [ COMMAND_DISARM, COMMAND_ARM_AWAY, COMMAND_ARM_NIGHT, COMMAND_ARM_HOME, COMMAND_ARM_CUSTOM_BYPASS, COMMAND_ARM_VACATION ] EVENT_DISARM = "disarm" EVENT_LEAVE = "leave" EVENT_ARM = "arm" EVENT_ENTRY = "entry" EVENT_TRIGGER = "trigger" EVENT_FAILED_TO_ARM = "failed_to_arm" EVENT_COMMAND_NOT_ALLOWED = "command_not_allowed" EVENT_INVALID_CODE_PROVIDED = "invalid_code_provided" EVENT_NO_CODE_PROVIDED = "no_code_provided" EVENT_TRIGGER_TIME_EXPIRED = "trigger_time_expired" ATTR_MODES = "modes" ATTR_ARM_MODE = "arm_mode" ATTR_CODE_DISARM_REQUIRED = "code_disarm_required" ATTR_REMOVE = "remove" ATTR_OLD_CODE = "old_code" ATTR_TRIGGER_TIME = "trigger_time" ATTR_EXIT_TIME = "exit_time" ATTR_ENTRY_TIME = "entry_time" ATTR_ENABLED = "enabled" ATTR_USER_ID = "user_id" ATTR_CAN_ARM = "can_arm" ATTR_CAN_DISARM = "can_disarm" ATTR_DISARM_AFTER_TRIGGER = "disarm_after_trigger" ATTR_REMOVE = "remove" ATTR_IS_OVERRIDE_CODE = "is_override_code" ATTR_AREA_LIMIT = "area_limit" ATTR_CODE_FORMAT = "code_format" ATTR_CODE_LENGTH = "code_length" ATTR_AUTOMATION_ID = "automation_id" ATTR_TYPE = "type" ATTR_AREA = "area" ATTR_MASTER = "master" ATTR_TRIGGERS = "triggers" ATTR_ACTIONS = "actions" ATTR_EVENT = "event" ATTR_REQUIRE_CODE = "require_code" ATTR_NOTIFICATION = "notification" ATTR_VERSION = "version" ATTR_STATE_PAYLOAD = "state_payload" ATTR_COMMAND_PAYLOAD = "command_payload" ATTR_FORCE = "force" ATTR_SKIP_DELAY = "skip_delay" ATTR_CONTEXT_ID = "context_id" PUSH_EVENT = "mobile_app_notification_action" EVENT_ACTION_FORCE_ARM = "ALARMO_FORCE_ARM" EVENT_ACTION_RETRY_ARM = "ALARMO_RETRY_ARM" EVENT_ACTION_DISARM = "ALARMO_DISARM" MODES_TO_SUPPORTED_FEATURES = { STATE_ALARM_ARMED_AWAY: AlarmControlPanelEntityFeature.ARM_AWAY, STATE_ALARM_ARMED_HOME: AlarmControlPanelEntityFeature.ARM_HOME, STATE_ALARM_ARMED_NIGHT: AlarmControlPanelEntityFeature.ARM_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS: AlarmControlPanelEntityFeature.ARM_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION: AlarmControlPanelEntityFeature.ARM_VACATION } SERVICE_ARM = "arm" SERVICE_DISARM = "disarm" SERVICE_ARM_SCHEMA = vol.Schema( { vol.Required(ATTR_ENTITY_ID): cv.entity_id, vol.Optional(CONF_CODE, default=""): cv.string, vol.Optional(CONF_MODE, default=STATE_ALARM_ARMED_AWAY): vol.In([ "away", "home", "night", "custom", "vacation", STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION, ]), vol.Optional(ATTR_SKIP_DELAY, default=False): cv.boolean, vol.Optional(ATTR_FORCE, default=False): cv.boolean, vol.Optional(ATTR_CONTEXT_ID): int } ) SERVICE_DISARM_SCHEMA = vol.Schema( { vol.Required(ATTR_ENTITY_ID): cv.entity_id, vol.Optional(CONF_CODE, default=""): cv.string, vol.Optional(ATTR_CONTEXT_ID): int } ) SERVICE_ENABLE_USER = "enable_user" SERVICE_DISABLE_USER = "disable_user" SERVICE_TOGGLE_USER_SCHEMA = vol.Schema( { vol.Required(ATTR_NAME, default=""): cv.string, } ) ================================================ FILE: custom_components/alarmo/event.py ================================================ # fire events in HA for use with automations import logging from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import const _LOGGER = logging.getLogger(__name__) class EventHandler: def __init__(self, hass): """Class constructor.""" self.hass = hass self._subscription = async_dispatcher_connect( self.hass, "alarmo_event", self.async_handle_event ) def __del__(self): """Class destructor.""" self._subscription() @callback async def async_handle_event(self, event: str, area_id: str, args: dict = {}): """handle event""" if event in [ const.EVENT_FAILED_TO_ARM, const.EVENT_COMMAND_NOT_ALLOWED, const.EVENT_INVALID_CODE_PROVIDED, const.EVENT_NO_CODE_PROVIDED ]: reasons = { const.EVENT_FAILED_TO_ARM: "open_sensors", const.EVENT_COMMAND_NOT_ALLOWED: "not_allowed", const.EVENT_INVALID_CODE_PROVIDED: "invalid_code", const.EVENT_NO_CODE_PROVIDED: "invalid_code", } data = dict(**args, **{ "area_id": area_id, "reason": reasons[event], }) if "open_sensors" in data: data["sensors"] = list(data["open_sensors"].keys()) del data["open_sensors"] self.hass.bus.fire("alarmo_failed_to_arm", data) elif event in [ const.EVENT_ARM, const.EVENT_DISARM ]: data = dict(**args, **{ "area_id": area_id, "action": event }) if "arm_mode" in data: data["mode"] = const.STATE_TO_ARM_MODE[data["arm_mode"]] del data["arm_mode"] self.hass.bus.fire("alarmo_command_success", data) ================================================ FILE: custom_components/alarmo/frontend/dist/alarm-panel.js ================================================ !function(e){"use strict"; /*! ***************************************************************************** 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 t(e,t,a,i){var s,n=arguments.length,r=n<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,a):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(e,t,a,i);else for(var o=e.length-1;o>=0;o--)(s=e[o])&&(r=(n<3?s(r):n>3?s(t,a,r):s(t,a))||r);return n>3&&r&&Object.defineProperty(t,a,r),r /** * @license * Copyright 2019 Google LLC * SPDX-License-Identifier: BSD-3-Clause */}const a=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,i=Symbol();class s{constructor(e,t){if(t!==i)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){return a&&void 0===this.t&&(this.t=new CSSStyleSheet,this.t.replaceSync(this.cssText)),this.t}toString(){return this.cssText}}const n=new Map,r=e=>{let t=n.get(e);return void 0===t&&n.set(e,t=new s(e,i)),t},o=(e,...t)=>{const a=1===e.length?e[0]:t.reduce((t,a,i)=>t+(e=>{if(e instanceof s)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.")})(a)+e[i+1],e[0]);return r(a)},l=(e,t)=>{a?e.adoptedStyleSheets=t.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet):t.forEach(t=>{const a=document.createElement("style");a.textContent=t.cssText,e.appendChild(a)})},d=a?e=>e:e=>e instanceof CSSStyleSheet?(e=>{let t="";for(const a of e.cssRules)t+=a.cssText;return(e=>r("string"==typeof e?e:e+""))(t)})(e):e /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */;var c,h,u,m;const p={toAttribute(e,t){switch(t){case Boolean:e=e?"":null;break;case Object:case Array:e=null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){let a=e;switch(t){case Boolean:a=null!==e;break;case Number:a=null===e?null:Number(e);break;case Object:case Array:try{a=JSON.parse(e)}catch(e){a=null}}return a}},g=(e,t)=>t!==e&&(t==t||e==e),v={attribute:!0,type:String,converter:p,reflect:!1,hasChanged:g};class f extends HTMLElement{constructor(){super(),this.Πi=new Map,this.Πo=void 0,this.Πl=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this.Πh=null,this.u()}static addInitializer(e){var t;null!==(t=this.v)&&void 0!==t||(this.v=[]),this.v.push(e)}static get observedAttributes(){this.finalize();const e=[];return this.elementProperties.forEach((t,a)=>{const i=this.Πp(a,t);void 0!==i&&(this.Πm.set(i,a),e.push(i))}),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 a="symbol"==typeof e?Symbol():"__"+e,i=this.getPropertyDescriptor(e,a,t);void 0!==i&&Object.defineProperty(this.prototype,e,i)}}static getPropertyDescriptor(e,t,a){return{get(){return this[t]},set(i){const s=this[e];this[t]=i,this.requestUpdate(e,s,a)},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.Πm=new Map,this.hasOwnProperty("properties")){const e=this.properties,t=[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)];for(const a of t)this.createProperty(a,e[a])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(e){const t=[];if(Array.isArray(e)){const a=new Set(e.flat(1/0).reverse());for(const e of a)t.unshift(d(e))}else void 0!==e&&t.push(d(e));return t}static"Πp"(e,t){const a=t.attribute;return!1===a?void 0:"string"==typeof a?a:"string"==typeof e?e.toLowerCase():void 0}u(){var e;this.Πg=new Promise(e=>this.enableUpdating=e),this.L=new Map,this.Π_(),this.requestUpdate(),null===(e=this.constructor.v)||void 0===e||e.forEach(e=>e(this))}addController(e){var t,a;(null!==(t=this.ΠU)&&void 0!==t?t:this.ΠU=[]).push(e),void 0!==this.renderRoot&&this.isConnected&&(null===(a=e.hostConnected)||void 0===a||a.call(e))}removeController(e){var t;null===(t=this.ΠU)||void 0===t||t.splice(this.ΠU.indexOf(e)>>>0,1)}"Π_"(){this.constructor.elementProperties.forEach((e,t)=>{this.hasOwnProperty(t)&&(this.Πi.set(t,this[t]),delete this[t])})}createRenderRoot(){var e;const t=null!==(e=this.shadowRoot)&&void 0!==e?e:this.attachShadow(this.constructor.shadowRootOptions);return l(t,this.constructor.elementStyles),t}connectedCallback(){var e;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(e=this.ΠU)||void 0===e||e.forEach(e=>{var t;return null===(t=e.hostConnected)||void 0===t?void 0:t.call(e)}),this.Πl&&(this.Πl(),this.Πo=this.Πl=void 0)}enableUpdating(e){}disconnectedCallback(){var e;null===(e=this.ΠU)||void 0===e||e.forEach(e=>{var t;return null===(t=e.hostDisconnected)||void 0===t?void 0:t.call(e)}),this.Πo=new Promise(e=>this.Πl=e)}attributeChangedCallback(e,t,a){this.K(e,a)}"Πj"(e,t,a=v){var i,s;const n=this.constructor.Πp(e,a);if(void 0!==n&&!0===a.reflect){const r=(null!==(s=null===(i=a.converter)||void 0===i?void 0:i.toAttribute)&&void 0!==s?s:p.toAttribute)(t,a.type);this.Πh=e,null==r?this.removeAttribute(n):this.setAttribute(n,r),this.Πh=null}}K(e,t){var a,i,s;const n=this.constructor,r=n.Πm.get(e);if(void 0!==r&&this.Πh!==r){const e=n.getPropertyOptions(r),o=e.converter,l=null!==(s=null!==(i=null===(a=o)||void 0===a?void 0:a.fromAttribute)&&void 0!==i?i:"function"==typeof o?o:null)&&void 0!==s?s:p.fromAttribute;this.Πh=r,this[r]=l(t,e.type),this.Πh=null}}requestUpdate(e,t,a){let i=!0;void 0!==e&&(((a=a||this.constructor.getPropertyOptions(e)).hasChanged||g)(this[e],t)?(this.L.has(e)||this.L.set(e,t),!0===a.reflect&&this.Πh!==e&&(void 0===this.Πk&&(this.Πk=new Map),this.Πk.set(e,a))):i=!1),!this.isUpdatePending&&i&&(this.Πg=this.Πq())}async"Πq"(){this.isUpdatePending=!0;try{for(await this.Πg;this.Πo;)await this.Πo}catch(e){Promise.reject(e)}const e=this.performUpdate();return null!=e&&await e,!this.isUpdatePending}performUpdate(){var e;if(!this.isUpdatePending)return;this.hasUpdated,this.Πi&&(this.Πi.forEach((e,t)=>this[t]=e),this.Πi=void 0);let t=!1;const a=this.L;try{t=this.shouldUpdate(a),t?(this.willUpdate(a),null===(e=this.ΠU)||void 0===e||e.forEach(e=>{var t;return null===(t=e.hostUpdate)||void 0===t?void 0:t.call(e)}),this.update(a)):this.Π$()}catch(e){throw t=!1,this.Π$(),e}t&&this.E(a)}willUpdate(e){}E(e){var t;null===(t=this.ΠU)||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)}"Π$"(){this.L=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this.Πg}shouldUpdate(e){return!0}update(e){void 0!==this.Πk&&(this.Πk.forEach((e,t)=>this.Πj(t,this[t],e)),this.Πk=void 0),this.Π$()}updated(e){}firstUpdated(e){}} /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ var _,b,y,w;f.finalized=!0,f.elementProperties=new Map,f.elementStyles=[],f.shadowRootOptions={mode:"open"},null===(h=(c=globalThis).reactiveElementPlatformSupport)||void 0===h||h.call(c,{ReactiveElement:f}),(null!==(u=(m=globalThis).reactiveElementVersions)&&void 0!==u?u:m.reactiveElementVersions=[]).push("1.0.0-rc.2");const k=globalThis.trustedTypes,$=k?k.createPolicy("lit-html",{createHTML:e=>e}):void 0,A=`lit$${(Math.random()+"").slice(9)}$`,x="?"+A,O=`<${x}>`,E=document,T=(e="")=>E.createComment(e),j=e=>null===e||"object"!=typeof e&&"function"!=typeof e,S=Array.isArray,C=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,M=/-->/g,N=/>/g,D=/>|[ \n \r](?:([^\s"'>=/]+)([ \n \r]*=[ \n \r]*(?:[^ \n \r"'`<>=]|("|')|))|$)/g,L=/'/g,z=/"/g,P=/^(?:script|style|textarea)$/i,q=(e=>(t,...a)=>({_$litType$:e,strings:t,values:a}))(1),R=Symbol.for("lit-noChange"),I=Symbol.for("lit-nothing"),U=new WeakMap,G=(e,t,a)=>{var i,s;const n=null!==(i=null==a?void 0:a.renderBefore)&&void 0!==i?i:t;let r=n._$litPart$;if(void 0===r){const e=null!==(s=null==a?void 0:a.renderBefore)&&void 0!==s?s:null;n._$litPart$=r=new B(t.insertBefore(T(),e),e,void 0,a)}return r.I(e),r},F=E.createTreeWalker(E,129,null,!1);class V{constructor({strings:e,_$litType$:t},a){let i;this.parts=[];let s=0,n=0;const r=e.length-1,o=this.parts,[l,d]=((e,t)=>{const a=e.length-1,i=[];let s,n=2===t?"":"",r=C;for(let t=0;t"===l[0]?(r=null!=s?s:C,d=-1):void 0===l[1]?d=-2:(d=r.lastIndex-l[2].length,o=l[1],r=void 0===l[3]?D:'"'===l[3]?z:L):r===z||r===L?r=D:r===M||r===N?r=C:(r=D,s=void 0);const h=r===D&&e[t+1].startsWith("/>")?" ":"";n+=r===C?a+O:d>=0?(i.push(o),a.slice(0,d)+"$lit$"+a.slice(d)+A+h):a+A+(-2===d?(i.push(void 0),t):h)}const o=n+(e[a]||"")+(2===t?"":"");return[void 0!==$?$.createHTML(o):o,i]})(e,t);if(this.el=V.createElement(l,a),F.currentNode=this.el.content,2===t){const e=this.el.content,t=e.firstChild;t.remove(),e.append(...t.childNodes)}for(;null!==(i=F.nextNode())&&o.length0){i.textContent=k?k.emptyScript:"";for(let a=0;a{var t;return S(e)||"function"==typeof(null===(t=e)||void 0===t?void 0:t[Symbol.iterator])})(e)?this.g(e):this.m(e)}k(e,t=this.B){return this.A.parentNode.insertBefore(e,t)}$(e){this.H!==e&&(this.R(),this.H=this.k(e))}m(e){const t=this.A.nextSibling;null!==t&&3===t.nodeType&&(null===this.B?null===t.nextSibling:t===this.B.previousSibling)?t.data=e:this.$(E.createTextNode(e)),this.H=e}_(e){var t;const{values:a,_$litType$:i}=e,s="number"==typeof i?this.C(e):(void 0===i.el&&(i.el=V.createElement(i.h,this.options)),i);if((null===(t=this.H)||void 0===t?void 0:t.D)===s)this.H.v(a);else{const e=new Y(s,this),t=e.u(this.options);e.v(a),this.$(t),this.H=e}}C(e){let t=U.get(e.strings);return void 0===t&&U.set(e.strings,t=new V(e)),t}g(e){S(this.H)||(this.H=[],this.R());const t=this.H;let a,i=0;for(const s of e)i===t.length?t.push(a=new B(this.k(T()),this.k(T()),this,this.options)):a=t[i],a.I(s),i++;i2||""!==a[0]||""!==a[1]?(this.H=Array(a.length-1).fill(I),this.strings=a):this.H=I}get tagName(){return this.element.tagName}I(e,t=this,a,i){const s=this.strings;let n=!1;if(void 0===s)e=H(this,e,t,0),n=!j(e)||e!==this.H&&e!==R,n&&(this.H=e);else{const i=e;let r,o;for(e=s[0],r=0;rt=>"function"==typeof t?((e,t)=>(window.customElements.define(e,t),t))(e,t):((e,t)=>{const{kind:a,elements:i}=t;return{kind:a,elements:i,finisher(t){window.customElements.define(e,t)}}})(e,t) /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */,oe=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?{...t,finisher(a){a.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(a){a.createProperty(t.key,e)}};function le(e){return(t,a)=>void 0!==a?((e,t,a)=>{t.constructor.createProperty(a,e)})(e,t,a):oe(e,t) /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */}function de(e){return le({...e,state:!0,attribute:!1})} /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ function ce(e,t){return(({finisher:e,descriptor:t})=>(a,i)=>{var s;if(void 0===i){const i=null!==(s=a.originalKey)&&void 0!==s?s:a.key,n=null!=t?{kind:"method",placement:"prototype",key:i,descriptor:t(a.key)}:{...a,key:i};return null!=e&&(n.finisher=function(t){e(t,i)}),n}{const s=a.constructor;void 0!==t&&Object.defineProperty(a,i,t(i)),null==e||e(s,i)}})({descriptor:a=>{const i={get(){var t;return null===(t=this.renderRoot)||void 0===t?void 0:t.querySelector(e)},enumerable:!0,configurable:!0};if(t){const t="symbol"==typeof a?Symbol():"__"+a;i.get=function(){var a;return void 0===this[t]&&(this[t]=null===(a=this.renderRoot)||void 0===a?void 0:a.querySelector(e)),this[t]}}return i}})}var he=/d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|Z|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g,ue="[^\\s]+",me=/\[([^]*?)\]/gm;function pe(e,t){for(var a=[],i=0,s=e.length;i-1?i:null}};function ve(e){for(var t=[],a=1;a3?0:(e-e%10!=10?1:0)*e%10]}},we=ve({},ye),ke=function(e,t){for(void 0===t&&(t=2),e=String(e);e.length0?"-":"+")+ke(100*Math.floor(Math.abs(t)/60)+Math.abs(t)%60,4)},Z:function(e){var t=e.getTimezoneOffset();return(t>0?"-":"+")+ke(Math.floor(Math.abs(t)/60),2)+":"+ke(Math.abs(t)%60,2)}},Ae=function(e){return+e-1},xe=[null,"[1-9]\\d?"],Oe=[null,ue],Ee=["isPm",ue,function(e,t){var a=e.toLowerCase();return a===t.amPm[0]?0:a===t.amPm[1]?1:null}],Te=["timezoneOffset","[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z?",function(e){var t=(e+"").match(/([+-]|\d\d)/gi);if(t){var a=60*+t[1]+parseInt(t[2],10);return"+"===t[0]?a:-a}return 0}],je=(ge("monthNamesShort"),ge("monthNames"),{default:"ddd MMM DD YYYY HH:mm:ss",shortDate:"M/D/YY",mediumDate:"MMM D, YYYY",longDate:"MMMM D, YYYY",fullDate:"dddd, MMMM D, YYYY",isoDate:"YYYY-MM-DD",isoDateTime:"YYYY-MM-DDTHH:mm:ssZ",shortTime:"HH:mm",mediumTime:"HH:mm:ss",longTime:"HH:mm:ss.SSS"});var Se=function(e,t,a){if(void 0===t&&(t=je.default),void 0===a&&(a={}),"number"==typeof e&&(e=new Date(e)),"[object Date]"!==Object.prototype.toString.call(e)||isNaN(e.getTime()))throw new Error("Invalid Date pass to format");var i=[];t=(t=je[t]||t).replace(me,(function(e,t){return i.push(t),"@@@"}));var s=ve(ve({},we),a);return(t=t.replace(he,(function(t){return $e[t](e,s)}))).replace(/@@@/g,(function(){return i.shift()}))};(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 Ce(e){return e.substr(0,e.indexOf("."))}function Me(e){return e.substr(e.indexOf(".")+1)}var Ne="hass:bookmark",De=function(e,t,a,i){i=i||{},a=null==a?{}:a;var s=new Event(t,{bubbles:void 0===i.bubbles||i.bubbles,cancelable:Boolean(i.cancelable),composed:void 0===i.composed||i.composed});return s.detail=a,e.dispatchEvent(s),s},Le={alert:"hass:alert",automation:"hass:playlist-play",calendar:"hass:calendar",camera:"hass:video",climate:"hass:thermostat",configurator:"hass:settings",conversation:"hass:text-to-speech",device_tracker:"hass:account",fan:"hass:fan",group:"hass:google-circles-communities",history_graph:"hass:chart-line",homeassistant:"hass:home-assistant",homekit:"hass:home-automation",image_processing:"hass:image-filter-frames",input_boolean:"hass:drawing",input_datetime:"hass:calendar-clock",input_number:"hass:ray-vertex",input_select:"hass:format-list-bulleted",input_text:"hass:textbox",light:"hass:lightbulb",mailbox:"hass:mailbox",notify:"hass:comment-alert",person:"hass:account",plant:"hass:flower",proximity:"hass:apple-safari",remote:"hass:remote",scene:"hass:google-pages",script:"hass:file-document",sensor:"hass:eye",simple_alarm:"hass:bell",sun:"hass:white-balance-sunny",switch:"hass:flash",timer:"hass:timer",updater:"hass:cloud-upload",vacuum:"hass:robot-vacuum",water_heater:"hass:thermometer",weblink:"hass:open-in-new"};function ze(e,t){if(e in Le)return Le[e];switch(e){case"alarm_control_panel":switch(t){case"armed_home":return"hass:bell-plus";case"armed_night":return"hass:bell-sleep";case"disarmed":return"hass:bell-outline";case"triggered":return"hass:bell-ring";default:return"hass:bell"}case"binary_sensor":return t&&"off"===t?"hass:radiobox-blank":"hass:checkbox-marked-circle";case"cover":return"closed"===t?"hass:window-closed":"hass:window-open";case"lock":return t&&"unlocked"===t?"hass:lock-open":"hass:lock";case"media_player":return t&&"off"!==t&&"idle"!==t?"hass:cast-connected":"hass:cast";case"zwave":switch(t){case"dead":return"hass:emoticon-dead";case"sleeping":return"hass:sleep";case"initializing":return"hass:timer-sand";default:return"hass:z-wave"}default:return console.warn("Unable to find icon for domain "+e+" ("+t+")"),Ne}}var Pe=function(e,t,a){void 0===a&&(a=!1),a?history.replaceState(null,"",t):history.pushState(null,"",t),De(window,"location-changed",{replace:a})},qe={humidity:"hass:water-percent",illuminance:"hass:brightness-5",temperature:"hass:thermometer",pressure:"hass:gauge",power:"hass:flash",signal_strength:"hass:wifi"},Re={binary_sensor:function(e){var t=e.state&&"off"===e.state;switch(e.attributes.device_class){case"battery":return t?"hass:battery":"hass:battery-outline";case"cold":return t?"hass:thermometer":"hass:snowflake";case"connectivity":return t?"hass:server-network-off":"hass:server-network";case"door":return t?"hass:door-closed":"hass:door-open";case"garage_door":return t?"hass:garage":"hass:garage-open";case"gas":case"power":case"problem":case"safety":case"smoke":return t?"hass:shield-check":"hass:alert";case"heat":return t?"hass:thermometer":"hass:fire";case"light":return t?"hass:brightness-5":"hass:brightness-7";case"lock":return t?"hass:lock":"hass:lock-open";case"moisture":return t?"hass:water-off":"hass:water";case"motion":return t?"hass:walk":"hass:run";case"occupancy":return t?"hass:home-outline":"hass:home";case"opening":return t?"hass:square":"hass:square-outline";case"plug":return t?"hass:power-plug-off":"hass:power-plug";case"presence":return t?"hass:home-outline":"hass:home";case"sound":return t?"hass:music-note-off":"hass:music-note";case"vibration":return t?"hass:crop-portrait":"hass:vibrate";case"window":return t?"hass:window-closed":"hass:window-open";default:return t?"hass:radiobox-blank":"hass:checkbox-marked-circle"}},cover:function(e){var t="closed"!==e.state;switch(e.attributes.device_class){case"garage":return t?"hass:garage-open":"hass:garage";case"door":return t?"hass:door-open":"hass:door-closed";case"shutter":return t?"hass:window-shutter-open":"hass:window-shutter";case"blind":return t?"hass:blinds-open":"hass:blinds";case"window":return t?"hass:window-open":"hass:window-closed";default:return ze("cover",e.state)}},sensor:function(e){var t=e.attributes.device_class;if(t&&t in qe)return qe[t];if("battery"===t){var a=Number(e.state);if(isNaN(a))return"hass:battery-unknown";var i=10*Math.round(a/10);return i>=100?"hass:battery":i<=0?"hass:battery-alert":"hass:battery-"+i}var s=e.attributes.unit_of_measurement;return"°C"===s||"°F"===s?"hass:thermometer":ze("sensor")},input_datetime:function(e){return e.attributes.has_date?e.attributes.has_time?ze("input_datetime"):"hass:calendar":"hass:clock"}};const Ie=async()=>{if(customElements.get("ha-checkbox")&&customElements.get("ha-slider"))return;await customElements.whenDefined("partial-panel-resolver");const e=document.createElement("partial-panel-resolver");e.hass={panels:[{url_path:"tmp",component_name:"config"}]},e._updateRoutes(),await e.routerOptions.routes.tmp.load(),await customElements.whenDefined("ha-panel-config");const t=document.createElement("ha-panel-config");await t.routerOptions.routes.automation.load(),e.hass={panels:[{url_path:"tmp",component_name:"developer-tools"}]},e._updateRoutes(),await e.routerOptions.routes.tmp.load(),await customElements.whenDefined("ha-app-layout")},Ue=async()=>{var e,t,a,i,s,n,r,o;if(customElements.get("ha-yaml-editor"))return;const l=document.createElement("partial-panel-resolver").getRoutes([{component_name:"developer-tools",url_path:"a"}]);await(null===(a=null===(t=null===(e=null==l?void 0:l.routes)||void 0===e?void 0:e.a)||void 0===t?void 0:t.load)||void 0===a?void 0:a.call(t));const d=document.createElement("developer-tools-router");await(null===(o=null===(r=null===(n=null===(s=null===(i=d)||void 0===i?void 0:i.routerOptions)||void 0===s?void 0:s.routes)||void 0===n?void 0:n.service)||void 0===r?void 0:r.load)||void 0===o?void 0:o.call(r))},Ge=e=>e.callWS({type:"alarmo/config"}),Fe=e=>e.callWS({type:"alarmo/sensors"}),Ve=e=>e.callWS({type:"alarmo/users"}),He=e=>e.callWS({type:"alarmo/automations"}),Ye=e=>e.callWS({type:"alarmo/sensor_groups"}),Be=(e,t)=>e.callApi("POST","alarmo/config",t),Ke=(e,t)=>e.callApi("POST","alarmo/sensors",t),Qe=(e,t)=>e.callApi("POST","alarmo/users",t),We=(e,t)=>e.callApi("POST","alarmo/automations",t),Xe=(e,t)=>e.callApi("POST","alarmo/automations",{automation_id:t,remove:!0}),Ze=e=>e.callWS({type:"alarmo/areas"}),Je=(e,t)=>e.callApi("POST","alarmo/area",t),et=e=>{class a extends e{connectedCallback(){super.connectedCallback(),this.__checkSubscribed()}disconnectedCallback(){if(super.disconnectedCallback(),this.__unsubs){for(;this.__unsubs.length;){const e=this.__unsubs.pop();e instanceof Promise?e.then(e=>e()):e()}this.__unsubs=void 0}}updated(e){super.updated(e),e.has("hass")&&this.__checkSubscribed()}hassSubscribe(){return[]}__checkSubscribed(){void 0===this.__unsubs&&this.isConnected&&void 0!==this.hass&&(this.__unsubs=this.hassSubscribe())}}return t([le({attribute:!1})],a.prototype,"hass",void 0),a};var tt={modes_short:{armed_away:"Fora",armed_home:"Casa",armed_night:"Nit",armed_custom_bypass:"Personalitzat",armed_vacation:"Vacation"},enabled:"Activat",disabled:"Desactivat"},at={time_slider:{seconds:"seg",minutes:"min",infinite:"infinit",none:"cap"},editor:{ui_mode:"Canvia a UI",yaml_mode:"Canvia a YAML",edit_in_yaml:"Edit in YAML"},table:{filter:{label:"Filter items",item:"Filter by {name}",hidden_items:"{number} {number, plural,\n one {item is}\n other {items are}\n} hidden"}}},it={general:{title:"General",cards:{general:{description:"Aquest tauler defineix alguns paràmetres globals de l'alarma.",fields:{disarm_after_trigger:{heading:"Desactivar després del disparador",description:"Quan hagi transcorregut el temps d’activació, desactiveu l’alarma en lloc de tornar a l’estat armat."},enable_mqtt:{heading:"Activa MQTT",description:"Permet controlar el tauler d'alarma mitjançant MQTT."},enable_master:{heading:"Activa l'alarma mestra",description:"Crea una entitat per controlar totes les àrees simultàniament."}},actions:{setup_mqtt:"Configuració MQTT",setup_master:"Configuració mestra"}},modes:{title:"Modes",description:"Aquest tauler es pot utilitzar per configurar els modes d'activació de l'alarma.",modes:{armed_away:"El mode fora de casa s'utilitzarà quan totes les persones surtin de casa. Es controlen totes les portes i finestres que permeten l'accés a la casa, així com els sensors de moviment dins de la casa.",armed_home:"El mode a casa (també conegut com mode casa) s'utilitzarà quan configureu l'alarma mentre hi hagi persones a la casa. Es controlen totes les portes i finestres que permetin l'accés a la casa, però no els sensors de moviment a l'interior de la casa.",armed_night:"El mode nit s'utilitzarà quan configureu l'alarma abans d'anar a dormir. Es controlaran totes les portes i finestres que permetin l'accés a la casa i es seleccionaran els sensors de moviment (per exemple, a la planta baixa) de la casa.",armed_vacation:"Armed vacation can be used as an extension to the armed away mode in case of absence for longer duration. The delay times and trigger responses can be adapted (as desired) to being distant from home.",armed_custom_bypass:"Un mode addicional per definir el vostre propi perímetre de seguretat."},number_sensors_active:"{number} {number, plural,\n one {sensor}\n other {sensors}\n} activa",fields:{status:{heading:"Status",description:"Controls whether the alarm can be armed in this mode."},exit_delay:{heading:"Retard de sortida",description:"Quan activeu l'alarma, en aquest període de temps els sensors encara no activaran l'alarma."},entry_delay:{heading:"Retard d'entrada",description:"Temps de retard fins que s'activi l'alarma després que s'activi un dels sensors."},trigger_time:{heading:"Temps d'activació",description:"Temps durant el qual sonarà la sirena"}}},mqtt:{title:"Configuració MQTT",description:"Aquest tauler es pot utilitzar per configurar la interfície MQTT.",fields:{state_topic:{heading:"Tema d'estat",description:"Tema sobre el qual es publiquen les actualitzacions d'estat"},event_topic:{heading:"Tema d'esdeveniment",description:"Tema sobre el qual es publiquen els esdeveniments d'alarma"},command_topic:{heading:"Tama d'ordre",description:"Tema sobre el qual s'envien les ordres d'activació/desactivació."},require_code:{heading:"Requereix codi",description:"Requereix l'enviament d'un codi amb l'ordre."},state_payload:{heading:"Configura la càrrega útil per estat",item:"Definiu una càrrega útil per a l'estat ''{state}''"},command_payload:{heading:"Configura la càrrega útil per ordre",item:"Definiu una càrrega útil per a l'ordre ''{command}''"}}},areas:{title:"Àrees",description:"Les àrees es poden utilitzar per dividir el sistema d'alarma en diversos compartiments.",no_items:"Encara no hi ha àrees definides.",table:{remarks:"Observacions",summary:"Aquesta àrea conté {summary_sensors} i {summary_automations}.",summary_sensors:"{number} {number, plural,\n one {sensor}\n other {sensors}\n}",summary_automations:"{number} {number, plural,\n one {automatisme}\n other {automatismes}\n}"},actions:{add:"Afegeix"}}},dialogs:{create_area:{title:"Àrea nova",fields:{copy_from:"Copia la configuració de"}},edit_area:{title:"Edita l'àrea ''{area}''",name_warning:"Nota: si canvieu el nom, es canviarà l'identificador d'entitat"},remove_area:{title:"Voleu eliminar l'àrea?",description:"Confirmeu que voleu eliminar aquesta àrea? Aquesta àrea conté {sensors} sensors i {automatismes} automatismes, que també s'eliminaran."},edit_master:{title:"Configuració mestra"},disable_master:{title:"Voleu desactivar l'alarma mestra?",description:"Confirmeu que voleu eliminar l'alarma mestra? Aquesta àrea conté automatismes {automatismes}, que s'eliminaran amb aquesta acció."}}},sensors:{title:"Sensors",cards:{sensors:{description:"Sensors configurats actualment. Feu clic a una entitat per fer canvis.",table:{no_items:"No hi ha cap sensor per mostrar",arm_modes:"Modes d'armat",always_on:"(Sempre)",no_area_warning:"Sensor is not assigned to any area."}},add_sensors:{title:"Afegeix sensors",description:"Afegiu més sensors. Assegureu-vos que els vostres sensors tinguin un friendly_name perquè pugueu identificar-los.",no_items:"No hi ha entitats HA disponibles que es puguin configurar per a l'alarma. Assegureu-vos d'incloure entitats del tipus binary_sensor.",table:{type:"Detected type"},actions:{add_to_alarm:"afegeix a l'alarma",show_all:"Mostra-ho tot"}},editor:{title:"Edita el sensor",description:"Edita la configuració del sensor de ''{entity}''.",fields:{area:{heading:"Àrea",description:"Seleccioneu una àrea que contingui aquest sensor."},group:{heading:"Group",description:"Group with other sensors for combined triggering."},device_type:{heading:"Tipus de dispositiu",description:"Trieu un tipus de dispositiu per aplicar automàticament la configuració adequada.",choose:{door:{name:"Porta",description:"Porta, porta de garatge o altra entrada que s'utilitzi per entrar/sortir de casa."},window:{name:"Finestra",description:"Finestra o una porta que no s'utilitza per entrar a la casa, com ara un balcó."},motion:{name:"Moviment",description:"Sensor de presència o dispositiu similar que té un retard entre les activacions."},tamper:{name:"Antisabotatge",description:"Detector de retirada de la coberta del sensor, sensor de trencament de vidre, etc."},environmental:{name:"Ambiental",description:"Sensor de fum o gas, detector de fuites, etc. (no relacionat amb la protecció antirobatori)."},other:{name:"Genèric"}}},always_on:{heading:"Sempre activat",description:"El sensor sempre ha de disparar l'alarma."},modes:{heading:"Modes habilitats",description:"Modes d'alarma en què aquest sensor està actiu."},arm_on_close:{heading:"Arma després de tancar",description:"Després de la desactivació d'aquest sensor, s'omet automàticament el temps de retard de sortida restant."},use_exit_delay:{heading:"Use exit delay",description:"Sensor is allowed to be active when the exit delay starts."},use_entry_delay:{heading:"Use entry delay",description:"Sensor activation triggers the alarm after the entry delay rather than directly."},allow_open:{heading:"Permet obrir mentre s'arma l'alarma",description:"Permeteu que aquest sensor estigui actiu poc després de configurar-lo de manera que no bloquegi l'activació de l'alarma."},auto_bypass:{heading:"Omet automàticament",description:"Excloeu aquest sensor de l'alarma si està obert mentre s'arma l'alarma.",modes:"Modes in which sensor may be bypassed"},trigger_unavailable:{heading:"Activador quan no estigui disponible",description:"Quan l'estat del sensor no estigui disponible, això activarà el sensor."}},actions:{toggle_advanced:"Configuració avançada",remove:"Elimina",setup_groups:"Setup groups"},errors:{description:"Corregiu els errors següents:",no_area:"No s'ha seleccionat cap àrea",no_modes:"No s'han seleccionat modes per als quals el sensor hauria d'estar actiu",no_auto_bypass_modes:"No modes are selected for the sensor may be automatically bypassed"}}},dialogs:{manage_groups:{title:"Manage sensor groups",description:"In a sensor group multiple sensors must be activated within a time period before the alarm is triggered.",no_items:"No groups yet",actions:{new_group:"New group"}},create_group:{title:"New sensor group",fields:{name:{heading:"Name",description:"Name for sensor group"},timeout:{heading:"Time-out",description:"Time period during which consecutive sensor activations triggers the alarm."},sensors:{heading:"Sensors",description:"Select the sensors which are contained by this group."}},errors:{invalid_name:"Invalid name provided.",insufficient_sensors:"At least 2 sensors need to be selected."}},edit_group:{title:"Edit sensor group ''{name}''"}}},codes:{title:"Codis",cards:{codes:{description:"Canvieu la configuració del codi.",fields:{code_arm_required:{heading:"Utilitzeu un codi d'activació",description:"Requereix un codi per activar l'alarma"},code_disarm_required:{heading:"Utilitzeu un codi de desactivació",description:"Requereix un codi per desactivar l'alarma"},code_format:{heading:"Format del codi",description:"Estableix el tipus de codi per a la targeta d'alarma Lovelace.",code_format_number:"codi PIN",code_format_text:"contrasenya"}}},user_management:{title:"Gestió d'usuaris",description:"Cada usuari té el seu propi codi per activar/desactivar l'alarma.",no_items:"Encara no hi ha usuaris",actions:{new_user:"usuari nou"}},new_user:{title:"Crea un usuari nou",description:"Es poden crear usuaris per proporcionar accés al funcionament de l'alarma.",fields:{name:{heading:"Nom",description:"Nom de l'usuari."},code:{heading:"Codi",description:"Codi d'aquest usuari."},confirm_code:{heading:"Confirmeu el codi",description:"Repetiu el codi."},can_arm:{heading:"Permetre que el codi active l'alarma",description:"Entering this code activates the alarm"},can_disarm:{heading:"Permetre que el codi desactive l'alarma",description:"Entering this code deactivates the alarm"},is_override_code:{heading:"És un codi de sobreescriptura",description:"Si introduïu aquest codi, es forçarà l'estat d'activació de l'alarma"},area_limit:{heading:"Restricted areas",description:"Limit user to control only the selected areas"}},errors:{no_name:"No s'ha proporcionat cap nom.",no_code:"El codi ha de tenir 4 caràcters o números com a mínim.",code_mismatch:"Els codis no coincideixen."}},edit_user:{title:"Edita l'usuari",description:"Canvia la configuració de l'usuari ''{name}''.",fields:{old_code:{heading:"Codi actual",description:"Codi actual, deixeu-lo en blanc per deixar-lo sense canvis."}}}}},actions:{title:"Accions",cards:{notifications:{title:"Notificacions",description:"Utilitzant aquest tauler, podeu gestionar les notificacions que s'envien quan es produeix un determinat esdeveniment d'alarma.",table:{no_items:"Encara no s'han creat notificacions.",no_area_warning:"Action is not assigned to any area."},actions:{new_notification:"nova notificació"}},actions:{description:"Aquest tauler es pot utilitzar per canviar un dispositiu quan l'estat d'alarma canvia.",table:{no_items:"Encara no s'han creat accions."},actions:{new_action:"nova acció"}},new_notification:{title:"Crea una notificació",description:"Crea una nova notificació.",trigger:"Condition",action:"Task",options:"Options",fields:{event:{heading:"Esdeveniment",description:"Quan s'ha d'enviar la notificació",choose:{armed:{name:"L'alarma està activada",description:"L'alarma s'ha activat correctament"},disarmed:{name:"L'alarma està desactivada",description:"L'alarma està desactivada"},triggered:{name:"L'alarma s'activat per esdeveniment",description:"L'alarma s'activat per esdeveniment"},untriggered:{name:"Alarm not longer triggered",description:"The triggered state of the alarm has ended"},arm_failure:{name:"No s'ha pogut activar l'alarma",description:"L'activació de l'alarma ha fallat a causa d'un o més sensors estan oberts"},arming:{name:"S'ha iniciat el retard de sortida",description:"S'ha iniciat el retard de sortida, a punt per sortir de casa."},pending:{name:"S'ha iniciat el retard d'entrada",description:"El retard d'entrada s'ha iniciat, l'alarma s'activarà aviat."}}},mode:{heading:"Mode",description:"Limita l'acció a modes específics d'activació (opcional)"},title:{heading:"Títol",description:"Títol del missatge de notificació"},message:{heading:"Missatge",description:"Contingut del missatge de notificació",insert_wildcard:"Insert wildcard",placeholders:{armed:"The alarm is set to {{arm_mode}}",disarmed:"The alarm is now OFF",triggered:"The alarm is triggered! Cause: {{open_sensors}}.",untriggered:"The alarm is not longer triggered.",arm_failure:"The alarm could not be armed right now, due to: {{open_sensors}}.",arming:"The alarm will be armed soon, please leave the house.",pending:"The alarm is about to trigger, disarm it quickly!"}},open_sensors_format:{heading:"Format for open_sensors wildcard",description:"Choose which sensor information in inserted in the message",options:{default:"Names and states",short:"Names only"}},arm_mode_format:{heading:"Translation for arm_mode wildcard",description:"Choose in which language the arm mode is inserted in the message"},target:{heading:"Destinatari",description:"Dispositiu al qual enviar el missatge"},name:{heading:"Nom",description:"Descripció d'aquesta notificació",placeholders:{armed:"Notify {target} upon arming",disarmed:"Notify {target} upon disarming",triggered:"Notify {target} when triggered",untriggered:"Notify {target} when triggering stops",arm_failure:"Notify {target} on failure",arming:"Notify {target} when leaving",pending:"Notify {target} when arriving"}},delete:{heading:"Delete automation",description:"Permanently remove this automation"}},actions:{test:"Prova-ho"}},new_action:{title:"Crea una acció",description:"Aquest tauler es pot utilitzar per canviar un dispositiu quan l'estat d'alarma canvia.",fields:{event:{heading:"Esdeveniment",description:"Quan s'ha d'executar l'acció"},area:{heading:"Àrea",description:"Àrea per a la qual s'aplica l'esdeveniment, deixeu-la en blanc per seleccionar l'alarma global."},mode:{heading:"Mode",description:"Limita l'acció a modes específics d'activació (opcional)"},entity:{heading:"Entitat",description:"Entitat en què es realitzarà l'acció"},action:{heading:"Acció",description:"Acció a realitzar a l'entitat",no_common_actions:"Actions can only be assigned in YAML mode for the selected entities."},name:{heading:"Nom",description:"Descripció d'aquesta acció",placeholders:{armed:"Set {entity} to {state} upon arming",disarmed:"Set {entity} to {state} upon disarming",triggered:"Set {entity} to {state} when triggered",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Set {entity} to {state} on failure",arming:"Set {entity} to {state} when leaving",pending:"Set {entity} to {state} when arriving"}}}}}}},st={common:tt,components:at,title:"Tauler alarma",panels:it},nt=Object.freeze({__proto__:null,common:tt,components:at,title:"Tauler alarma",panels:it,default:st}),rt={modes_short:{armed_away:"Away",armed_home:"Home",armed_night:"Night",armed_custom_bypass:"Custom",armed_vacation:"Vacation"},enabled:"Enabled",disabled:"Disabled"},ot={time_slider:{seconds:"sec",minutes:"min",infinite:"infinite",none:"none"},editor:{ui_mode:"To UI",yaml_mode:"To YAML",edit_in_yaml:"Edit in YAML"},table:{filter:{label:"Filter items",item:"Filter by {name}",hidden_items:"{number} {number, plural,\n one {item is}\n other {items are}\n} hidden"}}},lt={general:{title:"General",cards:{general:{description:"This panel defines some global settings for the alarm.",fields:{disarm_after_trigger:{heading:"Disarm after trigger",description:"After trigger time has expired, disarm the alarm instead of returning to armed state."},enable_mqtt:{heading:"Enable MQTT",description:"Allow the alarm panel to be controlled through MQTT."},enable_master:{heading:"Enable alarm master",description:"Creates an entity for controlling all areas simultaneously."}},actions:{setup_mqtt:"MQTT Configuration",setup_master:"Master Configuration"}},modes:{title:"Modes",description:"This panel can be used to set up the arm modes of the alarm.",modes:{armed_away:"Armed away will be used when all people left the house. All doors and windows allowing access to the house will be guarded, as well as motion sensors inside the house.",armed_home:"Armed home (also known as armed stay) will be used when setting the alarm while people are in the house. All doors and windows allowing access to the house will be guarded, but not motion sensors inside the house.",armed_night:"Armed night will be used when setting the alarm before going to sleep. All doors and windows allowing access to the house will be guarded, and selected motion sensors (downstairs) in the house.",armed_vacation:"Armed vacation can be used as an extension to the armed away mode in case of absence for longer duration. The delay times and trigger responses can be adapted (as desired) to being distant from home.",armed_custom_bypass:"An extra mode for defining your own security perimeter."},number_sensors_active:"{number} {number, plural,\n one {sensor}\n other {sensors}\n} active",fields:{status:{heading:"Status",description:"Controls whether the alarm can be armed in this mode."},exit_delay:{heading:"Exit delay",description:"When arming the alarm, within this time period the sensors will not trigger the alarm yet."},entry_delay:{heading:"Entry delay",description:"Delay time until the alarm is triggered after one of the sensors is activated."},trigger_time:{heading:"Trigger time",description:"Time during which the alarm will remain in the triggered state after activation."}}},mqtt:{title:"MQTT configuration",description:"This panel can be used for configuration of the MQTT interface.",fields:{state_topic:{heading:"State topic",description:"Topic on which state updates are published"},event_topic:{heading:"Event topic",description:"Topic on which alarm events are published"},command_topic:{heading:"Command topic",description:"Topic which Alarmo listens to for arm/disarm commands."},require_code:{heading:"Require code",description:"Require the code to be sent with the command."},state_payload:{heading:"Configure payload per state",item:"Define a payload for state ''{state}''"},command_payload:{heading:"Configure payload per command",item:"Define a payload for command ''{command}''"}}},areas:{title:"Areas",description:"Areas can be used for dividing your alarm system into multiple compartments.",no_items:"There are no areas defined yet.",table:{remarks:"Remarks",summary:"This area contains {summary_sensors} and {summary_automations}.",summary_sensors:"{number} {number, plural,\n one {sensor}\n other {sensors}\n}",summary_automations:"{number} {number, plural,\n one {automation}\n other {automations}\n}"},actions:{add:"Add"}}},dialogs:{create_area:{title:"New area",fields:{copy_from:"Copy settings from"}},edit_area:{title:"Editing area ''{area}''",name_warning:"Note: changing the name will change the entity ID"},remove_area:{title:"Remove area?",description:"Are you sure you want to remove this area? This area contains {sensors} sensors and {automations} automations, which will be removed as well."},edit_master:{title:"Master configuration"},disable_master:{title:"Disable master?",description:"Are you sure you want to remove the alarm master? This area contains {automations} automations, which will be removed with this action."}}},sensors:{title:"Sensors",cards:{sensors:{description:"Currently configured sensors. Click on an item to make changes.",table:{no_items:"There are no sensors to be displayed here.",no_area_warning:"Sensor is not assigned to any area.",arm_modes:"Arm Modes",always_on:"(Always)"}},add_sensors:{title:"Add Sensors",description:"Add more sensors. Make sure that your sensors have a suitable name, so you can identify them.",no_items:"There are no available HA entities that can be configured for the alarm. Make sure to include entities of the type binary_sensor.",table:{type:"Detected type"},actions:{add_to_alarm:"add to alarm",filter_supported:"Hide items with unknown type"}},editor:{title:"Edit Sensor",description:"Configuring the sensor settings of ''{entity}''.",fields:{area:{heading:"Area",description:"Select an area which contains this sensor."},group:{heading:"Group",description:"Group with other sensors for combined triggering."},device_type:{heading:"Device Type",description:"Choose a device type to automatically apply appropriate settings.",choose:{door:{name:"Door",description:"A door, gate or other entrance that is used for entering/leaving the home."},window:{name:"Window",description:"A window, or a door not used for entering the house such as balcony."},motion:{name:"Motion",description:"Presence sensor or similar device having a delay between activations."},tamper:{name:"Tamper",description:"Detector of sensor cover removal, glass break sensor, etc."},environmental:{name:"Environmental",description:"Smoke/gas sensor, leak detector, etc. (not related to burglar protection)."},other:{name:"Generic"}}},always_on:{heading:"Always on",description:"Sensor should always trigger the alarm."},modes:{heading:"Enabled modes",description:"Alarm modes in which this sensor is active."},arm_on_close:{heading:"Arm after closing",description:"After deactivation of this sensor, the remaining exit delay will automatically be skipped."},use_exit_delay:{heading:"Use exit delay",description:"Sensor is allowed to be active when the exit delay starts."},use_entry_delay:{heading:"Use entry delay",description:"Sensor activation triggers the alarm after the entry delay rather than directly."},allow_open:{heading:"Allow open after arming",description:"If the sensor is still active after the exit delay, this will not cause arming to fail."},auto_bypass:{heading:"Bypass automatically",description:"Exclude this sensor from the alarm if it is open while arming.",modes:"Modes in which sensor may be bypassed"},trigger_unavailable:{heading:"Trigger when unavailable",description:"When the sensor state becomes 'unavailable', this will activate the sensor."}},actions:{toggle_advanced:"Advanced settings",remove:"Remove",setup_groups:"Setup groups"},errors:{description:"Please correct the following errors:",no_area:"No area is selected",no_modes:"No modes are selected for which the sensor should be active",no_auto_bypass_modes:"No modes are selected for the sensor may be automatically bypassed"}}},dialogs:{manage_groups:{title:"Manage sensor groups",description:"In a sensor group multiple sensors must be activated within a time period before the alarm is triggered.",no_items:"No groups yet",actions:{new_group:"New group"}},create_group:{title:"New sensor group",fields:{name:{heading:"Name",description:"Name for sensor group"},timeout:{heading:"Time-out",description:"Time period during which consecutive sensor activations triggers the alarm."},sensors:{heading:"Sensors",description:"Select the sensors which are contained by this group."}},errors:{invalid_name:"Invalid name provided.",insufficient_sensors:"At least 2 sensors need to be selected."}},edit_group:{title:"Edit sensor group ''{name}''"}}},codes:{title:"Codes",cards:{codes:{description:"Change settings for the code.",fields:{code_arm_required:{heading:"Use arm code",description:"Require a code for arming the alarm"},code_disarm_required:{heading:"Use disarm code",description:"Require a code for disarming the alarm"},code_format:{heading:"Code format",description:"Sets the input type for Lovelace alarm card.",code_format_number:"pincode",code_format_text:"password"}}},user_management:{title:"User management",description:"Each user has its own code to arm/disarm the alarm.",no_items:"There are no users yet",actions:{new_user:"new user"}},new_user:{title:"Create new user",description:"Users can be created for providing access to operating the alarm.",fields:{name:{heading:"Name",description:"Name of the user."},code:{heading:"Code",description:"Code for this user."},confirm_code:{heading:"Confirm code",description:"Repeat the code."},can_arm:{heading:"Allow code for arming",description:"Entering this code activates the alarm"},can_disarm:{heading:"Allow code for disarming",description:"Entering this code deactivates the alarm"},is_override_code:{heading:"Is override code",description:"Entering this code will arm the alarm in force"},area_limit:{heading:"Restricted areas",description:"Limit user to control only the selected areas"}},errors:{no_name:"No name provided.",no_code:"Code should have 4 characters/numbers minimum.",code_mismatch:"The codes don't match."}},edit_user:{title:"Edit User",description:"Change configuration for user ''{name}''.",fields:{old_code:{heading:"Current code",description:"Current code, leave empty to leave unchanged."}}}}},actions:{title:"Actions",cards:{notifications:{title:"Notifications",description:"Using this panel, you can manage notifications to be sent when a certain alarm event occurs.",table:{no_items:"There are no notifications created yet.",no_area_warning:"Action is not assigned to any area."},actions:{new_notification:"new notification"}},actions:{description:"This panel can be used to switch a device when the alarm state changes.",table:{no_items:"There are no actions created yet."},actions:{new_action:"new action"}},new_notification:{title:"Configure notification",description:"Receive a notification when arming/disarming the alarm, on activation, etc.",trigger:"Condition",action:"Task",options:"Options",fields:{event:{heading:"Event",description:"When should the notification be sent",choose:{armed:{name:"Alarm is armed",description:"The alarm is succesfully armed"},disarmed:{name:"Alarm is disarmed",description:"The alarm is disarmed"},triggered:{name:"Alarm is triggered",description:"The alarm is triggered"},untriggered:{name:"Alarm not longer triggered",description:"The triggered state of the alarm has ended"},arm_failure:{name:"Failed to arm",description:"The arming of the alarm failed due to one or more open sensors"},arming:{name:"Exit delay started",description:"Exit delay started, ready to leave the house."},pending:{name:"Entry delay started",description:"Entry delay started, the alarm will trigger soon."}}},mode:{heading:"Mode",description:"Limit the action to specific arm modes (optional)"},title:{heading:"Title",description:"Title for the notification message"},message:{heading:"Message",description:"Content of the notification message",insert_wildcard:"Insert wildcard",placeholders:{armed:"The alarm is set to {{arm_mode}}",disarmed:"The alarm is now OFF",triggered:"The alarm is triggered! Cause: {{open_sensors}}.",untriggered:"The alarm is not longer triggered.",arm_failure:"The alarm could not be armed right now, due to: {{open_sensors}}.",arming:"The alarm will be armed soon, please leave the house.",pending:"The alarm is about to trigger, disarm it quickly!"}},open_sensors_format:{heading:"Format for open_sensors wildcard",description:"Choose which sensor information in inserted in the message",options:{default:"Names and states",short:"Names only"}},arm_mode_format:{heading:"Translation for arm_mode wildcard",description:"Choose in which language the arm mode is inserted in the message"},target:{heading:"Target",description:"Device to send the notification to"},name:{heading:"Name",description:"Description for this notification",placeholders:{armed:"Notify {target} upon arming",disarmed:"Notify {target} upon disarming",triggered:"Notify {target} when triggered",untriggered:"Notify {target} when triggering stops",arm_failure:"Notify {target} on failure",arming:"Notify {target} when leaving",pending:"Notify {target} when arriving"}},delete:{heading:"Delete automation",description:"Permanently remove this automation"}},actions:{test:"Try it"}},new_action:{title:"Configure action",description:"Switch lights or devices (such as sirens) when arming/disarming the alarm, on activation, etc.",fields:{event:{heading:"Event",description:"When should the action be executed"},area:{heading:"Area",description:"Area for which the event applies, leave empty to select the global alarm."},mode:{heading:"Mode",description:"Limit the action to specific arm modes (optional)"},entity:{heading:"Entity",description:"Entity to perform action on"},action:{heading:"Action",description:"Action to perform on the entity",no_common_actions:"Actions can only be assigned in YAML mode for the selected entities."},name:{heading:"Name",description:"Description for this action",placeholders:{armed:"Set {entity} to {state} upon arming",disarmed:"Set {entity} to {state} upon disarming",triggered:"Set {entity} to {state} when triggered",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Set {entity} to {state} on failure",arming:"Set {entity} to {state} when leaving",pending:"Set {entity} to {state} when arriving"}}}}}}},dt={common:rt,components:ot,title:"Alarm panel",panels:lt},ct=Object.freeze({__proto__:null,common:rt,components:ot,title:"Alarm panel",panels:lt,default:dt}),ht={modes_short:{armed_away:"Ausente",armed_home:"En casa",armed_night:"Nocturno",armed_custom_bypass:"Personalizado",armed_vacation:"Vacaciones"},enabled:"Habilitar",disabled:"Deshabilitar"},ut={time_slider:{seconds:"seg",minutes:"min",infinite:"infinito",none:"ninguno"},editor:{ui_mode:"Editar en la UI",yaml_mode:"Editar en YAML",edit_in_yaml:"Editar en YAML"},table:{filter:{label:"Filtrar entidades",item:"Filtrar por {name}",hidden_items:"{number} {number, plural,\n one {entidas está}\n other {entidades están}\n} oculta"}}},mt={general:{title:"General",cards:{general:{description:"Este panel define algunos ajustes globales para la alarma.",fields:{disarm_after_trigger:{heading:"Desarmar después de disparar",description:"Una vez transcurrido el tiempo de activación, desactivar la alarma en lugar de volver al estado de armada."},enable_mqtt:{heading:"Habilitar MQTT",description:"Permitir que el panel de alarma se controle a través de MQTT."},enable_master:{heading:"Habilitar alarma maestra",description:"Crea una entidad para controlar todas las áreas simultáneamente."}},actions:{setup_mqtt:"Configuración MQTT",setup_master:"Configuración maestra"}},modes:{title:"Modos",description:"Este panel se puede utilizar para configurar los modos de armado de la alarma.",modes:{armed_away:"Armado ausente se utilizará cuando todas las personas salgan de la casa. Todas las puertas y ventanas que permitan el acceso a la casa estarán vigiladas, así como los sensores de movimiento dentro de la casa.",armed_home:"Armado en casa (también conocido como estancia armada) se utilizará cuando se active la alarma mientras haya personas en la casa. Todas las puertas y ventanas que permitan el acceso a la casa estarán protegidas, pero no los sensores de movimiento dentro de la casa.",armed_night:"Armado nocturno se usará al configurar la alarma antes de irse a dormir. Todas las puertas y ventanas que permitan el acceso a la casa estarán resguardadas y se seleccionarán sensores de movimiento en la casa.",armed_vacation:"Armado en vacaciones se puede usar como una extensión del modo armado ausente en caso de ausencia de mayor duración. Los tiempos de retardo y las respuestas de activación se pueden adaptar (como se desee) a estar lejos de casa.",armed_custom_bypass:"Un modo adicional para definir su propio perímetro de seguridad."},number_sensors_active:"{number} {number, plural,\n one {sensor}\n other {sensores}\n} activo",fields:{status:{heading:"Estado",description:"Controla si la alarma se puede armar en este modo."},exit_delay:{heading:"Retardo de salida",description:"Al armar la alarma, dentro de este período de tiempo, los sensores aún no dispararán la alarma."},entry_delay:{heading:"Retardo de entrada",description:"Tiempo de retardo hasta que se activa la alarma después de que se active alguno de los sensores."},trigger_time:{heading:"Tiempo de activación",description:"Tiempo durante el cual sonará la sirena."}}},mqtt:{title:"Configuración MQTT",description:"Este panel se puede utilizar para configurar la interfaz MQTT.",fields:{state_topic:{heading:"Tema del estado",description:"Tema sobre el que se publican las actualizaciones de estado."},event_topic:{heading:"Tema del evento",description:"Tema sobre el que se publican los eventos de alarma."},command_topic:{heading:"Tema del comando",description:"Tema sobre el que se envían los comandos de armado / desarmado."},require_code:{heading:"Requerir código",description:"Requiere que el código se envíe con el comando."},state_payload:{heading:"Configurar la carga útil por estado",item:"Defina una carga útil para el estado ''{state}''"},command_payload:{heading:"Configurar la carga útil por comando",item:"Defina una carga útil para el comando ''{command}''"}}},areas:{title:"Áreas",description:"Las áreas se pueden utilizar para dividir su sistema de alarma en varios compartimentos.",no_items:"Aún no hay áreas definidas.",table:{remarks:"Comentarios",summary:"Esta área contiene {summary_sensors} y {summary_automations}.",summary_sensors:"{number} {number, plural,\n one {sensor}\n other {sensores}\n}",summary_automations:"{number} {number, plural,\n one {automatizacion}\n other {automatizaciones}\n}"},actions:{add:"Agregar"}}},dialogs:{create_area:{title:"Nueva área",fields:{copy_from:"Copiar la configuración de"}},edit_area:{title:"Editando área ''{area}''",name_warning:"Nota: cambiar el nombre cambiará el ID de la entidad."},remove_area:{title:"¿Eliminar área?",description:"¿Está seguro de que desea eliminar esta área? Esta área contiene {sensors} sensores y {automations} automatizaciones que también se eliminarán."},edit_master:{title:"Configuración maestra"},disable_master:{title:"¿Deshabilitar maestro?",description:"¿Está seguro de que desea eliminar la alarma maestra? Esta área contiene {sensors} sensores y {automations} automatizaciones que también se eliminarán."}}},sensors:{title:"Sensores",cards:{sensors:{description:"Sensores configurados actualmente. Haga clic en una entidad para realizar cambios.",table:{no_items:"No hay sensores para mostrar aquí.",no_area_warning:"El sensor no está asignado a ningún área.",arm_modes:"Modos de armado",always_on:"(Siempre)"}},add_sensors:{title:"Agregar sensores",description:"Agrega más sensores. Asegúrate de que tus sensores tengan un nombre amigable, para que puedas identificarlos.",no_items:"No hay entidades HA disponibles que se puedan configurar para la alarma. Asegúrese de incluir entidades del tipo sensor binario.",table:{type:"Tipo detectado"},actions:{add_to_alarm:"agregar a la alarma",filter_supported:"Ocultar elementos con tipo desconocido"}},editor:{title:"Editar sensor",description:"Configurando los ajustes del sensor de ''{entity}''.",fields:{area:{heading:"Área",description:"Seleccione un área que contenga este sensor."},group:{heading:"Grupo",description:"Agrupar con otros sensores para un disparado combinado."},device_type:{heading:"Tipo de dispositivo",description:"Elija un tipo de dispositivo para aplicar automáticamente la configuración adecuada.",choose:{door:{name:"Puerta",description:"Una puerta, portón u otra entrada que se utilice para entrar / salir de la casa."},window:{name:"Ventana",description:"Una ventana o una puerta que no se use para entrar a la casa, como un balcón."},motion:{name:"Movimiento",description:"Sensor de presencia o dispositivo similar que tiene un retardo entre activaciones."},tamper:{name:"Sabotaje",description:"Detector de extracción de la cubierta del sensor, sensor de rotura de vidrio, etc."},environmental:{name:"Medioambiental",description:"Sensor de humo / gas, detector de fugas, etc. (no relacionado con la protección antirrobo)."},other:{name:"Genérico"}}},always_on:{heading:"Siempre encendido",description:"El sensor siempre debe activar la alarma."},modes:{heading:"Modos habilitados",description:"Modos de alarma en los que este sensor está activo."},arm_on_close:{heading:"Armar después de cerrar",description:"Después de la desactivación de este sensor, se saltará automáticamente el retardo de salida restante."},use_exit_delay:{heading:"Usar retardo de salida",description:"Se permite que el sensor esté activo cuando comienza el retardo de salida."},use_entry_delay:{heading:"Usar retardo de entrada",description:"La activación del sensor activa la alarma después del retardo de entrada en lugar de directamente."},allow_open:{heading:"Permitir abrir mientras se arma",description:"Si el sensor aún está activo después del retardo de salida, esto no hará que falle el armado."},auto_bypass:{heading:"Omitir automáticamente",description:"Excluya este sensor de la alarma si está abierto mientras se arma.",modes:"Modos en los que se puede omitir el sensor"},trigger_unavailable:{heading:"Activar cuando no esté disponible",description:"Cuando el estado del sensor se vuelve 'no disponible', esto activará el sensor."}},actions:{toggle_advanced:"Configuración avanzada",remove:"Eliminar",setup_groups:"Configurar grupos"},errors:{description:"Por favor, corrija los siguientes errores:",no_area:"No se ha seleccionado ninguna área",no_modes:"No se han seleccionados modos para los que el sensor deba estar activo",no_auto_bypass_modes:"No se han seleccionados modos para los que el sensor pueda ser omitido"}}},dialogs:{manage_groups:{title:"Administrar grupos de sensores",description:"En un grupo de sensores, se deben activar varios sensores dentro de un período de tiempo antes de que se dispare la alarma.",no_items:"Todavía no hay grupos",actions:{new_group:"Nuevo grupo"}},create_group:{title:"Nuevo grupo de sensores",fields:{name:{heading:"Nombre",description:"Nombre del grupo de sensores"},timeout:{heading:"Tiempo muerto",description:"Período de tiempo durante el cual las activaciones consecutivas del sensor activan la alarma."},sensors:{heading:"Sensores",description:"Seleccione los sensores que están contenidos en este grupo."}},errors:{invalid_name:"Nombre proporcionado no válido.",insufficient_sensors:"Se deben seleccionar al menos 2 sensores."}},edit_group:{title:"Editar grupo de sensores '{name}'"}}},codes:{title:"Códigos",cards:{codes:{description:"Cambiar la configuración del código.",fields:{code_arm_required:{heading:"Usar código de armado",description:"Requiere un código para armar la alarma."},code_disarm_required:{heading:"Usar código de desarmado",description:"Requiere un código para desarmar la alarma."},code_format:{heading:"Formato del código",description:"Establece el tipo de entrada para la tarjeta de la alarma.",code_format_number:"código PIN",code_format_text:"contraseña"}}},user_management:{title:"Gestión de usuarios",description:"Cada usuario tiene su propio código para armar / desarmar la alarma.",no_items:"Aún no hay usuarios",actions:{new_user:"nuevo usuario"}},new_user:{title:"Crear nuevo usuario",description:"Se pueden crear usuarios para proporcionar acceso a la operación de la alarma.",fields:{name:{heading:"Nombre",description:"Nombre del usuario."},code:{heading:"Código",description:"Código para este usuario."},confirm_code:{heading:"Confirmar código",description:"Repite el código."},can_arm:{heading:"Permitir código para armar",description:"Al ingresar este código se activa la alarma."},can_disarm:{heading:"Permitir código para desarmar",description:"Al ingresar este código se desactiva la alarma."},is_override_code:{heading:"Es un código de anulación",description:"Al ingresar este código se forzará el armado de la alarma."},area_limit:{heading:"Áreas restringidas",description:"Limitar al usuario a controlar solo las áreas seleccionadas"}},errors:{no_name:"No se proporcionó ningún nombre.",no_code:"El código debe tener 4 caracteres / números como mínimo.",code_mismatch:"Los códigos no coinciden."}},edit_user:{title:"Editar usuario",description:"Cambiar la configuración del usuario ''{name}''.",fields:{old_code:{heading:"Código actual",description:"Código actual, déjelo en blanco para no modificarlo."}}}}},actions:{title:"Acciones",cards:{notifications:{title:"Notificaciones",description:"Usando este panel, puede administrar las notificaciones que se enviarán durante un evento de alarma determinado.",table:{no_items:"Aún no se han creado notificaciones.",no_area_warning:"La acción no está asignada a ningún área."},actions:{new_notification:"nueva notificación"}},actions:{description:"Este panel se puede utilizar para cambiar un dispositivo cuando cambia el estado de alarma.",table:{no_items:"Aún no se han creado acciones."},actions:{new_action:"nueva acción"}},new_notification:{title:"Crear notificación",description:"Crear una nueva notificación.",trigger:"Condición",action:"Tarea",options:"Opciones",fields:{event:{heading:"Evento",description:"Cuándo debe enviarse la notificación.",choose:{armed:{name:"La alarma está armada",description:"La alarma está correctamente armada."},disarmed:{name:"La alarma está desarmada",description:"La alarma está desarmada."},triggered:{name:"Se ha disparado la alarma",description:"La alarma se ha disparado."},untriggered:{name:"Alarm not longer triggered",description:"The triggered state of the alarm has ended"},arm_failure:{name:"No se pudo armar",description:"El armado de la alarma falló debido a uno o más sensores abiertos."},arming:{name:"Se ha iniciado el retardo de salida",description:"Se ha iniciado el retardo de salida, listo para salir de la casa."},pending:{name:"Se ha iniciado el retardo de entrada",description:"Se ha iniciado el retardo de entrada, la alarma se disparará pronto."}}},mode:{heading:"Modo",description:"Limita la acción a modos de armado específicos (opcional)."},title:{heading:"Título",description:"Título del mensaje de notificación."},message:{heading:"Mensaje",description:"Contenido del mensaje de notificación.",insert_wildcard:"Insertar comodín",placeholders:{armed:"La alarma está configurada en {{arm_mode}}",disarmed:"Ahora la alarma está APAGADA",triggered:"¡Se ha disparado la alarma! Causa: {{open_sensors}}.",untriggered:"The alarm is not longer triggered.",arm_failure:"No se pudo armar la alarma en este momento debido a: {{open_sensors}}.",arming:"Se armará pronto la alarma, por favor, salga de la casa.",pending:"¡La alarma está a punto de dispararse, desarme rápidamente!"}},open_sensors_format:{heading:"Formato para el comodín open_sensors",description:"Elija qué información del sensor se inserta en el mensaje",options:{default:"Nombres y estados",short:"Solo nombres"}},arm_mode_format:{heading:"Traducción del comodín arm_mode",description:"Elija en qué idioma se inserta el modo de armado en el mensaje"},target:{heading:"Objetivo",description:"Dispositivo al que enviar el mensaje push."},name:{heading:"Nombre",description:"Descripción de esta notificación.",placeholders:{armed:"Notificar a {target} al armar",disarmed:"Notificar a {target} al desarmar",triggered:"Notificar a {target} cuando se dispare",untriggered:"Notify {target} when triggering stops",arm_failure:"Notificar a {target} si falla",arming:"Notificar a {target} cuando se vaya",pending:"Notificar a {target} cuando llegue"}},delete:{heading:"Eliminar automatización",description:"Eliminar esta automatización de forma permanente"}},actions:{test:"Pruébelo"}},new_action:{title:"Crear acción",description:"Este panel se puede utilizar para cambiar un dispositivo cuando cambia el estado de la alarma.",fields:{event:{heading:"Evento",description:"¿Cuándo debe ejecutarse la acción?"},area:{heading:"Área",description:"Área para la que se aplica el evento, déjelo en blanco para seleccionar la alarma global."},mode:{heading:"Modo",description:"Limita la acción a modos de armado específicos (opcional)"},entity:{heading:"Entidad",description:"Entidad sobre la que realizar la acción."},action:{heading:"Acción",description:"Acción a realizar en la entidad.",no_common_actions:"Las acciones solo se pueden asignar en modo YAML para las entidades seleccionadas."},name:{heading:"Nombre",description:"Descripción de esta acción.",placeholders:{armed:"Establecer {entity} en {state} al armar",disarmed:"Establecer {entity} en {state} al desarmar",triggered:"Establecer {entity} en {state} cuando se dispare",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Establecer {entity} en {state} si falla",arming:"Establecer {entity} en {state} cuando se vaya",pending:"Establecer {entity} en {state} cuando llegue"}}}}}}},pt={common:ht,components:ut,title:"Panel de alarma",panels:mt},gt=Object.freeze({__proto__:null,common:ht,components:ut,title:"Panel de alarma",panels:mt,default:pt}),vt={modes_short:{armed_away:"Eemal",armed_home:"Kodus",armed_night:"Ööseks",armed_custom_bypass:"Valikuline",armed_vacation:"Vacation"},enabled:"Lubatud",disabled:"Keelatud"},ft={time_slider:{seconds:"sek",minutes:"min",infinite:"piiranguta",none:"puudub"},editor:{ui_mode:"Kasutajaliides",yaml_mode:"Koodiredaktor",edit_in_yaml:"Edit in YAML"},table:{filter:{label:"Filter items",item:"Filter by {name}",hidden_items:"{number} {number, plural,\n one {item is}\n other {items are}\n} hidden"}}},_t={general:{title:"Üldsätted",cards:{general:{description:"Need seaded kehtivad kõikides valve olekutes.",fields:{disarm_after_trigger:{heading:"Häire summutamine",description:"Peale häire lõppu võta valvest maha miite ära valvesta uuesti."},enable_mqtt:{heading:"Luba MQTT juhtimine",description:"Luba nupustiku juhtimist MQTT abil."},enable_master:{heading:"Luba põhivalvestus",description:"Loob olemi mis haldab kõiki valvestamise alasid korraga."}},actions:{setup_mqtt:"MQTT seadistamine",setup_master:"Põhivalvestuse sätted"}},modes:{title:"Režiimid",description:"Selles vaates seadistatakse valvestamise režiime.",modes:{armed_away:"Täielik valvestamine kui kedagi pole kodus. Kasutusel on kõik andurid.",armed_home:"Valvestatud kodus ei kasuta liikumisandureid kuid väisuksed ja aknad on valve all.",armed_night:"Valvestatud ööseks ei kasuta määratud liikumisandureid, välisperimeeter on valve all.",armed_vacation:"Armed vacation can be used as an extension to the armed away mode in case of absence for longer duration. The delay times and trigger responses can be adapted (as desired) to being distant from home.",armed_custom_bypass:"Valikulise valvestuse puhul saab määrata kasutatavad andurid."},number_sensors_active:"{number} {number, plural,\n one {andur}\n other {andurit}\n} aktiiv",fields:{status:{heading:"Status",description:"Controls whether the alarm can be armed in this mode."},exit_delay:{heading:"Ooteaeg valvestamisel",description:"Viivitus enne valvestamise rakendumist."},entry_delay:{heading:"Sisenemise viivitus",description:"Viivitus sisenemisel enne häire rakendumist."},trigger_time:{heading:"Häire kestus",description:"Sireeni jne. aktiveerimise kestus."}}},mqtt:{title:"MQTT sätted",description:"MQTT parameetrite seadistamine.",fields:{state_topic:{heading:"Oleku teema (topic)",description:"Teema milles avaldatakse oleku muutused."},event_topic:{heading:"Event topic",description:"Topic on which alarm events are published"},command_topic:{heading:"Käskude teema (topic)",description:"Teema milles avaldatakse valvestamise käsud."},require_code:{heading:"Nõua PIN koodi",description:"Käskude edastamiseks on vajalik PIN kood."},state_payload:{heading:"Määra olekute toimeandmed",item:"Määra oleku ''{state}'' toimeandmed"},command_payload:{heading:"Määra käskude toimeandmed",item:"Määra käsu ''{command}'' toimeandmed"}}},areas:{title:"Alad",description:"Alasid kasutatakse elamise jagamiseks valvetsoonideks.",no_items:"Valvestamise alad on loomata.",table:{remarks:"Ala teave",summary:"See ala sisaldab {summary_sensors} ja {summary_automations}.",summary_sensors:"{number} {number, plural,\n one {andur}\n other {andurit}\n}",summary_automations:"{number} {number, plural,\n one {automatiseering}\n other {automatiseeringut}\n}"},actions:{add:"Lisa"}}},dialogs:{create_area:{title:"Uus ala",fields:{copy_from:"Kopeeri sätted allikast:"}},edit_area:{title:"Ala ''{area}'' muutmine",name_warning:"NB! Nime muutmisel muutub ka olemi ID"},remove_area:{title:"Kas kustutada ala?",description:"Kas kustutada see ala? Ala kaasab andurid {sensors} ja automatiseeringud {automations} mis samuti eemaldatakse."},edit_master:{title:"Põhiala seaded"},disable_master:{title:"Kas keelata põhiala?",description:"Kas keelata põhiala? Ala kaasab andurid {sensors} ja automatiseeringud {automations} mis samuti eemaldatakse.."}}},sensors:{title:"Andurid",cards:{sensors:{description:"Kasutusel olevad andurid. Klõpsa olemil, et seadistada.",table:{no_items:"Andureid pole lisatud. Alustuseks lisa mõni andur.",no_area_warning:"Sensor is not assigned to any area.",arm_modes:"Valvestamise olek",always_on:"(alati)"}},add_sensors:{title:"Andurite lisamine",description:"Lisa veel andureid. Mõistlik on panna neile arusaadav nimi (friendly_name).",no_items:"Puuduvad valvestamiseks sobivad Home Assistanti olemid. Lisatavad olemid peavad olema olekuandurid (binary_sensor).",table:{type:"Detected type"},actions:{add_to_alarm:"Lisa valvesüsteemile",filter_supported:"Hide items with unknown type"}},editor:{title:"Andurite sätted",description:"Muuda olemi ''{entity}'' sätteid.",fields:{area:{heading:"Ala",description:"Vali ala kus see andur asub."},group:{heading:"Group",description:"Group with other sensors for combined triggering."},device_type:{heading:"Seadme tüüp",description:"Vali anduri tüüp, et automaatselt rakendada sobivad sätted.",choose:{door:{name:"Uks",description:"Uks, värav või muu piire mida kasutatakse sisenemiseks või väljumiseks."},window:{name:"Aken",description:"Aken või uks mida ei kasutata sisenemiseks nagu rõduuks."},motion:{name:"Liikumisandur",description:"Kohaloleku andurid mille rakendumiste vahel on viide."},tamper:{name:"Terviklikkus",description:"Anduri muukimine või klaasipurustusandur jms."},environmental:{name:"Ohu andurid",description:"Suitsu või gaasilekke andur, veeleke jne. (ei ole seotud sissetungimisega)."},other:{name:"Tavaandur"}}},always_on:{heading:"Alati kasutusel",description:"Andur käivitab häire igas valve olekus."},modes:{heading:"Valve olekute valik",description:"Valve olekud kus seda andurit kasutatakse."},arm_on_close:{heading:"Valvesta sulgemisel",description:"Selle anduri rakendumisel valvestatakse kohe ilma viiveta."},use_exit_delay:{heading:"Use exit delay",description:"Sensor is allowed to be active when the exit delay starts."},use_entry_delay:{heading:"Use entry delay",description:"Sensor activation triggers the alarm after the entry delay rather than directly."},allow_open:{heading:"Lahkumisviivitus",description:"See andur ei aktiveeru enne lahkumisviivituse lõppu."},auto_bypass:{heading:"Bypass automatically",description:"Exclude this sensor from the alarm if it is open while arming.",modes:"Modes in which sensor may be bypassed"},trigger_unavailable:{heading:"Andurite saadavus",description:"Käivita häire kui andur muutub kättesaamatuks."}},actions:{toggle_advanced:"Täpsemad sätted",remove:"Eemalda",setup_groups:"Setup groups"},errors:{description:"Palun paranda jägmised vead:",no_area:"Ala pole määratud",no_modes:"Anduri tüüp on määramata, ei tea kuida kasutada",no_auto_bypass_modes:"No modes are selected for the sensor may be automatically bypassed"}}},dialogs:{manage_groups:{title:"Manage sensor groups",description:"In a sensor group multiple sensors must be activated within a time period before the alarm is triggered.",no_items:"No groups yet",actions:{new_group:"New group"}},create_group:{title:"New sensor group",fields:{name:{heading:"Name",description:"Name for sensor group"},timeout:{heading:"Time-out",description:"Time period during which consecutive sensor activations triggers the alarm."},sensors:{heading:"Sensors",description:"Select the sensors which are contained by this group."}},errors:{invalid_name:"Invalid name provided.",insufficient_sensors:"At least 2 sensors need to be selected."}},edit_group:{title:"Edit sensor group ''{name}''"}}},codes:{title:"Koodid",cards:{codes:{description:"Valvestuskoodide muutmine.",fields:{code_arm_required:{heading:"Valvestamine koodiga",description:"Valvestamiseks tuleb sisestada kood"},code_disarm_required:{heading:"Valvest vabastamise kood",description:"Valvest vabastamiseks tulem sisestada kood"},code_format:{heading:"Koodi vorming",description:"Kasutajaliidese koodi tüübid.",code_format_number:"PIN kood",code_format_text:"Salasõna"}}},user_management:{title:"Kasutajate haldus",description:"Igal kasutajal on oma juhtkood.",no_items:"Kasutajaid pole määratud",actions:{new_user:"Uus kasutaja"}},new_user:{title:"Lisa uus kasutaja",description:"Valvesüsteemi kasutaja lisamine.",fields:{name:{heading:"Nimi",description:"Kasutaja nimi."},code:{heading:"Valvestuskood",description:"Selle kasutaja kood."},confirm_code:{heading:"Koodi kinnitamine",description:"Sisesta sama kood uuesti."},can_arm:{heading:"Tohib valvestada",description:"Koodi sisestamine valvestab."},can_disarm:{heading:"Tohib valvest maha võtta",description:"Koodi sisestamine võtab valvest maha."},is_override_code:{heading:"Alistuskood",description:"Koodi sisestamine käivitab kohese häire"},area_limit:{heading:"Restricted areas",description:"Limit user to control only the selected areas"}},errors:{no_name:"Nimi puudub.",no_code:"Kood peab olema vhemalt 4 tärki.",code_mismatch:"Sisestatud koodid ei klapi."}},edit_user:{title:"Muuda kasutaja sätteid",description:"Muuda kasutaja ''{name}'' sätteid.",fields:{old_code:{heading:"Kehtiv kood",description:"Kehtiv kood, jäta tühjaks kui ei taha muuta."}}}}},actions:{title:"Toimingud",cards:{notifications:{title:"Teavitused",description:"Halda saadetavaid teavitusi",table:{no_items:"Teavitusi pole veel loodud.",no_area_warning:"Action is not assigned to any area."},actions:{new_notification:"Uus teavitus"}},actions:{description:"Arenduses, mõeldud seadmete lülitamiseks.",table:{no_items:"Toiminguid pole veel määratud."},actions:{new_action:"Uus toiming"}},new_notification:{title:"Loo teavitus",description:"Uue teavituse loomine.",trigger:"Condition",action:"Task",options:"Options",fields:{event:{heading:"Sündmus",description:"Mille puhul teavitada",choose:{armed:{name:"Valvestatud",description:"Valvestamine oli edukas"},disarmed:{name:"Valvest maas",description:"Valve mahavõtmine õnnestus"},triggered:{name:"Häire",description:"Valvesüsteem andis häire"},untriggered:{name:"Alarm not longer triggered",description:"The triggered state of the alarm has ended"},arm_failure:{name:"Valvestamine nurjus",description:"Valvestamine ei õnnestunud mõne anduri oleku või vea tõttu"},arming:{name:"Valvestamise eelne viivitus algas",description:"Algas valvestamise eelviide, majast võib lahkuda."},pending:{name:"Sisenemise viide rakendus",description:"Märgati sisenemist, häire rakendub peale viidet."}}},mode:{heading:"Olek",description:"Millises valve olekus teavitada (valikuline)"},title:{heading:"Päis",description:"Teavitussõnumi päis"},message:{heading:"Sisu",description:"Teavitussõnumi tekst",insert_wildcard:"Insert wildcard",placeholders:{armed:"The alarm is set to {{arm_mode}}",disarmed:"The alarm is now OFF",triggered:"The alarm is triggered! Cause: {{open_sensors}}.",untriggered:"The alarm is not longer triggered.",arm_failure:"The alarm could not be armed right now, due to: {{open_sensors}}.",arming:"The alarm will be armed soon, please leave the house.",pending:"The alarm is about to trigger, disarm it quickly!"}},open_sensors_format:{heading:"Format for open_sensors wildcard",description:"Choose which sensor information in inserted in the message",options:{default:"Names and states",short:"Names only"}},arm_mode_format:{heading:"Translation for arm_mode wildcard",description:"Choose in which language the arm mode is inserted in the message"},target:{heading:"Saaja",description:"Seade millele edastada teavitus"},name:{heading:"Nimi",description:"Teavituse kirjeldus",placeholders:{armed:"Notify {target} upon arming",disarmed:"Notify {target} upon disarming",triggered:"Notify {target} when triggered",untriggered:"Notify {target} when triggering stops",arm_failure:"Notify {target} on failure",arming:"Notify {target} when leaving",pending:"Notify {target} when arriving"}},delete:{heading:"Delete automation",description:"Permanently remove this automation"}},actions:{test:"Try it"}},new_action:{title:"Loo toiming",description:"Seadme oleku muutmine valve oleku muutmisel.",fields:{event:{heading:"Sündmus",description:"Millisel juhul käivitada toiming"},area:{heading:"Ala",description:"Ala millele sündmus rakendub, põhiala puhul jäta tühjaks."},mode:{heading:"Olek",description:"Millises valve olekus toiming käivitada (valikuline)"},entity:{heading:"Olem",description:"Toimingu olem"},action:{heading:"Toiming",description:"Olemi toiming",no_common_actions:"Actions can only be assigned in YAML mode for the selected entities."},name:{heading:"Nimi",description:"Toimingu kirjeldus",placeholders:{armed:"Set {entity} to {state} upon arming",disarmed:"Set {entity} to {state} upon disarming",triggered:"Set {entity} to {state} when triggered",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Set {entity} to {state} on failure",arming:"Set {entity} to {state} when leaving",pending:"Set {entity} to {state} when arriving"}}}}}}},bt={common:vt,components:ft,title:"Alarm panel",panels:_t},yt=Object.freeze({__proto__:null,common:vt,components:ft,title:"Alarm panel",panels:_t,default:bt}),wt={modes_short:{armed_away:"Absence",armed_home:"Présence",armed_night:"Nuit",armed_custom_bypass:"Personnalisé",armed_vacation:"Vacances"},enabled:"Actif",disabled:"Inactif"},kt={time_slider:{seconds:"sec",minutes:"min",infinite:"infini",none:"Aucune"},editor:{ui_mode:"Afficher l'éditeur visuel",yaml_mode:"Afficher l'éditeur de code",edit_in_yaml:"Editer en YAML"},table:{filter:{label:"Filtrer par items",item:"Filtrer par {name}",hidden_items:"{number} {number, plural,\n one { item est caché}\n other { items sont cachés}\n} "}}},$t="Configuration de l' alarme",At={general:{title:"Généraux",cards:{general:{description:"Ce panneau définit les paramètres globaux de l'alarme.",fields:{disarm_after_trigger:{heading:"Désactivation après déclenchement",description:"Lors que le temps de fonctionnement de la sirène est écoulé, désactive l'alarme au lieu de la réactiver."},enable_mqtt:{heading:"Utilisation avec MQTT",description:"Permet au panneau d'alarme d'être contrôlé via MQTT."},enable_master:{heading:"Activation de commande centralisée",description:"Créer une entité pour piloter toutes les zones en même temps."}},actions:{setup_mqtt:"Configuration MQTT",setup_master:"Configuration pricipale"}},modes:{title:"Modes",description:"Ce panneau définit le mode de gestion pour chaque type d'activation.",modes:{armed_away:"Ce mode sera utilisé lorsque toutes les personnes auront quitté la maison. Toutes les portes et fenêtres permettant l'accès à la maison seront surveillées, les détecteurs de mouvement à l'intérieur de la maison seront opérationnels.",armed_home:"Ce mode sera utilisée lorsque des personnes sont dans la maison. Toutes les portes et fenêtres permettant l'accès à la maison seront surveillées (périmétrie), les détecteurs de mouvement à l'intérieur de la maison seront inopérants.",armed_night:"Ce mode sera utilisé lors du réglage de l'alarme avant de s'endormir. Toutes les portes et fenêtres permettant l'accès à la maison seront surveillées, et les capteurs de mouvement sélectionnés (ex : rez de chaussée) dans la maison seront opérationnels.",armed_vacation:"Ce mode peut être utilisé comme une extension du mode armé absent en cas d'absence pour une durée plus longue. Les temps de retard et les réponses de déclenchement peuvent être adaptés (au choix) à l'éloignement du domicile.",armed_custom_bypass:"Ce mode supplémentaire permet de définir votre propre périmètre de sécurité."},number_sensors_active:"{number} {number, plural,\n one {capteur actif}\n other {capteurs actifs}\n} ",fields:{status:{heading:"Statut",description:"Active l'alarme dans ce mode."},exit_delay:{heading:"Délai pour sortir",description:"Lors de l'activation, pendant cette période, les capteurs ne déclencheront pas l'alarme."},entry_delay:{heading:"Délai pour entrer",description:"Temps d'attente avant que l'alarme ne se déclenche après détection d'un des capteurs."},trigger_time:{heading:"Temps de fonctionnement",description:"Temps de fonctionnement de la sirène"}}},mqtt:{title:"Configuration MQTT",description:"Ce panneau peut être utilisé pour la configuration de l'interface MQTT.",fields:{state_topic:{heading:"Etat des données",description:"Donnée sur laquelle les mises à jour d'état sont publiées"},event_topic:{heading:"Evènement de données",description:"Donnée sur laquelle les évènements d'état sont publiés"},command_topic:{heading:"Commande de données",description:"Donnée sur laquelle les commandes d'armement / désarmement sont envoyées."},require_code:{heading:"Code requis",description:"Exige que le code soit envoyé avec la commande."},state_payload:{heading:"Configurer une valeur par état",item:"Définir une valeur par état ''{state}''"},command_payload:{heading:"Configurer une valeur par commande",item:"Définir une valeur par commande ''{command}''"}}},areas:{title:"Zones",description:"Les zones peuvent être utilisées pour diviser votre système d'alarme en plusieurs secteurs.",no_items:"Il n'y a pas encore de zone définie.",table:{remarks:"Remarque",summary:"Cette zone contient des {summary_sensors} et {summary_automations}.",summary_sensors:"{number} {number, plural,\n one {capteur}\n other {capteurs}\n}",summary_automations:"{number} {number, plural,\n one {automatisation}\n other {automatisations}\n}"},actions:{add:"Ajouter"}}},dialogs:{create_area:{title:"Nouvelle zone",fields:{copy_from:"Copier les paramètres"}},edit_area:{title:"Editer la zone ''{area}''",name_warning:"Note: Changer le nom, changera l'entity ID"},remove_area:{title:"Suppression de zone?",description:"Etes vous sur de vouloir supprimer cette zone? Cette zone contient {sensors} des capteurs et {automations} automatisations, qui seront également supprimés."},edit_master:{title:"Configuration principale"},disable_master:{title:"Désactiver la configuration principale?",description:"Etes vous sur de vouloir supprimer la configuration principale? Cette zone contient {automations} automatisations, qui seront également supprimées."}}},sensors:{title:"Capteurs",cards:{sensors:{description:"Capteurs actuellement configurés. Cliquez sur une entité pour apporter des modifications.",table:{no_items:"Il n'y a pas encore de capteur ajouté à l'alarme. Assurez-vous de les ajouter d'abord.",no_area_warning:"Le capteur n'est affecté à aucune zone.",arm_modes:"Type d'activation",always_on:"(Toujours)"}},add_sensors:{title:"Ajouter un capteur",description:"Ajoutez plus de capteurs. Assurez-vous que vos capteurs ont un nom personnalisé afin de pouvoir les identifier.",no_items:"Aucune entité HA disponible ne peut être configurée pour l'alarme. Assurez-vous d'inclure les entités de type binary_sensor.",table:{type:"Type de détection"},actions:{add_to_alarm:"Ajouter à l'alarme",filter_supported:"Masquer les éléments de type inconnu"}},editor:{title:"Editer un capteur",description:"Configurer les paramètres du capteur ''{entity}''.",fields:{area:{heading:"Zone",description:"Selectionner une zone contenant ce capteur."},group:{heading:"Groupe",description:"Grouper avec d'autres capteurs pour un déclenchement combiné."},device_type:{heading:"Type de détection",description:"Choisissez un type de détection pour appliquer automatiquement les paramètres appropriés.",choose:{door:{name:"Porte",description:"Une porte, un portail ou une autre entrée utilisée pour entrer / sortir de la maison."},window:{name:"Fenêtre",description:"Une fenêtre, ou une porte non utilisée pour entrer dans la maison comme un balcon."},motion:{name:"Mouvement",description:"Capteur de présence ou appareil similaire présentant un délai entre les activations."},tamper:{name:"Effraction",description:"Détection d'arrachage du capteur, capteur de bris de verre, etc.."},environmental:{name:"Détecteur Environmental",description:"Détecteur de fumée / gaz, détecteur de fuite, etc. (non lié à la protection anti-effraction)."},other:{name:"Générique"}}},always_on:{heading:"Toujours en service",description:"Le capteur doit toujours déclencher l'alarme."},modes:{heading:"Mode possible",description:"Modes d'alarme dans lesquels ce capteur est actif."},arm_on_close:{heading:"Activer après fermeture",description:"Après la désactivation de ce capteur, le délai de sortie restant sera automatiquement ignoré."},use_exit_delay:{heading:"Utiliser le délai de sortie",description:"Le capteur sera actif à la fin du délai de sortie."},use_entry_delay:{heading:"Utiliser le délai d'entrée",description:"L'activation du capteur déclenche l'alarme après le délai d'entrée plutôt qu'instantanément."},allow_open:{heading:"Autoriser l'ouverture lors de l'activation",description:"Permet à ce capteur d'être actif, peu de temps après votre départ afin qu'il ne bloque pas l'armement."},auto_bypass:{heading:"Bypass automatique",description:"Exclut ce capteur de l'alarme s'il est ouvert lors de l'armement.",modes:"Modes dans lesquels le capteur peut être ignoré"},trigger_unavailable:{heading:"Déclenchement lorsqu'il n'est pas disponible",description:"Lorsque l'état du capteur devient `` indisponible '', cela activera l'alarme."}},actions:{toggle_advanced:"Paramètres avancées",remove:"Supprimer",setup_groups:"Configuration de Groupe"},errors:{description:"Veuillez corriger les erreurs suivantes:",no_area:"Aucune zone n'est sélectionnée",no_modes:"Aucun mode sélectionné pour lequel le capteur doit être actif",no_auto_bypass_modes:"Aucun mode n'est sélectionné car le capteur peut être automatiquement ignoré"}}},dialogs:{manage_groups:{title:"Gérer les groupes de capteurs",description:"Dans un groupe de capteurs, plusieurs capteurs doivent être activés dans un laps de temps avant que l'alarme ne se déclenche.",no_items:"Aucun groupe",actions:{new_group:"Nouveau groupe"}},create_group:{title:"Nouveau groupe de capteurs",fields:{name:{heading:"Nom",description:"Nom du nouveau groupe de capteurs"},timeout:{heading:"Laps de temps",description:"Période de temps pendant laquelle les activations consécutives du capteur déclenchent l'alarme."},sensors:{heading:"Capteurs",description:"Sélectionnez les capteurs qui sont contenus dans ce groupe."}},errors:{invalid_name:"Nom fourni non valide.",insufficient_sensors:"Au moins 2 capteurs doivent être sélectionnés."}},edit_group:{title:"Editer le groupe de capteurs ''{name}''"}}},codes:{title:"Codes",cards:{codes:{description:"Gestion des paramètres des codes.",fields:{code_arm_required:{heading:"Utiliser un code pour l'activation",description:"Code requis pour l'activation de l'alarme"},code_disarm_required:{heading:"Utiliser un code pour la désactivation",description:"Code requis pour la désactivation de l'alarme"},code_format:{heading:"Format du code",description:"Définit le type d'entrée pour la carte d'alarme Lovelace.",code_format_number:"pincode",code_format_text:"password"}}},user_management:{title:"Gestion des utilisateurs",description:"Chaque utilisateur a son propre code pour activer / désactiver l'alarme.",no_items:"Il n'y a aucun utilisateur de défini",actions:{new_user:"Nouvel utilisateur"}},new_user:{title:"Créer un nouvel utilisateur",description:"Des utilisateurs peuvent être créés pour donner accès au fonctionnement de l'alarme.",fields:{name:{heading:"Nom",description:"Nom de l'utilisateur."},code:{heading:"Code",description:"Code personnel de l'utilisateur."},confirm_code:{heading:"Confirmation du code",description:"Répèter le code."},can_arm:{heading:"Demande de code pour l'activation",description:"Entrer ce code pour activer l'alarme."},can_disarm:{heading:"Demande de code pour désactivation",description:"Entrer ce code pour désactiver l'alarme."},is_override_code:{heading:"Code de sécurité",description:"La saisie de ce code forcera l'activation l'alarme."},area_limit:{heading:"Zones Restreintes",description:"L'utilisateur ne peut contrôler uniquement les zones sélectionnées"}},errors:{no_name:"Aucun nom saisi.",no_code:"Le code doit contenir 4 caractères/chiffres minimum.",code_mismatch:"Les codes sont différents."}},edit_user:{title:"Editer l'utilisateur",description:"Changer la configuration pour l'utilisateur ''{name}''.",fields:{old_code:{heading:"Code utilisé",description:"Code actuel, laissez vide pour ne rien changer."}}}}},actions:{title:"Actions",cards:{notifications:{title:"Notifications",description:"À l'aide de ce panneau, vous pouvez gérer les notifications à envoyer lors d'un évènement d'alarme",table:{no_items:"Il n'y a aucune notification de créée.",no_area_warning:"L'action n'est affectée à aucune zone."},actions:{new_notification:"Nouvelle notification"}},actions:{description:"Ce panneau est utilisé pour changer d'état les appareils de votre choix.",table:{no_items:"Il n'y a aucune action de créer."},actions:{new_action:"Nouvelle action"}},new_notification:{title:"Créer une notification",description:"Créer une nouvelle notification.",trigger:"Condition",action:"Action",options:"Options",fields:{event:{heading:"Evènement",description:"Détermine quand la notification doit être envoyée",choose:{armed:{name:"Alarme activée ",description:"l'alarme s'est correctement activée"},disarmed:{name:"Alarme désactivée",description:"L'alarme est désactivée"},triggered:{name:"Alarme déclenchée",description:"L'alarme est déclenchée"},untriggered:{name:"Alarm not longer triggered",description:"The triggered state of the alarm has ended"},arm_failure:{name:"Armement impossible",description:"L'armement est impossible dû à un ou plusieurs capteurs"},arming:{name:"Délai de sortie activé",description:"Le délai de sortie est activé, vous devez quitter la maison."},pending:{name:"Délai d'entrée activé",description:"Le délai d'entrée est activé, sans action de désarmement, l'alarme va se déclencher."}}},mode:{heading:"Mode",description:"Limite la notification à un mode spécifique (optionnel)"},title:{heading:"Titre",description:"Titre du message de la notification"},message:{heading:"Message",description:"Contenu du message de la notification",insert_wildcard:"Inserer la wildcard",placeholders:{armed:"L'alarme est réglée sur {{arm_mode}}",disarmed:"L'alarme est maintenant désactivée",triggered:"L'alarme est déclenchée! En cause: {{open_sensors}}.",untriggered:"The alarm is not longer triggered.",arm_failure:"L'alarme n'a pas pu être armée pour le moment, en cause: {{open_sensors}}.",arming:"L'alarme sera bientôt armée, veuillez quitter la maison.",pending:"L'alarme est sur le point de se déclencher, désarmez-la rapidement!"}},open_sensors_format:{heading:"Format pour les 'open_sensors wildcard'",description:"Choisissez les informations du capteur à insérer dans le message",options:{default:"Noms et états",short:"Noms seulement"}},arm_mode_format:{heading:"Traduction pour 'arm_mode wildcard'",description:"Choisissez dans quelle langue le mode d'armement est inséré dans le message"},target:{heading:"Cible",description:"Appareil recevant le message"},name:{heading:"Nom",description:"Description de la notification",placeholders:{armed:"Notification à l'armement de : {target}",disarmed:"Notification au désarmement de : {target}",triggered:"Notification au déclenchement de : {target}",untriggered:"Notify {target} when triggering stops",arm_failure:"Notification en cas d'échec de : {target}",arming:"Notification en quittant de : {target}",pending:"Notification au retour de : {target}"}},delete:{heading:"Supprimer l'automatisme",description:"Supprimer définitivement cet automatisme"}},actions:{test:"Essai"}},new_action:{title:"Créer une action",description:"Ce panneau peut être utilisé pour commuter un appareil lorsque l'état de l'alarme change.",fields:{event:{heading:"Evènement",description:"Détermine quand l'action doit être exécutée"},area:{heading:"Zone",description:"Zone pour laquelle l'évènement s'applique, laissez vide pour sélectionner l'alarme globale."},mode:{heading:"Mode",description:"Limite l'action à un mode spécifique (optionnel)"},entity:{heading:"Entité",description:"Entité sur laquelle effectuer une action"},action:{heading:"Action",description:"Action à exécuter sur l'entité",no_common_actions:"Les actions ne peuvent être affectées qu'en mode YAML pour les entités sélectionnées."},name:{heading:"Nom",description:"Description de l'action",placeholders:{armed:"Mettre {entity} à {state} lors de l'armement",disarmed:"Mettre {entity} à {state} lors du désarmement",triggered:"Mettre {entity} à {state} lors du déclenchement de l'alarme",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Mettre {entity} à {state} en cas d'échec de l'armement",arming:"Mettre {entity} à {state} lors du départ de la maison",pending:"Mettre {entity} à {state} lors du retour à la maison"}}}}}}},xt={common:wt,components:kt,title:$t,panels:At},Ot=Object.freeze({__proto__:null,common:wt,components:kt,title:$t,panels:At,default:xt}),Et={modes_short:{armed_away:"Fuori casa",armed_home:"In casa",armed_night:"Notte",armed_custom_bypass:"Personalizzato",armed_vacation:"Vacanza"},enabled:"Abilitato",disabled:"Disabilitato"},Tt={time_slider:{seconds:"sec",minutes:"min",infinite:"infinito",none:"niente"},editor:{ui_mode:"Passa a UI",yaml_mode:"Passa a YAML",edit_in_yaml:"Modifica in YAML"},table:{filter:{label:"Filtra elementi",item:"Filtra per {name}",hidden_items:"{number} {number, plural,\n one {item is}\n other {items are}\n} hidden"}}},jt={general:{title:"Generale",cards:{general:{description:"Questo pannello definisce alcune impostazioni da applicare alle modalità di allarme.",fields:{disarm_after_trigger:{heading:"Disattiva allarme dopo l'attivazione",description:"Dopo che il tempo di attivazione è scaduto, disattivare l'allarme invece di tornare allo stato inserito."},enable_mqtt:{heading:"Abilita MQTT",description:"Permetti al pannello allarme di essere controllato attraverso MQTT."},enable_master:{heading:"Abilita Allarme Master",description:"Crea una entità per controllare tutte le aree simultaneamente."}},actions:{setup_mqtt:"Configurazione MQTT",setup_master:"Configurazione Master"}},modes:{title:"Modalità",description:"Questo pannello può essere usato per impostare le modalità dell'allarme.",modes:{armed_away:"Modalità 'fuori casa': da utilizzare quando tutte le persone lasciano la casa. Tutti i sensori di porte e finestre che consentono l'accesso alla casa saranno attivi, così come i sensori di movimento all'interno della casa.",armed_home:"Modalità 'in casa': da utilizzare quando si attiva l'allarme mentre le persone sono in casa. Tutti i sensori di porte e finestre che consentono l'accesso alla casa saranno attivi, ma non i sensori di movimento all'interno della casa.",armed_night:"Modalità 'notte': da utilizzare quando si imposta la sveglia prima di andare a dormire. Tutti i sensori di porte e finestre che consentono l'accesso alla casa saranno attivi e sensori di movimento selezionati (ad esempio al piano di sotto) nella casa.",armed_vacation:"Modalità 'vacanza': da utlizzare come estensione della modalità 'fuori casa' in caso di assenza prolungata. I ritardi e i tempi di attivazione possono essere adattati per essere distanti da casa.",armed_custom_bypass:"Modalità 'personalizzato': da utilizzare per definire una modalità di allarme specifica per le esigenze dell'utilizzatore."},number_sensors_active:"{number} {number, plural,\n one {sensor}\n other {sensors}\n} active",fields:{status:{heading:"Stato",description:"Definisce quando l'allarme può essere armato in questa modalità."},exit_delay:{heading:"Tempo di preattivazione",description:"Quando si attiva l'allarme, entro questo periodo di tempo i sensori non attiveranno ancora l'allarme."},entry_delay:{heading:"Ritardo di attivazione",description:"Tempo di ritardo fino allo scatto dell'allarme dopo l'attivazione di uno dei sensori."},trigger_time:{heading:"Tempo di attivazione",description:"Tempo durante il quale suonerà la sirena."}}},mqtt:{title:"Configurazione MQTT",description:"Questo pannello può essere usato per le impostazioni MQTT.",fields:{state_topic:{heading:"Topic di stato",description:"Topic su cui vengono pubblicati gli aggiornamenti di stato"},event_topic:{heading:"Event topic",description:"opic su cui vengono pubblicati gli eventi"},command_topic:{heading:"Topic di comando",description:"Topic su cui vengono inviati i comandi di inserimento / disinserimento."},require_code:{heading:"Richiedi Codice",description:"Richiedi il codice da inviare con il comando."},state_payload:{heading:"Configura payload per stato",item:"Definisci un payload per lo stato ''{state}''"},command_payload:{heading:"Configura payload per comando",item:"Definisci un payload per il comando ''{command}''"}}},areas:{title:"Aree",description:"Le aree possono essere utilizzate per dividere il tuo allarme in più sezioni.",no_items:"Non ci sono ancora aree definite.",table:{remarks:"Commenti",summary:"Questa area contiene {summary_sensors} e {summary_automations}.",summary_sensors:"{number} {number, plural,\n one {sensor}\n other {sensors}\n}",summary_automations:"{number} {number, plural,\n one {automation}\n other {automations}\n}"},actions:{add:"Add"}}},dialogs:{create_area:{title:"Nuova area",fields:{copy_from:"Copia impostazioni da"}},edit_area:{title:"Modifica Area ''{area}''",name_warning:"Nota: cambiare il nome modificherà l'entity ID"},remove_area:{title:"Rimuovi Area?",description:"Sei sicuro che vuoi rimuovere questa area? Questa area contiene {sensors} sensori e {automations} automazioni, che verranno anch'esse rimossi."},edit_master:{title:"Configura Master"},disable_master:{title:"Disabilita Master?",description:"Sei sicuro che vuoi rimuovere l'allarme master? Questa area contiene {automations} automazioni, che verranno eliminate con questa azione."}}},sensors:{title:"Sensori",cards:{sensors:{description:"Sensori attualmente configurati. Clicca sull'entità per modificare.",table:{no_items:"Non ci sono ancora sensori aggiunti a questo allarme. Assicurati di aggiungerli prima.",no_area_warning:"Sensore non assegnato a nessuna area.",arm_modes:"Modalità di attivazione",always_on:"(Sempre)"}},add_sensors:{title:"Aggiungi Sensori",description:"Aggiungi più sensori. Assicurati che i sensori abbiano un friendly_name (nome amichevole), in modo da identificarli più facilmente.",no_items:"Non ci sono entità disponibili che possono essere configurate con l'allarme. Assicurati di includere entità del tipo binary_sensor (sensore binario).",table:{type:"Tipologia Innesco"},actions:{add_to_alarm:"aggiungi all'allarme",filter_supported:"Nascondi elementi con tipologia sconosciuta"}},editor:{title:"Modifica Sensore",description:"Configura le impostazioni del sensore ''{entity}''.",fields:{area:{heading:"Area",description:"Seleziona una area che contiene questo sensore."},group:{heading:"Gruppo",description:"Raggruppa con altri sensori per inneschi combinati."},device_type:{heading:"Tipologia Dispositivo",description:"Scegli la tipologia del dispositivo per applicare le impostazioni appropriate.",choose:{door:{name:"Porta",description:"Una porta, cancello o altro ingresso che è usato per entrare/lasciare casa."},window:{name:"Finestra",description:"Una finestra, o una porta-finestra non usata per accedere alla casa."},motion:{name:"Movimento",description:"Sensore di presenza o simile che ha un ritardo tra le attivazioni."},tamper:{name:"Vibrazione",description:"Rilaveamento di vibrazione, rottura vetri, ecc."},environmental:{name:"Ambientale",description:"Rilevatori fumo/gas, ecc. (non correlati alla protezione intrusi)."},other:{name:"Generico"}}},always_on:{heading:"Sempre attivo",description:"Il sensore attiverà sempre l'allarme."},modes:{heading:"Modalità attive",description:"Modalità di allarme in cui il sensore risulta collegato."},arm_on_close:{heading:"Attiva dopo chisura after closing",description:"Dopo la disattivazione di questo sensore il ritardo rimanente verrà automaticamente skippato."},use_exit_delay:{heading:"Usa Ritardo d'uscita",description:"Sensore che può rimanre attivo mentre il ritardo di uscita è in corso."},use_entry_delay:{heading:"Usa ritardo in ingresso",description:"Sensore che innesca l'allarme dopo il ritardo in ingresso anzichè direttamente."},allow_open:{heading:"Permetti apertura",description:"Consentire a questo sensore di rimanere attivo poco dopo essere usciti."},auto_bypass:{heading:"Bypass automatico",description:"Escludi questo sensore dall'allarme se è aperto durante l'attivazione.",modes:"Modalità in cui il sensore può essere bypassato"},trigger_unavailable:{heading:"Fai scattare l'allarme quando non disponibile",description:"L'allarme scatterà quando lo stato del sensore diverrà 'non disponibile'."}},actions:{toggle_advanced:"Impostazione avanzate",remove:"Rimuovi",setup_groups:"Setup gruppi"},errors:{description:"Per favore correggi i seguenti errori:",no_area:"Nessuna area è selezionata",no_modes:"Nessuna modalità è selezionata per la quale il sensore dovrebbe essere attivo",no_auto_bypass_modes:"Nessuna modalità è selezionata per il sensore che può essere automaticamente bypassato"}}},dialogs:{manage_groups:{title:"Gestisci gruppi sensori",description:"In un gruppo sensori più sensori devono essere attivi in un intevallo di tempo prima che l'allarme sia innescato.",no_items:"Nessun gruppo",actions:{new_group:"Nuovo gruppo"}},create_group:{title:"Nuovo gruppo sensori",fields:{name:{heading:"Nome",description:"Nome del gruppo sensori"},timeout:{heading:"Time-out",description:"Periodo di tempo durante il quale l'attivazione consecutiva innesca l'allarme."},sensors:{heading:"Sensori",description:"Seleziona i sensori che fanno parte di questo gruppo."}},errors:{invalid_name:"Nome non valido.",insufficient_sensors:"Almeno 2 sensori devono essere selezionati."}},edit_group:{title:"Modifica gruppo sensori ''{name}''"}}},codes:{title:"Codici",cards:{codes:{description:"Modifica le impostazioni dei codici.",fields:{code_arm_required:{heading:"Usa codice d'attivazione",description:"Richiedi un codice per attivare l'allarme"},code_disarm_required:{heading:"Usa codice di disattivazione",description:"Richiedi un codice per disattivare l'allarme"},code_format:{heading:"Formato del codice",description:"Imposta il tipo di codice da digitare nella card di Lovelace.",code_format_number:"codice numerico",code_format_text:"password"}}},user_management:{title:"Gestione utente",description:"Ogni utente ha il suo codice per attivare/disattivare l'allarme.",no_items:"Non è stato creato nessun utente per ora",actions:{new_user:"Nuovo utente"}},new_user:{title:"Crea nuovo utente",description:"Gli utenti potranno operare con l'allarme.",fields:{name:{heading:"Nome",description:"Nome dell'utente."},code:{heading:"Codice operativo",description:"Codice che utilizzerà quest'utente."},confirm_code:{heading:"Ripeti codice operativo",description:"Ripeti il codice operativo scelto."},can_arm:{heading:"Utilizza codice per attivare l'allarme",description:"Utilizza codice per attivare l'allarme"},can_disarm:{heading:"Utilizza codice per disattivare l'allarme",description:"Utilizza codice per disattivare l'allarme"},is_override_code:{heading:"E' un codice di forzatura",description:"Inserendo questo codice forzerai lo stato di attivazione dell'allarme"},area_limit:{heading:"Aree riservate areas",description:"Limita l'utente a controllare solo le aree selezionate"}},errors:{no_name:"Non hai inserito il nome.",no_code:"Il codice deve avere almeno 4 numeri o caratteri.",code_mismatch:"Il codice scelto non combacia, verifica il codice inserito."}},edit_user:{title:"Modifica Utente",description:"Cambia impostazioni per l'utente ''{name}''.",fields:{old_code:{heading:"Modifica Codice",description:"Codice attuale, lascia vuoto per non modificare."}}}}},actions:{title:"Azioni",cards:{notifications:{title:"Notifiche",description:"Con questo pannello puoi gestire le notifiche da inviare quanto accade un determinato evento",table:{no_items:"Non è stata creata nessuna notifica per ora.",no_area_warning:"Azione non assegnata a nessuna."},actions:{new_notification:"Nuova notifica"}},actions:{description:"Questo pannello è in fase di sviluppo. Sarà usato per cambiare lo stato di una o più entità.",table:{no_items:"Non è stata creata nessuna azione per ora."},actions:{new_action:"Nuova azione"}},new_notification:{title:"Crea notifica",description:"Crea una nuova notifica.",trigger:"Condizione",action:"Azione",options:"Opzioni",fields:{event:{heading:"Evento",description:"Quando questa notifica deve essere inviata",choose:{armed:{name:"Allarme attivato",description:"L'allarme è attivo"},disarmed:{name:"Allarme disattivato",description:"L'allarme è disattivato"},triggered:{name:"Allarme innescato",description:"L'allarme è innescato"},untriggered:{name:"Alarm not longer triggered",description:"The triggered state of the alarm has ended"},arm_failure:{name:"Impossibile attivare",description:"L'attivazione dell'allarme non è riuscita a casa di uno o più sensori aperti"},arming:{name:"Ritardo d'uscita partito",description:"Ritardo d'uscita partito, preparati a lasciare la casa."},pending:{name:"Ritardo in ingresso partito",description:"Ritardo in ingresso partito, l'allarme verrà innescato a breve."}}},mode:{heading:"Modalità",description:"Limita ad una specifica modalità di allarme (opzionale)"},title:{heading:"Titolo",description:"Titolo per il messaggio di notifica"},message:{heading:"Messaggio",description:"Contenuto del messaggio di notifica",insert_wildcard:"Inserisci wildcard",placeholders:{armed:"L'allarme è impostato in {{arm_mode}}",disarmed:"L'allarme è disattivatoF",triggered:"L'allarme è stato innescato! Causa: {{open_sensors}}.",untriggered:"The alarm is not longer triggered.",arm_failure:"L'allarme non può essere attivato adesso. Causa: {{open_sensors}}.",arming:"L'allarme verrà attivato a breve, per favore lascia la casa.",pending:"L'allarme sta per essere innescato, disattivalo velocemente!"}},open_sensors_format:{heading:"Formato per la wildcard open_sensors",description:"Scegli quale informazione è inserita nel messaggio",options:{default:"Nomi e stati",short:"Nomi soltanto"}},arm_mode_format:{heading:"Traduzione per le wildcard per arm_mode",description:"Scegli la lingua in cui è scritto il messaggio"},target:{heading:"Destinatario",description:"Dispositivo a cui inviare il messaggio di notifica"},name:{heading:"Nome",description:"Descrizione della notifica",placeholders:{armed:"Notifica {target} in attivazione",disarmed:"Notifica {target} in disattivazione",triggered:"Notifica {target} quando innescato",untriggered:"Notify {target} when triggering stops",arm_failure:"Notifica {target} quando impossibile attivare",arming:"Notifica {target} in uscita",pending:"Notifica {target} in ingresso"}},delete:{heading:"Elimina automazione",description:"Elimina l'automazione permanentemente"}},actions:{test:"Prova"}},new_action:{title:"Crea azione",description:"Questo pannello può essere usato per cambiare lo stato di un entità quando lo stato dell'allarme cambia.",fields:{event:{heading:"Evento",description:"Quando questa azione deve essere eseguita"},area:{heading:"Area",description:"Area nella quale l'evento avviene, lascia vuoti per selezionare l'intero allarme."},mode:{heading:"Modalità",description:"Limita ad una specifica modalità di allarme (opzionale)"},entity:{heading:"Entità",description:"Entità su cui eseguire l'azione"},action:{heading:"Azione",description:"Azione che deve eseguire l'entità",no_common_actions:"Le azioni possono essere definite solo in YAML mode per le entità selezionate."},name:{heading:"Nome",description:"Descrizione dell'azione",placeholders:{armed:"Imposta {entity} su {state} in attivazione",disarmed:"Imposta {entity} su {state} in disattivazione",triggered:"Imposta {entity} su {state} in innesco",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Imposta {entity} su {state} quando è impossibile attivare",arming:"Imposta {entity} su {state} in uscita",pending:"Imposta {entity} su {state} in entrata"}}}}}}},St={common:Et,components:Tt,title:"Pannello Allarme",panels:jt},Ct=Object.freeze({__proto__:null,common:Et,components:Tt,title:"Pannello Allarme",panels:jt,default:St}),Mt={modes_short:{armed_away:"Afwezig",armed_home:"Thuis",armed_night:"Nacht",armed_custom_bypass:"Aangepast",armed_vacation:"Vakantie"},enabled:"Actief",disabled:"Inactief"},Nt={time_slider:{seconds:"sec",minutes:"min",infinite:"oneindig",none:"geen"},editor:{ui_mode:"Naar UI",yaml_mode:"Naar YAML",edit_in_yaml:"In YAML bewerken"},table:{filter:{label:"Items filteren",item:"Filter op {name}",hidden_items:"{number} {number, plural,\n one {item is}\n other {items zijn}\n} verborgen"}}},Dt={general:{title:"Algemeen",cards:{general:{description:"Dit paneel definieert enkele instellingen die van toepassing zijn op alle inschakelmodi.",fields:{disarm_after_trigger:{heading:"Uitschakelen na activatie",description:"Nadat de triggertijd is verstreken, schakelt u het alarm uit in plaats van terug te keren naar de ingeschakelde toestand."},enable_mqtt:{heading:"MQTT inschakelen",description:"Toestaan het alarmpaneel via MQTT aan te sturen."},enable_master:{heading:"Master alarm inschakelen",description:"Creëert een entiteit om alle gebieden tegelijkertijd te besturen."}},actions:{setup_mqtt:"MQTT Configuratie",setup_master:"Master configuratie"}},modes:{title:"Modi",description:"Dit paneel kan worden gebruikt om de inschakelmodi van het alarm in te stellen.",modes:{armed_away:"De afwezigheidsmodus wordt gebruikt als alle mensen het huis hebben verlaten. Alle deuren en ramen die toegang geven tot het huis worden bewaakt, evenals bewegingssensoren in het huis.",armed_home:"De thuismodus wordt gebruikt bij het instellen van het alarm terwijl er mensen in huis zijn. Alle deuren en ramen die toegang geven tot het huis worden bewaakt, maar bewegingssensoren in het huis worden niet gebruikt.",armed_night:"De nachtmodus wordt gebruikt bij het instellen van het alarm voordat u gaat slapen. Alle deuren en ramen die toegang geven tot het huis worden bewaakt, en geselecteerde bewegingssensoren (beneden) in het huis.",armed_vacation:"De vakantiemodus dient voor afwezigheid voor langere duur. Er kunnen desgewenst andere vertragingstijden en acties worden ingesteld die beter passen bij de situatie.",armed_custom_bypass:"Een extra modus om uw eigen beveiligingsperimeter te definiëren."},number_sensors_active:"{number} {number, plural,\n one {sensor}\n other {sensoren}\n} ingesteld",fields:{status:{heading:"Status",description:"Stel in of het alarm op deze modus kan worden ingesteld."},exit_delay:{heading:"Vertrek vertraging",description:"Bij het inschakelen van het alarm zullen de sensoren binnen deze tijdsperiode het alarm nog niet activeren."},entry_delay:{heading:"Binnenkomst vertraging",description:"Vertragingstijd totdat het alarm afgaat nadat een van de sensoren is geactiveerd."},trigger_time:{heading:"Activatie tijd",description:"Tijd waarin het alarm in de geactiveerde toestand blijft na activatie."}}},mqtt:{title:"MQTT configuratie",description:"Dit paneel kan worden gebruikt voor configuratie van de MQTT-interface.",fields:{state_topic:{heading:"Toestand topic",description:"Topic waarop statusupdates worden gepubliceerd"},event_topic:{heading:"Gebeurtenis topic",description:"Topic waarop gebeurtenissen worden gepubliceerd"},command_topic:{heading:"Commando topic",description:"Topic waarop commando's voor in- / uitschakelen worden verzonden."},require_code:{heading:"Vereis code",description:"Vereis dat de code wordt verzonden met de opdracht."},state_payload:{heading:"Configureer de payload per toestand",item:"Definieer een payload voor toestand ''{state}''"},command_payload:{heading:"Configureer een payload per commando",item:"Definieer een payload voor commando ''{command}''"}}},areas:{title:"Gebieden",description:"Gebieden kunnen worden gebruikt om uw alarmsysteem in meerdere compartimenten op te delen.",no_items:"Er zijn nog geen gebieden gedefinieerd.",table:{remarks:"Opmerkingen",summary:"Dit gebied bevat {summary_sensors} en {summary_automations}.",summary_sensors:"{number} {number, plural,\n one {sensor}\n other {sensoren}\n}",summary_automations:"{number} {number, plural,\n one {automatisering}\n other {automatiseringen}\n}"},actions:{add:"Toevoegen"}}},dialogs:{create_area:{title:"Nieuw gebied",fields:{copy_from:"Kopieer instellingen van"}},edit_area:{title:"Bewerken van gebied ''{area}''",name_warning:"Opmerking: als u de naam wijzigt, wordt de entiteits-ID gewijzigd"},remove_area:{title:"Gebied verwijderen?",description:"Weet u zeker dat u dit gebied wilt verwijderen? Dit gebied bevat {sensors} sensoren en {automations} automatiseringen, die ook zullen worden verwijderd."},edit_master:{title:"Master configuratie"},disable_master:{title:"Master uitschakelen?",description:"Weet u zeker dat u het master alarm wilt verwijderen? Dit gebied bevat {automations} automatiseringen, die met deze actie worden verwijderd."}}},sensors:{title:"Sensoren",cards:{sensors:{description:"Momenteel geconfigureerde sensoren. Klik op een entiteit om wijzigingen aan te brengen.",table:{no_items:"Er zijn nog geen sensoren aan het alarm toegevoegd. Zorg ervoor dat u ze eerst toevoegt.",no_area_warning:"Sensor is niet aan een gebied toegewezen.",arm_modes:"Inschakelmodi",always_on:"(Altijd)"}},add_sensors:{title:"Voeg sensoren toe",description:"Voeg meer sensoren toe. Zorg ervoor dat uw sensoren een duidelijke naam hebben, zodat u ze kunt identificeren.",no_items:"Er zijn geen beschikbare HA-entiteiten die voor het alarm kunnen worden geconfigureerd. Zorg ervoor dat u entiteiten van het type binary_sensor opneemt.",table:{type:"Gedetecteerd type"},actions:{add_to_alarm:"Voeg aan alarm toe",filter_supported:"Verberg items met onbekend type"}},editor:{title:"Wijzig Sensor",description:"Configureren van de sensorinstellingen van ''{entity}''.",fields:{area:{heading:"Gebied",description:"Selecteer een gebied dat deze sensor bevat."},group:{heading:"Groep",description:"Groepeer met andere sensors voor gecombineerde triggers."},device_type:{heading:"Apparaat Type",description:"Kies een apparaattype om automatisch de juiste instellingen toe te passen.",choose:{door:{name:"Deur",description:"Een deur, poort of andere ingang die wordt gebruikt voor het betreden/verlaten van de woning."},window:{name:"Raam",description:"Een raam of een deur die niet wordt gebruikt om het huis binnen te komen, zoals een balkon."},motion:{name:"Beweging",description:"Aanwezigheidssensor of soortgelijk apparaat met een vertraging tussen activeringen."},tamper:{name:"Sabotage",description:"Detector van verwijdering van sensorkap, glasbreuksensor, enz."},environmental:{name:"Klimaat",description:"Rook/gassensor, lekkage detector, etc. (niet gerelateerd aan inbraakbeveiliging)."},other:{name:"Algemeen"}}},always_on:{heading:"Altijd aan",description:"Een sensor moet altijd het alarm activeren."},modes:{heading:"Ingeschakelde modi",description:"Alarmmodi waarin deze sensor actief is."},arm_on_close:{heading:"Inschakelen na sluiten",description:"Na deactivering van deze sensor wordt de resterende vertrek vertraging automatisch overgeslagen."},use_exit_delay:{heading:"Vertragingstijd bij vertrek",description:"De sensor mag actief zijn wanneer de vertrekperiode wordt gestart."},use_entry_delay:{heading:"Vertragingstijd bij binnenkomst",description:"Als de sensor actief wordt, activeert deze het alarm pas na de vertragingstijd voor binnenkomst."},allow_open:{heading:"Sta open toe tijdens het inschakelen",description:"Sta toe dat deze sensor kort na het verlaten actief is, zodat hij het inschakelen niet blokkeert."},auto_bypass:{heading:"Automatisch omzeilen",description:"Elimineer de sensor als deze actief is tijdens het inschakelen van het alarm.",modes:"Modi waarin de sensor automatisch omzeild mag worden"},trigger_unavailable:{heading:"Activeren indien niet beschikbaar",description:"Wanneer de sensorstatus 'niet beschikbaar' wordt, wordt de sensor geactiveerd."}},actions:{toggle_advanced:"Geavanceerde instellingen",remove:"Verwijder",setup_groups:"Configureer groepen"},errors:{description:"Corrigeer de volgende fouten:",no_area:"Er is geen gebied geselecteerd",no_modes:"Er zijn geen modi geselecteerd waarvoor de sensor actief zou moeten zijn",no_auto_bypass_modes:"Er zijn geen modi geselecteerd waarin de sensor automatisch omzeild mag worden"}}},dialogs:{manage_groups:{title:"Beheer sensorgroepen",description:"In een sensorgroep moeten twee of meer sensoren worden geactiveerd binnen een tijdsperiode voordat het alarm wordt geactiveerd.",no_items:"Nog geen groepen ingesteld.",actions:{new_group:"Nieuwe groep"}},create_group:{title:"Nieuwe sensorgroep",fields:{name:{heading:"Naam",description:"Naam voor sensorgroep."},timeout:{heading:"Time-out",description:"Tijdsperiode waarin meerdere sensoren moeten worden geactiveerd om het alarm te activeren."},sensors:{heading:"Sensoren",description:"Selecteer de sensoren die deel moeten uitmaken van deze groep."}},errors:{invalid_name:"Verkeerde naam opgegeven.",insufficient_sensors:"Tenminste 2 sensoren moeten worden geselecteerd."}},edit_group:{title:"Bewerk sensorgroep ''{name}''"}}},codes:{title:"Codes",cards:{codes:{description:"Wijzig de instellingen voor de code.",fields:{code_arm_required:{heading:"Gebruik inschakel code",description:"Vereist een code voor het inschakelen van het alarm"},code_disarm_required:{heading:"Gebruik uitschakelcode",description:"Vereist een code om het alarm uit te schakelen"},code_format:{heading:"Code opmaak",description:"Stelt het invoertype in voor de Lovelace alarmkaart.",code_format_number:"pincode",code_format_text:"wachtwoord"}}},user_management:{title:"Gebruikersbeheer",description:"Elke gebruiker heeft zijn eigen code om het alarm in/uit te schakelen.",no_items:"Er zijn nog geen gebruikers",actions:{new_user:"nieuwe gebruiker"}},new_user:{title:"Maak een nieuwe gebruiker aan",description:"Gebruikers kunnen worden aangemaakt om toegang te verlenen tot het bedienen van het alarm.",fields:{name:{heading:"Naam",description:"Naam van de gebruiker."},code:{heading:"Code",description:"Code voor deze gebruiker."},confirm_code:{heading:"Bevestig de code",description:"Herhaal de code."},can_arm:{heading:"Code toestaan voor inschakeling",description:"Door deze code in te voeren, wordt het alarm geactiveerd"},can_disarm:{heading:"Code toestaan voor uitschakelen",description:"Door deze code in te voeren, wordt het alarm gedeactiveerd"},is_override_code:{heading:"Is een forceer code",description:"Als u deze code invoert, wordt het alarm geforceerd geactiveerd"},area_limit:{heading:"Beperk gebieden",description:"Beperk de gebruiker tot controle over alleen de gelesecteerde gebieden"}},errors:{no_name:"Geen naam opgegeven.",no_code:"Code moet minimaal 4 tekens/cijfers bevatten.",code_mismatch:"De codes komen niet overeen."}},edit_user:{title:"Wijzig Gebruiker",description:"Wijzig de configuratie voor gebruiker ''{name}''.",fields:{old_code:{heading:"Huidige code",description:"Huidige code, laat leeg om ongewijzigd te laten."}}}}},actions:{title:"Acties",cards:{notifications:{title:"Meldingen",description:"Met dit paneel kunt u meldingen beheren die moeten worden verzonden tijdens een bepaalde alarmgebeurtenis",table:{no_items:"Er zijn nog geen notificaties aangemaakt.",no_area_warning:"Actie is niet toegewezen aan een gebied."},actions:{new_notification:"nieuwe melding"}},actions:{description:"Dit paneel kan worden gebruikt om een apparaat te schakelen wanneer de status van het alarm veranderd.",table:{no_items:"Er zijn nog geen acties gemaakt."},actions:{new_action:"nieuwe actie"}},new_notification:{title:"Notificatie instellen",description:"Ontvang een notificatie wanneer het alarm wordt in- of uitgeschakeld, wordt geactiveerd etc.",trigger:"Conditie",action:"Taak",options:"Opties",fields:{event:{heading:"Gebeurtenis",description:"Wanneer moet de notificatie worden verzonden",choose:{armed:{name:"Alarm is ingeschakeld",description:"Het alarm is succesvol ingeschakeld"},disarmed:{name:"Alarm is uitgeschakeld",description:"Het alarm is uitgeschakeld"},triggered:{name:"Alarm is afgegaan",description:"Het alarm gaat af"},untriggered:{name:"Gestopt na afgaan",description:"Het alarm gaat niet meer af"},arm_failure:{name:"Kan niet inschakelen",description:"Het inschakelen van het alarm is mislukt vanwege een of meerdere blokkerende sensoren"},arming:{name:"Vertrek",description:"Vertrekvertraging ingegaan, tijd om het huis te verlaten."},pending:{name:"Binnenkomst",description:"Binnenkomstvertraging ingegaan, het alarm dient te worden uitgeschakeld."}}},mode:{heading:"Modi",description:"Beperk de actie tot specifieke inschakel modi."},title:{heading:"Titel",description:"Titel voor de notificatie"},message:{heading:"Bericht",description:"Tekst voor de notificatie",insert_wildcard:"Wildcard invoegen",placeholders:{armed:"Het alarm is ingeschakeld op {{arm_mode}}",disarmed:"Het alarm is nu uit",triggered:"Het alarm is geactiveerd! Oorzaak: {{open_sensors}}.",untriggered:"The alarm gaat niet langer af.",arm_failure:"Het alarm kon niet worden ingeschakeld. Oorzaak: {{open_sensors}}.",arming:"Het alarm wordt ingeschakeld, verlaat het huis.",pending:"Het alarm moet nu worden uitgeschakeld, anders wordt deze geactiveerd."}},open_sensors_format:{heading:"Opmaak voor open_sensors wildcard",description:"Kies welke sensor informatie wordt weergegeven in het bericht",options:{default:"Naam en status",short:"Alleen naam"}},arm_mode_format:{heading:"Vertaling voor arm_mode wildcard",description:"Kies in welke taal de inschakelmodus wordt weergegeven in het bericht"},target:{heading:"Doel",description:"Apparaat om het push-bericht naar te sturen"},name:{heading:"Naam",description:"Beschrijving voor deze notificatie",placeholders:{armed:"Stuur notificatie naar {target} bij inschakelen",disarmed:"Stuur notificatie naar {target} bij uitschakelen",triggered:"Stuur notificatie naar {target} bij alarm",untriggered:"Stuur notificatie naar {target} als het alarm stopt met afgaan",arm_failure:"Stuur notificatie naar {target} bij fout",arming:"Stuur notificatie naar {target} bij vertrek",pending:"Stuur notificatie naar {target} bij binnenkomst"}},delete:{heading:"Automatisering verwijderen",description:"Verwijder deze automatisering permanent"}},actions:{test:"Testen"}},new_action:{title:"Actie instellen",description:"Schakel verlichting of apparaatuur (bijv. sirene) wanneer het alarm wordt in- of uitgeschakeld of wordt geactiveerd etc.",fields:{event:{heading:"Gebeurtenis",description:"Wanneer moet de actie worden uitgevoerd"},area:{heading:"Gebied",description:"Het gebied waarop de gebeurtenis van toepassing is, laat leeg om het algemene alarm te selecteren."},mode:{heading:"Mode",description:"Beperk de actie tot specifieke inschakel modi (optioneel)"},entity:{heading:"Entiteit",description:"Entiteit om actie op uit te voeren"},action:{heading:"Actie",description:"Actie die op de entiteit moet worden uitgevoerd",no_common_actions:"Acties kunnen alleen worden toegewezen in de YAML modus voor de geselecteerde entiteiten."},name:{heading:"Naam",description:"Beschrijving voor deze actie",placeholders:{armed:"Schakel {entity} naar {state} bij inschakelen",disarmed:"Schakel {entity} naar {state} bij uitschakelen",triggered:"Schakel {entity} naar {state} bij alarm",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Schakel {entity} naar {state} bij fout",arming:"Schakel {entity} naar {state} bij vertrek",pending:"Schakel {entity} naar {state} bij binnenkomst"}}}}}}},Lt={common:Mt,components:Nt,title:"Alarmpaneel",panels:Dt},zt=Object.freeze({__proto__:null,common:Mt,components:Nt,title:"Alarmpaneel",panels:Dt,default:Lt}),Pt={modes_short:{armed_away:"Borta",armed_home:"Hemma",armed_night:"Natt",armed_custom_bypass:"Anpassad",armed_vacation:"Semester"},enabled:"Aktiverat",disabled:"Inaktiverat"},qt={time_slider:{seconds:"sek",minutes:"min",infinite:"oändligt",none:"inget"},editor:{ui_mode:"Till UI",yaml_mode:"Till YAML",edit_in_yaml:"Redigera i YAML"},table:{filter:{label:"Filtrera sensorer",item:"Filtrera med {name}",hidden_items:"{number} {number, plural,\n en {item is}\n andra {items are}\n} dolda"}}},Rt={general:{title:"Generellt",cards:{general:{description:"Denna panel definierar några globala inställningar för larmet.",fields:{disarm_after_trigger:{heading:"Larma av efter trigger",description:"Efter trigger tiden har gått ut, larma av larmet istället för att återgå till larmat läge."},enable_mqtt:{heading:"Aktivera MQTT",description:"Tillåt alarm panelen att kontrolleras via MQTT."},enable_master:{heading:"Aktivera alarm master",description:"Skapar en entity för att kontrollera alla areor samtidigt."}},actions:{setup_mqtt:"MQTT konfiguration",setup_master:"Master konfiguration"}},modes:{title:"Lägen",description:"Denna panel kan användas för att konfiguera larmets olika larmlägen.",modes:{armed_away:"Larmat borta används när alla personer lämnat huset. Alla dörrar och fönster som tillåter tillgång till huset kommer att larmas, det samma gäller rörelsesensorer inne i huset.",armed_home:"Larmat hemma används när det finns personer kvar i huset. Alla dörrar och fönster som tillåter tillgång till huset kommer att larmas, dock inga rörelsesensorer inne i huset.",armed_night:"Larmat natt används när du aktiverar larmen innan du lägger dig. Alla dörrar och fönster som tillåter tillgång till huset kommer att larmas, det samma gäller utvalda rörelsesensorer inne i huset.",armed_vacation:"Larmat semester kan användas som en förlängning av läget för larmat borta vid längre frånvaro. Fördröjningstiderna och triggersvaren kan anpassas (efter önskemål) för att vara borta längre tid från hemmet.",armed_custom_bypass:"Ett extra läge för för att definiera sin egen säkerhetsperimeter."},number_sensors_active:"{number} {number, plural,\n en {sensor}\n andra {sensorer}\n} aktiv",fields:{status:{heading:"Status",description:"Styr om larmet kan aktiveras i detta läge."},exit_delay:{heading:"Lämna fördröjning",description:"Efter att du har aktiverat larmet kommer dina sensorer inte trigga ditt larm inom denna tid."},entry_delay:{heading:"Ankomst fördröjning",description:"Fördröjning i tid tills att ditt larm triggas efter att en av dina sensorer har aktiverats."},trigger_time:{heading:"Trigger tid",description:"Tid som ditt larm kommer vara i triggat läge efter att ett larm har triggats."}}},mqtt:{title:"MQTT konfiguration",description:"Denna panel kan användas för att anpassa konfigurationen av MQTT.",fields:{state_topic:{heading:"Status topic",description:"Topic på vilket status uppdateringar publiceras till."},event_topic:{heading:"Event topic",description:"Topic på vilket alarm events publiceras till."},command_topic:{heading:"Kommando topic",description:"Topic på vilket Alarmo lyssnar på för larma/larma av kommandon."},require_code:{heading:"Kräv kod",description:"Kräv att koden ska skickas med kommandot."},state_payload:{heading:"Konfiguera payload per state",item:"Definiera en payload för state ''{state}''"},command_payload:{heading:"Konfiguera payload per kommando",item:"Definiera en payload för kommando ''{command}''"}}},areas:{title:"Områden",description:"Områden kan användas för att dela upp ditt larm till flera områden.",no_items:"Det är inga områden definierade än.",table:{remarks:"Anmärkningar",summary:"Detta område innehåller {summary_sensors} och {summary_automations}.",summary_sensors:"{number} {number, plural,\n en {sensor}\n andra {sensorer}\n}",summary_automations:"{number} {number, plural,\n en {automation}\n andra {automationer}\n}"},actions:{add:"Lägg till"}}},dialogs:{create_area:{title:"Nytt område",fields:{copy_from:"Kopiera inställningarna från"}},edit_area:{title:"Redigera område ''{area}''",name_warning:"OBS: Ändrar du namn kommer entity ID att ändras"},remove_area:{title:"Ta bort område?",description:"Är du säker att du vill ta bort detta område? Detta område innehåller {sensors} sensorer och {automations} automationer, som också kommer att tas bort."},edit_master:{title:"Master konfiguration"},disable_master:{title:"Inaktivera master?",description:"Är du säker att du vill ta bort master alarm? Denna area innehåller {automations} automationer, som kommer att tas bort med detta val."}}},sensors:{title:"Sensorer",cards:{sensors:{description:"Nuvarande konfiguerade sensorer. Klicka på ett entity för att göra förändringar.",table:{no_items:"Det finns inga sensorer att visa här.",no_area_warning:"Sensor är inte tilldelat till något område.",arm_modes:"Larmläge",always_on:"(Alltid)"}},add_sensors:{title:"Lägg till sensorer",description:"Lägg till mer sensorer. Säkerhetsställ att dina sensorer har ett friendly_name, så du kan identifiera dem.",no_items:"Det finns inga tillgängliga HA entities som kan konfigueras för larmet. Säkerhetsställ att inkludera entities av type binary_sensor.",table:{type:"Detekteringstyp"},actions:{add_to_alarm:"Addera till larmet",filter_supported:"Dölj sensorer av typen unknown"}},editor:{title:"Justera Sensor",description:"Justera inställningarna för sensor ''{entity}''.",fields:{area:{heading:"Område",description:"Välj ett område som innehåller denna sensor."},group:{heading:"Grupp",description:"Gruppera med andra sensorer för kombinerad trigger."},device_type:{heading:"Enhetstyp",description:"Välj en enhetstyp att automatiskt applicera rekomenderade inställningar på.",choose:{door:{name:"Dörr",description:"En dörr, grind eller annan entre som används för att gå in/lämna hemmet."},window:{name:"Fönster",description:"Ett fönster eller en dörr som inte används för att gå in/lämna huset, t.ex. en balkongdörr."},motion:{name:"Rörelse",description:"Närvarosensor eller liknande som har fördröjning mellan sina aktiveringar."},tamper:{name:"Manipulering",description:"Detektor av sensorskydd, glaskross sensor etc."},environmental:{name:"Miljö",description:"Rök/gas sensor eller läckage sensor etc. (Inte relaterat till inbrottsskydd)."},other:{name:"Generell"}}},always_on:{heading:"Larma alltid",description:"Sensorn ska alltid trigga larmet."},modes:{heading:"Aktiverat läge",description:"Larmläge när sensorn ska vara aktiv."},arm_on_close:{heading:"Larma efter stängning",description:"Resterande lämna fördröjning skippas automatiskt när denna sensor inaktiveras."},use_exit_delay:{heading:"Använd lämna fördröjning",description:"Sensorn är tillåten att vara aktiv när lämna fördröjningen startar."},use_entry_delay:{heading:"Använd ankomst fördröjning",description:"Sensor aktivering triggar larmet after ankomst fördröjningen istället för direkt."},allow_open:{heading:"Tillåt öppnad efter larmning.",description:"Om sensorn fortfarande är aktiv efter lämna fördröjningen kommer det inte misslyckas att larma."},auto_bypass:{heading:"Exkludera automatiskt",description:"Exkludera denna sensor fr¨ån larmet open den är öppen vid pålarmning.",modes:"Lägen där sensor kan bli exkluderad"},trigger_unavailable:{heading:"Trigga vid otillgänglig",description:"Detta kommer aktiveras när sensorns status blir 'unavailable'."}},actions:{toggle_advanced:"Avancerade inställningar",remove:"Ta bort",setup_groups:"Hantera grupper"},errors:{description:"Var vänlig att justera följande fel:",no_area:"Inget område är vald",no_modes:"Inga lägen är valda när sensorn ska vara aktiv",no_auto_bypass_modes:"Inga lägen är valda när sensorn eventuellt automatiskt ska förbikopplas"}}},dialogs:{manage_groups:{title:"Hantera sensor grupper",description:"I en sensor grupp måste flera sensorer bli aktiverade inom en tidsperiod för att larmet ska triggas.",no_items:"Inga grupper ännu",actions:{new_group:"Ny grupp"}},create_group:{title:"Ny sensor grupp",fields:{name:{heading:"Namn",description:"Namn för sensor gruppen"},timeout:{heading:"Time-out",description:"Tidsperiod för de sammankopplade sensorernas aktivitet ska trigga larmet."},sensors:{heading:"Sensorer",description:"Välj sensorer som tillhöra gruppen."}},errors:{invalid_name:"Ogiltigt namn specificerat.",insufficient_sensors:"Minst två sensorer behöver väljas."}},edit_group:{title:"Justera sensor grupp ''{name}''"}}},codes:{title:"Koder",cards:{codes:{description:"Ändra inställningar för kod.",fields:{code_arm_required:{heading:"Använd pålarmningskod",description:"Kräv en kod för att aktivera larmet"},code_disarm_required:{heading:"Använd avlarmningskod",description:"Kräv en kod för att inaktivera larmet"},code_format:{heading:"Kodformat",description:"Ändra inmatningstyp för Lovelace alarm kortet.",code_format_number:"pinkod",code_format_text:"lösenord"}}},user_management:{title:"Användarhantering",description:"Varje användare har sin egen kod för aktivera/inaktivera larmet.",no_items:"Det finns inga användare än",actions:{new_user:"Ny användare"}},new_user:{title:"Skapa en ny användare",description:"Användare kan skapas för att ge tillgång att styra larmet.",fields:{name:{heading:"Namn",description:"Namn på användaren"},code:{heading:"Kod",description:"Koden för användaren."},confirm_code:{heading:"Repetera koden",description:"Repetera koden."},can_arm:{heading:"Tillåt kod för pålarmning",description:"Denna kod aktiverar larmet"},can_disarm:{heading:"Tillåt kod för avlarmning",description:"Denna kod inaktiverar larmet"},is_override_code:{heading:"Tvingande kod",description:"Denna kod tvingar aktivering av larmet"},area_limit:{heading:"Begränsade områden",description:"Begränsa användare att hantera utvalda områden"}},errors:{no_name:"Inget namn angivet.",no_code:"Koden ska vara minst 4 tecken eller siffror.",code_mismatch:"Koderna matchar inte."}},edit_user:{title:"Justera användare",description:"Ändra inställningar för användare ''{name}''.",fields:{old_code:{heading:"Nuvarande kod",description:"Nuvarande kod, lämna tomt för att inte ändra."}}}}},actions:{title:"Åtgärder",cards:{notifications:{title:"Notifikationer",description:"Du använder denna panel för att hantera notifikationer som ska sändas vid utvalda larmevents.",table:{no_items:"Det är inga notifikationer skapade än.",no_area_warning:"Åtgärd är inte tilldelad till något område."},actions:{new_notification:"ny notifikation"}},actions:{description:"I denna panel kan du trigga olika beteende på enheter baserat på oliak events från ditt larm.",table:{no_items:"Det finns inga åtgärder skapade ännu."},actions:{new_action:"ny åtgärd"}},new_notification:{title:"Konfiguera notifikationer",description:"Ta emot en notifikation när ditt larm aktivera/inaktiveras eller om en sensor aktiveras eller liknande.",trigger:"Villkor",action:"Åtgärd",options:"Inställningar",fields:{event:{heading:"Event",description:"När ska notifikationen skickas",choose:{armed:{name:"Larmet är aktiverat",description:"Larmet aktiveras framgångsrikt"},disarmed:{name:"Larmet är inaktiverat",description:"Larmet är inaktiverat"},triggered:{name:"Larmet har triggats",description:"Larmet har triggats"},untriggered:{name:"Alarm not longer triggered",description:"The triggered state of the alarm has ended"},arm_failure:{name:"Misslyckas att aktivera larm",description:"Larmet misslyckas att kativeras på grund av någon sensor"},arming:{name:"Lämna fördröjning startas",description:"Lämna fördröjning startas, redo att lämna huset."},pending:{name:"Ankomst fördröjning startas",description:"Ankomst fördröjning startas, larmet kommer triggas snart."}}},mode:{heading:"Läge",description:"Begränsa åtgärd till specifikt larmläge (valfritt)"},title:{heading:"Titel",description:"Titel för notifikationsmeddelandet"},message:{heading:"Meddelande",description:"Innehåll av notifikationsmeddelandet",insert_wildcard:"Lägg in wildcard",placeholders:{armed:"Larmet har bytt status till {{arm_mode}}",disarmed:"Larmet är nu AVSTÄNGT",triggered:"Larmet har triggats! Anledning: {{open_sensors}}.",untriggered:"The alarm is not longer triggered.",arm_failure:"Larmet kunde inte aktiveras nu, detta på grund av: {{open_sensors}}.",arming:"Larmet kommer aktiveras snart, lämna huset.",pending:"Larmet kommer snart triggas, inaktivera larmet snarast!"}},open_sensors_format:{heading:"Format för open_sensors wildcard",description:"Välj vilken sensorinformation som ska infogas i meddelandet",options:{default:"Namn och tillstånd",short:"Endast namn"}},arm_mode_format:{heading:"Översättning för larmläge wildcard",description:"Välj vilket språk som larmläge ska infogas i meddelandet"},target:{heading:"Mål",description:"Enhet att skicka push-meddelandet till"},name:{heading:"Namn",description:"Beskrivning av notifikationen",placeholders:{armed:"Notifiera {target} vid aktivering av larm",disarmed:"Notifiera {target} vid inaktivering av larm",triggered:"Notifiera {target} vid triggning av larm",untriggered:"Notify {target} when triggering stops",arm_failure:"Notifiera {target} vid fel av larm",arming:"Notifiera {target} vid utpassering",pending:"Notifiera {target} vid ankomst"}},delete:{heading:"Ta bort automation",description:"Ta bort automation permanent"}},actions:{test:"Testa"}},new_action:{title:"Konfiguera action",description:"Aktivera lampor eller andra enheter som sirener eller högatalare vid aktivering/inaktivering av larmet, triggning av larmet osv.",fields:{event:{heading:"Event",description:"När ska denna action aktiveras"},area:{heading:"Område",description:"Område som detta event ska appliceras på, lämna tomt om det ska gälla globalt."},mode:{heading:"Läge",description:"Begränsa åtgärd till specifika larmlägen (frivilligt)"},entity:{heading:"Entitet",description:"Entitet att utföra åtgärd på"},action:{heading:"Åtgärd",description:"Åtgärd att utföra på entitet",no_common_actions:"Åtgärder kan enbart bli applicerade i YAML läge för utvalda entiteter."},name:{heading:"Namn",description:"Beskrivning av denna åtgärd",placeholders:{armed:"Sätt {entity} till {state} vid aktivering av larmet",disarmed:"Sätt {entity} till {state} vid inaktivering av larmet",triggered:"Sätt {entity} till {state} när larmet triggas",untriggered:"Set {entity} to {state} when triggering stops",arm_failure:"Sätt {entity} till {state} vid fel av larmet",arming:"Sätt {entity} till {state} vid utpassering",pending:"Sätt {entity} till {state} vid ankomst"}}}}}}},It={common:Pt,components:qt,title:"Alarm panel",panels:Rt},Ut=Object.freeze({__proto__:null,common:Pt,components:qt,title:"Alarm panel",panels:Rt,default:It}),Gt=function(e,t){return(Gt=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var a in t)Object.prototype.hasOwnProperty.call(t,a)&&(e[a]=t[a])})(e,t)};function Ft(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Class extends value "+String(t)+" is not a constructor or null");function a(){this.constructor=e}Gt(e,t),e.prototype=null===t?Object.create(t):(a.prototype=t.prototype,new a)}var Vt=function(){return(Vt=Object.assign||function(e){for(var t,a=1,i=arguments.length;a1)throw new RangeError("integer-width stems only accept a single optional option");s.options[0].replace(ga,(function(e,a,i,s,n,r){if(a)t.minimumIntegerDigits=i.length;else{if(s&&n)throw new Error("We currently do not support maximum integer digits");if(r)throw new Error("We currently do not support exact integer digits")}return""}));continue}if(va.test(s.stem))t.minimumIntegerDigits=s.stem.length;else if(ma.test(s.stem)){if(s.options.length>1)throw new RangeError("Fraction-precision stems only accept a single optional option");s.stem.replace(ma,(function(e,a,i,s,n,r){return"*"===i?t.minimumFractionDigits=a.length:s&&"#"===s[0]?t.maximumFractionDigits=s.length:n&&r?(t.minimumFractionDigits=n.length,t.maximumFractionDigits=n.length+r.length):(t.minimumFractionDigits=a.length,t.maximumFractionDigits=a.length),""}));var n=s.options[0];"w"===n?t=ca(ca({},t),{trailingZeroDisplay:"stripIfInteger"}):n&&(t=ca(ca({},t),fa(n)))}else if(pa.test(s.stem))t=ca(ca({},t),fa(s.stem));else{var r=_a(s.stem);r&&(t=ca(ca({},t),r));var o=ba(s.stem);o&&(t=ca(ca({},t),o))}}return t}var ka=new RegExp("^".concat(oa.source,"*")),$a=new RegExp("".concat(oa.source,"*$"));function Aa(e,t){return{start:e,end:t}}var xa=!!String.prototype.startsWith,Oa=!!String.fromCodePoint,Ea=!!Object.fromEntries,Ta=!!String.prototype.codePointAt,ja=!!String.prototype.trimStart,Sa=!!String.prototype.trimEnd,Ca=!!Number.isSafeInteger?Number.isSafeInteger:function(e){return"number"==typeof e&&isFinite(e)&&Math.floor(e)===e&&Math.abs(e)<=9007199254740991},Ma=!0;try{Ma="a"===(null===(ua=Ia("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu").exec("a"))||void 0===ua?void 0:ua[0])}catch(M){Ma=!1}var Na,Da=xa?function(e,t,a){return e.startsWith(t,a)}:function(e,t,a){return e.slice(a,a+t.length)===t},La=Oa?String.fromCodePoint:function(){for(var e=[],t=0;tn;){if((a=e[n++])>1114111)throw RangeError(a+" is not a valid code point");i+=a<65536?String.fromCharCode(a):String.fromCharCode(55296+((a-=65536)>>10),a%1024+56320)}return i},za=Ea?Object.fromEntries:function(e){for(var t={},a=0,i=e;a=a)){var i,s=e.charCodeAt(t);return s<55296||s>56319||t+1===a||(i=e.charCodeAt(t+1))<56320||i>57343?s:i-56320+(s-55296<<10)+65536}},qa=ja?function(e){return e.trimStart()}:function(e){return e.replace(ka,"")},Ra=Sa?function(e){return e.trimEnd()}:function(e){return e.replace($a,"")};function Ia(e,t){return new RegExp(e,t)}if(Ma){var Ua=Ia("([^\\p{White_Space}\\p{Pattern_Syntax}]*)","yu");Na=function(e,t){var a;return Ua.lastIndex=t,null!==(a=Ua.exec(e)[1])&&void 0!==a?a:""}}else Na=function(e,t){for(var a=[];;){var i=Pa(e,t);if(void 0===i||Ha(i)||Ya(i))break;a.push(i),t+=i>=65536?2:1}return La.apply(void 0,a)};var Ga=function(){function e(e,t){void 0===t&&(t={}),this.message=e,this.position={offset:0,line:1,column:1},this.ignoreTag=!!t.ignoreTag,this.requiresOtherClause=!!t.requiresOtherClause,this.shouldParseSkeletons=!!t.shouldParseSkeletons}return e.prototype.parse=function(){if(0!==this.offset())throw Error("parser can only be used once");return this.parseMessage(0,"",!1)},e.prototype.parseMessage=function(e,t,a){for(var i=[];!this.isEOF();){var s=this.char();if(123===s){if((n=this.parseArgument(e,a)).err)return n;i.push(n.val)}else{if(125===s&&e>0)break;if(35!==s||"plural"!==t&&"selectordinal"!==t){if(60===s&&!this.ignoreTag&&47===this.peek()){if(a)break;return this.error(Yt.UNMATCHED_CLOSING_TAG,Aa(this.clonePosition(),this.clonePosition()))}if(60===s&&!this.ignoreTag&&Fa(this.peek()||0)){if((n=this.parseTag(e,t)).err)return n;i.push(n.val)}else{var n;if((n=this.parseLiteral(e,t)).err)return n;i.push(n.val)}}else{var r=this.clonePosition();this.bump(),i.push({type:Bt.pound,location:Aa(r,this.clonePosition())})}}}return{val:i,err:null}},e.prototype.parseTag=function(e,t){var a=this.clonePosition();this.bump();var i=this.parseTagName();if(this.bumpSpace(),this.bumpIf("/>"))return{val:{type:Bt.literal,value:"<".concat(i,"/>"),location:Aa(a,this.clonePosition())},err:null};if(this.bumpIf(">")){var s=this.parseMessage(e+1,t,!0);if(s.err)return s;var n=s.val,r=this.clonePosition();if(this.bumpIf("")?{val:{type:Bt.tag,value:i,children:n,location:Aa(a,this.clonePosition())},err:null}:this.error(Yt.INVALID_TAG,Aa(r,this.clonePosition())))}return this.error(Yt.UNCLOSED_TAG,Aa(a,this.clonePosition()))}return this.error(Yt.INVALID_TAG,Aa(a,this.clonePosition()))},e.prototype.parseTagName=function(){var e=this.offset();for(this.bump();!this.isEOF()&&Va(this.char());)this.bump();return this.message.slice(e,this.offset())},e.prototype.parseLiteral=function(e,t){for(var a=this.clonePosition(),i="";;){var s=this.tryParseQuote(t);if(s)i+=s;else{var n=this.tryParseUnquoted(e,t);if(n)i+=n;else{var r=this.tryParseLeftAngleBracket();if(!r)break;i+=r}}}var o=Aa(a,this.clonePosition());return{val:{type:Bt.literal,value:i,location:o},err:null}},e.prototype.tryParseLeftAngleBracket=function(){return this.isEOF()||60!==this.char()||!this.ignoreTag&&(Fa(e=this.peek()||0)||47===e)?null:(this.bump(),"<");var e},e.prototype.tryParseQuote=function(e){if(this.isEOF()||39!==this.char())return null;switch(this.peek()){case 39:return this.bump(),this.bump(),"'";case 123:case 60:case 62:case 125:break;case 35:if("plural"===e||"selectordinal"===e)break;return null;default:return null}this.bump();var t=[this.char()];for(this.bump();!this.isEOF();){var a=this.char();if(39===a){if(39!==this.peek()){this.bump();break}t.push(39),this.bump()}else t.push(a);this.bump()}return La.apply(void 0,t)},e.prototype.tryParseUnquoted=function(e,t){if(this.isEOF())return null;var a=this.char();return 60===a||123===a||35===a&&("plural"===t||"selectordinal"===t)||125===a&&e>0?null:(this.bump(),La(a))},e.prototype.parseArgument=function(e,t){var a=this.clonePosition();if(this.bump(),this.bumpSpace(),this.isEOF())return this.error(Yt.EXPECT_ARGUMENT_CLOSING_BRACE,Aa(a,this.clonePosition()));if(125===this.char())return this.bump(),this.error(Yt.EMPTY_ARGUMENT,Aa(a,this.clonePosition()));var i=this.parseIdentifierIfPossible().value;if(!i)return this.error(Yt.MALFORMED_ARGUMENT,Aa(a,this.clonePosition()));if(this.bumpSpace(),this.isEOF())return this.error(Yt.EXPECT_ARGUMENT_CLOSING_BRACE,Aa(a,this.clonePosition()));switch(this.char()){case 125:return this.bump(),{val:{type:Bt.argument,value:i,location:Aa(a,this.clonePosition())},err:null};case 44:return this.bump(),this.bumpSpace(),this.isEOF()?this.error(Yt.EXPECT_ARGUMENT_CLOSING_BRACE,Aa(a,this.clonePosition())):this.parseArgumentOptions(e,t,i,a);default:return this.error(Yt.MALFORMED_ARGUMENT,Aa(a,this.clonePosition()))}},e.prototype.parseIdentifierIfPossible=function(){var e=this.clonePosition(),t=this.offset(),a=Na(this.message,t),i=t+a.length;return this.bumpTo(i),{value:a,location:Aa(e,this.clonePosition())}},e.prototype.parseArgumentOptions=function(e,t,a,i){var s,n=this.clonePosition(),r=this.parseIdentifierIfPossible().value,o=this.clonePosition();switch(r){case"":return this.error(Yt.EXPECT_ARGUMENT_TYPE,Aa(n,o));case"number":case"date":case"time":this.bumpSpace();var l=null;if(this.bumpIf(",")){this.bumpSpace();var d=this.clonePosition();if((v=this.parseSimpleArgStyleIfPossible()).err)return v;if(0===(u=Ra(v.val)).length)return this.error(Yt.EXPECT_ARGUMENT_STYLE,Aa(this.clonePosition(),this.clonePosition()));l={style:u,styleLocation:Aa(d,this.clonePosition())}}if((f=this.tryParseArgumentClose(i)).err)return f;var c=Aa(i,this.clonePosition());if(l&&Da(null==l?void 0:l.style,"::",0)){var h=qa(l.style.slice(2));if("number"===r)return(v=this.parseNumberSkeletonFromString(h,l.styleLocation)).err?v:{val:{type:Bt.number,value:a,location:c,style:v.val},err:null};if(0===h.length)return this.error(Yt.EXPECT_DATE_TIME_SKELETON,c);var u={type:Kt.dateTime,pattern:h,location:l.styleLocation,parsedOptions:this.shouldParseSkeletons?da(h):{}};return{val:{type:"date"===r?Bt.date:Bt.time,value:a,location:c,style:u},err:null}}return{val:{type:"number"===r?Bt.number:"date"===r?Bt.date:Bt.time,value:a,location:c,style:null!==(s=null==l?void 0:l.style)&&void 0!==s?s:null},err:null};case"plural":case"selectordinal":case"select":var m=this.clonePosition();if(this.bumpSpace(),!this.bumpIf(","))return this.error(Yt.EXPECT_SELECT_ARGUMENT_OPTIONS,Aa(m,Qt({},m)));this.bumpSpace();var p=this.parseIdentifierIfPossible(),g=0;if("select"!==r&&"offset"===p.value){if(!this.bumpIf(":"))return this.error(Yt.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,Aa(this.clonePosition(),this.clonePosition()));var v;if(this.bumpSpace(),(v=this.tryParseDecimalInteger(Yt.EXPECT_PLURAL_ARGUMENT_OFFSET_VALUE,Yt.INVALID_PLURAL_ARGUMENT_OFFSET_VALUE)).err)return v;this.bumpSpace(),p=this.parseIdentifierIfPossible(),g=v.val}var f,_=this.tryParsePluralOrSelectOptions(e,r,t,p);if(_.err)return _;if((f=this.tryParseArgumentClose(i)).err)return f;var b=Aa(i,this.clonePosition());return"select"===r?{val:{type:Bt.select,value:a,options:za(_.val),location:b},err:null}:{val:{type:Bt.plural,value:a,options:za(_.val),offset:g,pluralType:"plural"===r?"cardinal":"ordinal",location:b},err:null};default:return this.error(Yt.INVALID_ARGUMENT_TYPE,Aa(n,o))}},e.prototype.tryParseArgumentClose=function(e){return this.isEOF()||125!==this.char()?this.error(Yt.EXPECT_ARGUMENT_CLOSING_BRACE,Aa(e,this.clonePosition())):(this.bump(),{val:!0,err:null})},e.prototype.parseSimpleArgStyleIfPossible=function(){for(var e=0,t=this.clonePosition();!this.isEOF();){switch(this.char()){case 39:this.bump();var a=this.clonePosition();if(!this.bumpUntil("'"))return this.error(Yt.UNCLOSED_QUOTE_IN_ARGUMENT_STYLE,Aa(a,this.clonePosition()));this.bump();break;case 123:e+=1,this.bump();break;case 125:if(!(e>0))return{val:this.message.slice(t.offset,this.offset()),err:null};e-=1;break;default:this.bump()}}return{val:this.message.slice(t.offset,this.offset()),err:null}},e.prototype.parseNumberSkeletonFromString=function(e,t){var a=[];try{a=function(e){if(0===e.length)throw new Error("Number skeleton cannot be empty");for(var t=[],a=0,i=e.split(ha).filter((function(e){return e.length>0}));a=48&&r<=57))break;s=!0,n=10*n+(r-48),this.bump()}var o=Aa(i,this.clonePosition());return s?Ca(n*=a)?{val:n,err:null}:this.error(t,o):this.error(e,o)},e.prototype.offset=function(){return this.position.offset},e.prototype.isEOF=function(){return this.offset()===this.message.length},e.prototype.clonePosition=function(){return{offset:this.position.offset,line:this.position.line,column:this.position.column}},e.prototype.char=function(){var e=this.position.offset;if(e>=this.message.length)throw Error("out of bound");var t=Pa(this.message,e);if(void 0===t)throw Error("Offset ".concat(e," is at invalid UTF-16 code unit boundary"));return t},e.prototype.error=function(e,t){return{val:null,err:{kind:e,message:this.message,location:t}}},e.prototype.bump=function(){if(!this.isEOF()){var e=this.char();10===e?(this.position.line+=1,this.position.column=1,this.position.offset+=1):(this.position.column+=1,this.position.offset+=e<65536?1:2)}},e.prototype.bumpIf=function(e){if(Da(this.message,e,this.offset())){for(var t=0;t=0?(this.bumpTo(a),!0):(this.bumpTo(this.message.length),!1)},e.prototype.bumpTo=function(e){if(this.offset()>e)throw Error("targetOffset ".concat(e," must be greater than or equal to the current offset ").concat(this.offset()));for(e=Math.min(e,this.message.length);;){var t=this.offset();if(t===e)break;if(t>e)throw Error("targetOffset ".concat(e," is at invalid UTF-16 code unit boundary"));if(this.bump(),this.isEOF())break}},e.prototype.bumpSpace=function(){for(;!this.isEOF()&&Ha(this.char());)this.bump()},e.prototype.peek=function(){if(this.isEOF())return null;var e=this.char(),t=this.offset(),a=this.message.charCodeAt(t+(e>=65536?2:1));return null!=a?a:null},e}();function Fa(e){return e>=97&&e<=122||e>=65&&e<=90}function Va(e){return 45===e||46===e||e>=48&&e<=57||95===e||e>=97&&e<=122||e>=65&&e<=90||183==e||e>=192&&e<=214||e>=216&&e<=246||e>=248&&e<=893||e>=895&&e<=8191||e>=8204&&e<=8205||e>=8255&&e<=8256||e>=8304&&e<=8591||e>=11264&&e<=12271||e>=12289&&e<=55295||e>=63744&&e<=64975||e>=65008&&e<=65533||e>=65536&&e<=983039}function Ha(e){return e>=9&&e<=13||32===e||133===e||e>=8206&&e<=8207||8232===e||8233===e}function Ya(e){return e>=33&&e<=35||36===e||e>=37&&e<=39||40===e||41===e||42===e||43===e||44===e||45===e||e>=46&&e<=47||e>=58&&e<=59||e>=60&&e<=62||e>=63&&e<=64||91===e||92===e||93===e||94===e||96===e||123===e||124===e||125===e||126===e||161===e||e>=162&&e<=165||166===e||167===e||169===e||171===e||172===e||174===e||176===e||177===e||182===e||187===e||191===e||215===e||247===e||e>=8208&&e<=8213||e>=8214&&e<=8215||8216===e||8217===e||8218===e||e>=8219&&e<=8220||8221===e||8222===e||8223===e||e>=8224&&e<=8231||e>=8240&&e<=8248||8249===e||8250===e||e>=8251&&e<=8254||e>=8257&&e<=8259||8260===e||8261===e||8262===e||e>=8263&&e<=8273||8274===e||8275===e||e>=8277&&e<=8286||e>=8592&&e<=8596||e>=8597&&e<=8601||e>=8602&&e<=8603||e>=8604&&e<=8607||8608===e||e>=8609&&e<=8610||8611===e||e>=8612&&e<=8613||8614===e||e>=8615&&e<=8621||8622===e||e>=8623&&e<=8653||e>=8654&&e<=8655||e>=8656&&e<=8657||8658===e||8659===e||8660===e||e>=8661&&e<=8691||e>=8692&&e<=8959||e>=8960&&e<=8967||8968===e||8969===e||8970===e||8971===e||e>=8972&&e<=8991||e>=8992&&e<=8993||e>=8994&&e<=9e3||9001===e||9002===e||e>=9003&&e<=9083||9084===e||e>=9085&&e<=9114||e>=9115&&e<=9139||e>=9140&&e<=9179||e>=9180&&e<=9185||e>=9186&&e<=9254||e>=9255&&e<=9279||e>=9280&&e<=9290||e>=9291&&e<=9311||e>=9472&&e<=9654||9655===e||e>=9656&&e<=9664||9665===e||e>=9666&&e<=9719||e>=9720&&e<=9727||e>=9728&&e<=9838||9839===e||e>=9840&&e<=10087||10088===e||10089===e||10090===e||10091===e||10092===e||10093===e||10094===e||10095===e||10096===e||10097===e||10098===e||10099===e||10100===e||10101===e||e>=10132&&e<=10175||e>=10176&&e<=10180||10181===e||10182===e||e>=10183&&e<=10213||10214===e||10215===e||10216===e||10217===e||10218===e||10219===e||10220===e||10221===e||10222===e||10223===e||e>=10224&&e<=10239||e>=10240&&e<=10495||e>=10496&&e<=10626||10627===e||10628===e||10629===e||10630===e||10631===e||10632===e||10633===e||10634===e||10635===e||10636===e||10637===e||10638===e||10639===e||10640===e||10641===e||10642===e||10643===e||10644===e||10645===e||10646===e||10647===e||10648===e||e>=10649&&e<=10711||10712===e||10713===e||10714===e||10715===e||e>=10716&&e<=10747||10748===e||10749===e||e>=10750&&e<=11007||e>=11008&&e<=11055||e>=11056&&e<=11076||e>=11077&&e<=11078||e>=11079&&e<=11084||e>=11085&&e<=11123||e>=11124&&e<=11125||e>=11126&&e<=11157||11158===e||e>=11159&&e<=11263||e>=11776&&e<=11777||11778===e||11779===e||11780===e||11781===e||e>=11782&&e<=11784||11785===e||11786===e||11787===e||11788===e||11789===e||e>=11790&&e<=11798||11799===e||e>=11800&&e<=11801||11802===e||11803===e||11804===e||11805===e||e>=11806&&e<=11807||11808===e||11809===e||11810===e||11811===e||11812===e||11813===e||11814===e||11815===e||11816===e||11817===e||e>=11818&&e<=11822||11823===e||e>=11824&&e<=11833||e>=11834&&e<=11835||e>=11836&&e<=11839||11840===e||11841===e||11842===e||e>=11843&&e<=11855||e>=11856&&e<=11857||11858===e||e>=11859&&e<=11903||e>=12289&&e<=12291||12296===e||12297===e||12298===e||12299===e||12300===e||12301===e||12302===e||12303===e||12304===e||12305===e||e>=12306&&e<=12307||12308===e||12309===e||12310===e||12311===e||12312===e||12313===e||12314===e||12315===e||12316===e||12317===e||e>=12318&&e<=12319||12320===e||12336===e||64830===e||64831===e||e>=65093&&e<=65094}function Ba(e,t){void 0===t&&(t={}),t=Qt({shouldParseSkeletons:!0,requiresOtherClause:!0},t);var a=new Ga(e,t).parse();if(a.err){var i=SyntaxError(Yt[a.err.kind]);throw i.location=a.err.location,i.originalMessage=a.err.message,i}return(null==t?void 0:t.captureLocation)||function e(t){t.forEach((function(t){if(delete t.location,ta(t)||aa(t))for(var a in t.options)delete t.options[a].location,e(t.options[a].value);else Zt(t)&&na(t.style)||(Jt(t)||ea(t))&&ra(t.style)?delete t.style.location:sa(t)&&e(t.children)}))}(a.val),a.val}function Ka(e,t){var a=t&&t.cache?t.cache:ai,i=t&&t.serializer?t.serializer:Ja;return(t&&t.strategy?t.strategy:Za)(e,{cache:a,serializer:i})}function Qa(e,t,a,i){var s,n=null==(s=i)||"number"==typeof s||"boolean"==typeof s?i:a(i),r=t.get(n);return void 0===r&&(r=e.call(this,i),t.set(n,r)),r}function Wa(e,t,a){var i=Array.prototype.slice.call(arguments,3),s=a(i),n=t.get(s);return void 0===n&&(n=e.apply(this,i),t.set(s,n)),n}function Xa(e,t,a,i,s){return a.bind(t,e,i,s)}function Za(e,t){return Xa(e,this,1===e.length?Qa:Wa,t.cache.create(),t.serializer)}var Ja=function(){return JSON.stringify(arguments)};function ei(){this.cache=Object.create(null)}ei.prototype.get=function(e){return this.cache[e]},ei.prototype.set=function(e,t){this.cache[e]=t};var ti,ai={create:function(){return new ei}},ii={variadic:function(e,t){return Xa(e,this,Wa,t.cache.create(),t.serializer)},monadic:function(e,t){return Xa(e,this,Qa,t.cache.create(),t.serializer)}};!function(e){e.MISSING_VALUE="MISSING_VALUE",e.INVALID_VALUE="INVALID_VALUE",e.MISSING_INTL_API="MISSING_INTL_API"}(ti||(ti={}));var si,ni=function(e){function t(t,a,i){var s=e.call(this,t)||this;return s.code=a,s.originalMessage=i,s}return Ft(t,e),t.prototype.toString=function(){return"[formatjs Error: ".concat(this.code,"] ").concat(this.message)},t}(Error),ri=function(e){function t(t,a,i,s){return e.call(this,'Invalid values for "'.concat(t,'": "').concat(a,'". Options are "').concat(Object.keys(i).join('", "'),'"'),ti.INVALID_VALUE,s)||this}return Ft(t,e),t}(ni),oi=function(e){function t(t,a,i){return e.call(this,'Value for "'.concat(t,'" must be of type ').concat(a),ti.INVALID_VALUE,i)||this}return Ft(t,e),t}(ni),li=function(e){function t(t,a){return e.call(this,'The intl string context variable "'.concat(t,'" was not provided to the string "').concat(a,'"'),ti.MISSING_VALUE,a)||this}return Ft(t,e),t}(ni);function di(e){return"function"==typeof e}function ci(e,t,a,i,s,n,r){if(1===e.length&&Wt(e[0]))return[{type:si.literal,value:e[0].value}];for(var o=[],l=0,d=e;le[t],pi[i])}catch(t){s=e.split(".").reduce((e,t)=>e[t],pi.en)}if(void 0===s&&(s=e.split(".").reduce((e,t)=>e[t],pi.en)),!a.length)return s;const n={};for(let e=0;e{t.find(t=>"object"==typeof e?function(...e){return e.every(t=>JSON.stringify(t)===JSON.stringify(e[0]))}(t,e):t===e)||t.push(e)}),t}function Ti(e,t){return e.filter(e=>e!==t)}function ji(e,t){return e?Object.entries(e).filter(([e])=>t.includes(e)).reduce((e,[t,a])=>Object.assign(e,{[t]:a}),{}):{}}!function(e){e.ArmedAway="hass:car-traction-control",e.ArmedHome="hass:home-outline",e.ArmedNight="hass:weather-night",e.ArmedCustom="hass:star-outline",e.ArmedVacation="hass:airplane-takeoff"}(vi||(vi={})),function(e){e.STATE_ALARM_DISARMED="disarmed",e.STATE_ALARM_ARMED_HOME="armed_home",e.STATE_ALARM_ARMED_AWAY="armed_away",e.STATE_ALARM_ARMED_NIGHT="armed_night",e.STATE_ALARM_ARMED_CUSTOM_BYPASS="armed_custom_bypass",e.STATE_ALARM_ARMED_VACATION="armed_vacation",e.STATE_ALARM_PENDING="pending",e.STATE_ALARM_ARMING="arming",e.STATE_ALARM_DISARMING="disarming",e.STATE_ALARM_TRIGGERED="triggered"}(fi||(fi={})),function(e){e.COMMAND_ALARM_DISARM="disarm",e.COMMAND_ALARM_ARM_HOME="arm_home",e.COMMAND_ALARM_ARM_AWAY="arm_away",e.COMMAND_ALARM_ARM_NIGHT="arm_night",e.COMMAND_ALARM_ARM_CUSTOM_BYPASS="arm_custom_bypass",e.COMMAND_ALARM_ARM_VACATION="arm_vacation"}(_i||(_i={})),function(e){e.Door="door",e.Window="window",e.Motion="motion",e.Tamper="tamper",e.Environmental="environmental",e.Other="other"}(bi||(bi={})),function(e){e.Door="hass:door-closed",e.Window="hass:window-closed",e.Motion="hass:motion-sensor",e.Tamper="hass:vibrate",e.Environmental="hass:fire",e.Other="hass:contactless-payment-circle-outline"}(yi||(yi={})),function(e){e.Notification="notification",e.Action="action"}(wi||(wi={})),function(e){e.ArmedAway="armed_away",e.ArmedHome="armed_home",e.ArmedNight="armed_night",e.ArmedVacation="armed_vacation",e.ArmedCustom="armed_custom_bypass"}(ki||(ki={})),function(e){e.Armed="armed",e.Disarmed="disarmed",e.Triggered="triggered",e.Untriggered="untriggered",e.ArmFailure="arm_failure",e.Arming="arming",e.Pending="pending"}($i||($i={}));const Si=(e,...t)=>{const a={};let i;for(i in e)t.includes(i)||(a[i]=e[i]);return a};function Ci(e){return null!=e}function Mi(e,t){if(null===e||null===t)return e===t;const a=Object.keys(e),i=Object.keys(t);if(a.length!==i.length)return!1;for(const i of a)if("object"==typeof e[i]&&"object"==typeof t[i]){if(!Mi(e[i],t[i]))return!1}else if(e[i]!==t[i])return!1;return!0}function Ni(e,t){const a=e.hasOwnProperty("tagName")?e:e.target;De(a,"show-dialog",{dialogTag:"error-dialog",dialogImport:()=>Promise.resolve().then((function(){return xs})),dialogParams:{error:t}})}function Di(e,t){Ni(t,q` Something went wrong!
${e.body.message?q` ${e.body.message}

`:""} ${e.error}

Please report the bug. `)}const Li=(e,t)=>{var a,i,s,n,r;if(!e)return!1;switch(e){case fi.STATE_ALARM_ARMED_AWAY:return null===(a=t[ki.ArmedAway])||void 0===a?void 0:a.enabled;case fi.STATE_ALARM_ARMED_HOME:return null===(i=t[ki.ArmedHome])||void 0===i?void 0:i.enabled;case fi.STATE_ALARM_ARMED_NIGHT:return null===(s=t[ki.ArmedNight])||void 0===s?void 0:s.enabled;case fi.STATE_ALARM_ARMED_CUSTOM_BYPASS:return null===(n=t[ki.ArmedCustom])||void 0===n?void 0:n.enabled;case fi.STATE_ALARM_ARMED_VACATION:return null===(r=t[ki.ArmedVacation])||void 0===r?void 0:r.enabled;default:return!0}};function zi(e,t){return Object.entries(t).forEach(([t,a])=>{e=t in e&&"object"==typeof e[t]&&null!==e[t]?Object.assign(Object.assign({},e),{[t]:zi(e[t],a)}):Object.assign(Object.assign({},e),{[t]:a})}),e}function Pi(e,t){const a=e=>"object"==typeof e?a(e.name):e.trim().toLowerCase();return a(e) * { color: var(--primary-text-color); transition: color 0.2s ease-in-out; } div.entity-row .secondary { display: block; color: var(--secondary-text-color); transition: color 0.2s ease-in-out; } div.entity-row state-badge { flex: 0 0 40px; } ha-dialog div.wrapper { margin-bottom: -20px; } ha-textfield { min-width: 220px; } a, a:visited { color: var(--primary-color); } mwc-button ha-icon { padding-right: 11px; } mwc-button[trailingIcon] ha-icon { padding: 0px 0px 0px 6px; } mwc-button.vertical { height: 60px; --mdc-button-height: 60px; background: var(--primary-color); --mdc-theme-primary: var(--text-primary-color); } mwc-button.vertical div { display: flex; flex-direction: column; } mwc-button.vertical span { display: flex; } mwc-button.vertical ha-icon { display: flex; margin-left: 50%; } mwc-tab { --mdc-tab-color-default: var(--secondary-text-color); --mdc-tab-text-label-color-default: var(--secondary-text-color); } mwc-tab ha-icon { --mdc-icon-size: 20px; } mwc-tab.disabled { --mdc-theme-primary: var(--disabled-text-color); --mdc-tab-color-default: var(--disabled-text-color); --mdc-tab-text-label-color-default: var(--disabled-text-color); } ha-card settings-row:first-child, ha-card settings-row:first-of-type { border-top: 0px; } ha-card > ha-card { margin: 10px; } `,Ri=o` /* mwc-dialog (ha-dialog) styles */ ha-dialog { --mdc-dialog-min-width: 400px; --mdc-dialog-max-width: 600px; --mdc-dialog-heading-ink-color: var(--primary-text-color); --mdc-dialog-content-ink-color: var(--primary-text-color); --justify-action-buttons: space-between; } /* make dialog fullscreen on small screens */ @media all and (max-width: 450px), all and (max-height: 500px) { ha-dialog { --mdc-dialog-min-width: calc(100vw - env(safe-area-inset-right) - env(safe-area-inset-left)); --mdc-dialog-max-width: calc(100vw - env(safe-area-inset-right) - env(safe-area-inset-left)); --mdc-dialog-min-height: 100%; --mdc-dialog-max-height: 100%; --vertial-align-dialog: flex-end; --ha-dialog-border-radius: 0px; } } ha-dialog div.description { margin-bottom: 10px; } `,Ii=()=>{const e=e=>{let t={};for(var a=0;a3){let i=t.slice(3);if(t.includes("filter")){const t=i.findIndex(e=>"filter"==e),s=i.slice(t+1);i=i.slice(0,t),a=Object.assign(Object.assign({},a),{filter:e(s)})}i.length&&(i.length%2&&(a=Object.assign(Object.assign({},a),{subpage:i.shift()})),i.length&&(a=Object.assign(Object.assign({},a),{params:e(i)})))}return a},Ui=(e,...t)=>{let a={page:e,params:{}};t.forEach(e=>{"string"==typeof e?a=Object.assign(Object.assign({},a),{subpage:e}):"params"in e?a=Object.assign(Object.assign({},a),{params:e.params}):"filter"in e&&(a=Object.assign(Object.assign({},a),{filter:e.filter}))});const i=e=>{let t=Object.keys(e);t=t.filter(t=>e[t]),t.sort();let a="";return t.forEach(t=>{let i=e[t];a=a.length?`${a}/${t}/${i}`:`${t}/${i}`}),a};let s="/alarmo/"+a.page;return a.subpage&&(s=`${s}/${a.subpage}`),i(a.params).length&&(s=`${s}/${i(a.params)}`),a.filter&&(s=`${s}/filter/${i(a.filter)}`),s};let Gi=class extends ne{constructor(){super(...arguments),this.min=0,this.max=100,this.step=10,this.value=0,this.scaleFactor=1,this.unit="",this.disabled=!1}firstUpdated(){this.value>0&&this.value<60&&(this.unit="sec"),"min"==this.unit&&(this.scaleFactor=1/60),"min"==this.unit&&(this.step=1)}render(){return q`

${this.getSlider()}
${this.getValue()}
`}getValue(){const e=Number(Math.round(this.value*this.scaleFactor));return!e&&this.zeroValue?this.zeroValue:`${e} ${this.getUnit()}`}getUnit(){switch(this.unit){case"sec":return gi("components.time_slider.seconds",this.hass.language);case"min":return gi("components.time_slider.minutes",this.hass.language);default:return""}}getSlider(){return q` `}updateValue(e){const t=Number(e.target.value);this.value=Math.round(t/this.scaleFactor)}toggleUnit(){this.unit="min"==this.unit?"sec":"min",this.scaleFactor="min"==this.unit?1/60:1,this.step="min"==this.unit?1:10}};Gi.styles=o` :host { display: flex; flex-direction: column; min-width: 250px; } div.container { display: grid; grid-template-columns: max-content 1fr 60px; grid-template-rows: min-content; grid-template-areas: 'prefix slider value'; } div.prefix { grid-area: prefix; display: flex; align-items: center; } div.slider { grid-area: slider; display: flex; align-items: center; flex: 1; } div.value { grid-area: value; min-width: 40px; display: flex; align-items: center; justify-content: flex-end; cursor: pointer; } ha-slider { width: 100%; } .disabled { color: var(--disabled-text-color); } `,t([le({type:Number})],Gi.prototype,"min",void 0),t([le({type:Number})],Gi.prototype,"max",void 0),t([le({type:Number})],Gi.prototype,"step",void 0),t([le({type:Number})],Gi.prototype,"value",void 0),t([le()],Gi.prototype,"scaleFactor",void 0),t([le({type:String})],Gi.prototype,"unit",void 0),t([le({type:Boolean})],Gi.prototype,"disabled",void 0),t([le({type:String})],Gi.prototype,"zeroValue",void 0),Gi=t([re("time-slider")],Gi);var Fi="M8.59,16.58L13.17,12L8.59,7.41L10,6L16,12L10,18L8.59,16.58Z",Vi="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41Z"; /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ const Hi=2,Yi=6,Bi=e=>(...t)=>({_$litDirective$:e,values:t});class Ki{constructor(e){}T(e,t,a){this.Σdt=e,this.M=t,this.Σct=a}S(e,t){return this.update(e,t)}update(e,t){return this.render(...t)}}let Qi=class extends ne{constructor(){super(...arguments),this.label="",this.items=[],this.clearable=!1,this.icons=!1,this.disabled=!1,this.invalid=!1,this.rowRenderer=e=>{const t=Ci(e.description);return this.icons?q` ${e.name} ${t?q` ${e.description} `:""} `:q` ${e.name} ${t?q` ${e.description} `:""} `}}open(){this.updateComplete.then(()=>{var e,t;null===(t=null===(e=this.shadowRoot)||void 0===e?void 0:e.querySelector("vaadin-combo-box-light"))||void 0===t||t.open()})}disconnectedCallback(){super.disconnectedCallback(),this._overlayMutationObserver&&(this._overlayMutationObserver.disconnect(),this._overlayMutationObserver=void 0)}focus(){this.updateComplete.then(()=>{var e;(null===(e=this.shadowRoot)||void 0===e?void 0:e.querySelector("ha-textfield")).focus()})}shouldUpdate(e){if(e.get("items"))if(Mi(this.items,e.get("items"))){if(1==e.size)return!1}else this.firstUpdated();return!0}firstUpdated(){this._comboBox.items=this.items}render(){const e=Ci(this._value)&&this.items.find(e=>e.value==this._value);return q` ${this.clearable&&e?q` `:""} `}_clearValue(e){e.stopPropagation(),this._setValue("")}get _value(){return Ci(this.value)?this.value:""}_toggleOpen(e){var t,a,i,s,n,r;this.items.length?this._opened?(null===(i=null===(a=null===(t=this.shadowRoot)||void 0===t?void 0:t.querySelector("vaadin-combo-box-light"))||void 0===a?void 0:a.inputElement)||void 0===i||i.blur(),e.stopPropagation()):null===(r=null===(n=null===(s=this.shadowRoot)||void 0===s?void 0:s.querySelector("vaadin-combo-box-light"))||void 0===n?void 0:n.inputElement)||void 0===r||r.focus():e.stopPropagation()}_openedChanged(e){if(this._opened=e.detail.value,this._opened&&"MutationObserver"in window&&!this._overlayMutationObserver){const e=document.querySelector("vaadin-combo-box-overlay");if(!e)return;this._overlayMutationObserver=new MutationObserver(t=>{t.forEach(t=>{var a;"attributes"===t.type&&"inert"===t.attributeName&&!0===e.inert?(e.inert=!1,null===(a=this._overlayMutationObserver)||void 0===a||a.disconnect(),this._overlayMutationObserver=void 0):"childList"===t.type&&t.removedNodes.forEach(e=>{var t;"VAADIN-COMBO-BOX-OVERLAY"===e.nodeName&&(null===(t=this._overlayMutationObserver)||void 0===t||t.disconnect(),this._overlayMutationObserver=void 0)})})}),this._overlayMutationObserver.observe(e,{attributes:!0}),this._overlayMutationObserver.observe(document.body,{childList:!0})}}_valueChanged(e){const t=e.detail.value;t!==this._value&&this._setValue(t)}_setValue(e){this.value=e,setTimeout(()=>{De(this,"value-changed",{value:e})},0)}static get styles(){return o` :host { display: block; } vaadin-combo-box-light { position: relative; } ha-textfield { width: 100%; } ha-textfield > ha-icon-button { --mdc-icon-button-size: 24px; padding: 2px; color: var(--secondary-text-color); } ha-svg-icon { color: var(--input-dropdown-icon-color); position: absolute; cursor: pointer; } ha-svg-icon.disabled { cursor: default; color: var(--disabled-text-color); } .toggle-button { right: 12px; bottom: 5px; } :host([opened]) .toggle-button { color: var(--primary-color); } .clear-button { --mdc-icon-size: 20px; bottom: 5px; right: 36px; } `}};t([le()],Qi.prototype,"label",void 0),t([le()],Qi.prototype,"value",void 0),t([le()],Qi.prototype,"items",void 0),t([le()],Qi.prototype,"clearable",void 0),t([le()],Qi.prototype,"icons",void 0),t([le({type:Boolean})],Qi.prototype,"disabled",void 0),t([de()],Qi.prototype,"_opened",void 0),t([le({attribute:"allow-custom-value",type:Boolean})],Qi.prototype,"allowCustomValue",void 0),t([le({type:Boolean})],Qi.prototype,"invalid",void 0),t([ce("vaadin-combo-box-light",!0)],Qi.prototype,"_comboBox",void 0),Qi=t([re("alarmo-select")],Qi);const Wi={};class Xi extends Ki{constructor(e){if(super(e),this.previousValue=Wi,e.type!==Yi)throw new Error("renderer only supports binding to element")}render(e,t){return I}update(e,[t,a]){var i;const s=this.previousValue===Wi;if(!this.hasChanged(a))return I;this.previousValue=Array.isArray(a)?Array.from(a):a;const n=e.element;if(s){const a=null===(i=e.options)||void 0===i?void 0:i.host;this.addRenderer(n,t,{host:a})}else this.runRenderer(n);return I}hasChanged(e){let t=!0;return Array.isArray(e)?Array.isArray(this.previousValue)&&this.previousValue.length===e.length&&e.every((e,t)=>e===this.previousValue[t])&&(t=!1):this.previousValue===e&&(t=!1),t}}const Zi=Bi(class extends Xi{addRenderer(e,t,a){e.renderer=(e,i,s)=>{G(t.call(a.host,s.item,s,i),e,a)}}runRenderer(e){e.requestContentUpdate()}}),Ji=(e,t)=>Zi(e,t);let es=class extends ne{static get styles(){return o` :host { display: block; } `}render(){return q` `}constructor(){super(),this.addEventListener("clickHeader",this.manageSpoilers)}manageSpoilers(e){const t=e.target;t.getAttribute("active")?t.removeAttribute("active"):t.setAttribute("active","true"),this.querySelectorAll("alarmo-collapsible-header[active]").forEach((function(e){e!==t&&e.removeAttribute("active")}))}};es=t([re("alarmo-collapsible-group")],es);let ts=class extends ne{static get styles(){return o` :host { display: block; } `}render(){return q` `}};ts=t([re("alarmo-collapsible-item")],ts);let as=class extends ne{constructor(){super(),this.clickHeader=new CustomEvent("clickHeader",{detail:{message:"clickHeader happened."},bubbles:!0,composed:!0}),this.active=!1,this.addEventListener("click",this.handleClick)}handleClick(){this.dispatchEvent(this.clickHeader)}render(){return q` `}static get styles(){return o` :host { display: block; cursor: pointer; } :host mwc-list-item::before { position: absolute; top: 0; right: 0; bottom: 0; left: 0; pointer-events: none; content: ''; transition: opacity 15ms linear; will-change: opacity; background-color: black; opacity: 0; } :host mwc-list-item:hover::before { opacity: 0.04; } :host([active]) mwc-list-item::before { opacity: 0.1; } :host([active]) mwc-list-item:hover::before { opacity: 0.12; } :host mwc-list-item:active::before, :host([active]) mwc-list-item:active::before { opacity: 0.14; } ::slotted(ha-icon) { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; margin-right: 16px; } :host mwc-list-item { font-size: 15px; --mdc-typography-body2-font-size: 14px; } :host .chevron { display: block; transition: 0.4s; } :host([active]) .chevron { transform: rotate(180deg); } `}attributeChangedCallback(e,t,a){this.hasAttribute("active")&&this.nextElementSibling?this.nextElementSibling.style.maxHeight=this.nextElementSibling.scrollHeight+"px":this.nextElementSibling&&(this.nextElementSibling.style.maxHeight="0px"),super.attributeChangedCallback(e,t,a)}};t([le({type:CustomEvent})],as.prototype,"clickHeader",void 0),t([le({type:Boolean,attribute:!0,reflect:!0})],as.prototype,"active",void 0),as=t([re("alarmo-collapsible-header")],as);let is=class extends ne{static get styles(){return o` :host { display: block; background-color: var(--card-background-color); max-height: 0px; overflow: hidden; transition: max-height 0.2s ease-out; } .wrapper { } `}render(){return q`
Default details
`}};is=t([re("alarmo-collapsible-body")],is);let ss=class extends(et(ne)){hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.areas=await Ze(this.hass),this.sensors=await Fe(this.hass))}async firstUpdated(){await this._fetchData(),this.selectedArea=Object.keys(this.areas)[0],this.data=Object.assign({},this.areas[this.selectedArea].modes)}render(){return this.data?q`
${gi("panels.general.cards.modes.title",this.hass.language)}
${Object.keys(this.areas).length>1?q` Object({value:e.area_id,name:e.name}))} value=${this.selectedArea} label=${this.hass.localize("ui.components.area-picker.area")} @value-changed=${e=>this.selectArea(e.target.value)} > `:""}
${gi("panels.general.cards.modes.description",this.hass.language)}
${Object.entries(ki).map(([e,t])=>{var a;return q` ${this.hass.localize("component.alarm_control_panel.state._."+t)} ${(null===(a=this.data[t])||void 0===a?void 0:a.enabled)?q` ${gi("common.enabled",this.hass.language)}, ${gi("panels.general.cards.modes.number_sensors_active",this.hass.language,"number",this.getSensorsByMode(t))} `:gi("common.disabled",this.hass.language)} ${this.renderModeConfig(t)} `})}
`:q``}getSensorsByMode(e){return Object.values(this.sensors).filter(t=>t.modes.includes(e)||t.always_on).length}renderModeConfig(e){const t=e in this.data?this.data[e]:void 0;return q`
${gi("panels.general.cards.modes.modes."+e,this.hass.language)}
${gi("panels.general.cards.modes.fields.status.heading",this.hass.language)} ${gi("panels.general.cards.modes.fields.status.description",this.hass.language)}
this.saveData(e,{enabled:!0})}> ${gi("common.enabled",this.hass.language)} this.saveData(e,{enabled:!1})} > ${gi("common.disabled",this.hass.language)}
${gi("panels.general.cards.modes.fields.exit_delay.heading",this.hass.language)} ${gi("panels.general.cards.modes.fields.exit_delay.description",this.hass.language)} this.saveData(e,{exit_time:Number(t.target.value)})} ?disabled=${!(null==t?void 0:t.enabled)} > ${gi("panels.general.cards.modes.fields.entry_delay.heading",this.hass.language)} ${gi("panels.general.cards.modes.fields.entry_delay.description",this.hass.language)} this.saveData(e,{entry_time:Number(t.target.value)})} ?disabled=${!(null==t?void 0:t.enabled)} > ${gi("panels.general.cards.modes.fields.trigger_time.heading",this.hass.language)} ${gi("panels.general.cards.modes.fields.trigger_time.description",this.hass.language)} this.saveData(e,{trigger_time:Number(t.target.value)})} ?disabled=${!(null==t?void 0:t.enabled)} > `}selectArea(e){e!=this.selectedArea&&(this.selectedArea=e,this.data=Object.assign({},this.areas[e].modes))}saveClick(e){Je(this.hass,{area_id:this.selectedArea,modes:this.data}).catch(t=>Di(t,e)).then()}saveData(e,t){this.data=Object.assign(Object.assign({},this.data),{[e]:Object.assign(Object.assign({},this.data[e]||{enabled:!1,exit_time:0,entry_time:0,trigger_time:0}),t)}),Je(this.hass,{area_id:this.selectedArea,modes:this.data}).catch(e=>Di(e,this.shadowRoot.querySelector("ha-card"))).then()}static get styles(){return o` ${qi} alarmo-collapsible-header:first-of-type { border-top: 1px solid var(--divider-color); } .description { margin: 8px; padding: 12px; color: var(--primary-color); filter: brightness(0.85); font-size: 14px; line-height: 1.5em; min-height: 36px; display: flex; align-items: center; position: relative; } .description::before { position: absolute; top: 0; right: 0; bottom: 0; left: 0; pointer-events: none; content: ''; background: rgba(var(--rgb-primary-color), 0.12); border-radius: 5px; } .description ha-icon { --mdc-icon-size: 36px; display: inline; float: left; margin-right: 12px; align-self: flex-start; } `}};t([le()],ss.prototype,"hass",void 0),t([le({type:Boolean})],ss.prototype,"narrow",void 0),t([le()],ss.prototype,"config",void 0),t([le()],ss.prototype,"areas",void 0),t([le()],ss.prototype,"sensors",void 0),t([le()],ss.prototype,"data",void 0),t([le()],ss.prototype,"selectedArea",void 0),ss=t([re("alarm-mode-card")],ss);let ns=class extends ne{constructor(){super(...arguments),this.threeLine=!1}render(){return q`
`}static get styles(){return o` :host { display: flex; flex-direction: row; padding: 0px 16px; align-items: center; min-height: 72px; } :host([large]) { align-items: normal; flex-direction: column; border-top: 1px solid var(--divider-color); border-bottom: 1px solid var(--divider-color); padding: 16px 16px; } :host([narrow]) { align-items: normal; flex-direction: column; border-bottom: none; border-top: 1px solid var(--divider-color); padding: 16px 16px; } :host([nested]) { border: none; padding: 0px 16px 16px 16px; margin-top: -16px; min-height: 40px; } :host([nested]:not([narrow])) { padding: 16px 16px 16px 32px; } :host([first]) { border-top: none; } :host([last]) { border-bottom: none; } :host([dialog]) { border: none; padding: 12px 0px; } ::slotted(ha-switch) { padding: 16px 0; } .info { flex: 1 0 60px; } :host([large]) .info, :host([narrow]) .info { flex: 1 0 40px; } :host([nested]) .info { flex: 1 0 26px; } :host([dialog]) .info { flex: 1 0 40px; padding-bottom: 8px; } .secondary { color: var(--secondary-text-color); } :host(:not([large]):not([narrow])):not([dialog])) ::slotted(*) { max-width: 66%; } `}};t([le({type:Boolean,reflect:!0})],ns.prototype,"narrow",void 0),t([le({type:Boolean,reflect:!0})],ns.prototype,"large",void 0),t([le({type:Boolean,attribute:"three-line"})],ns.prototype,"threeLine",void 0),t([le({type:Boolean})],ns.prototype,"nested",void 0),t([le({type:Boolean})],ns.prototype,"dialog",void 0),ns=t([re("settings-row")],ns);let rs=class extends ne{constructor(){super(...arguments),this.header="",this.open=!1}render(){return q` ${this.open?q`
{this.open=!1}}>${this.header} {this.open=!1}}>
{this.open=!1}}>${this.header} {this.open=!1}}>
`:q`
{this.open=!0}}>${this.header} {this.open=!0}}>
`} `}static get styles(){return o` :host { } div.header { display: flex; align-items: center; padding: 0px 16px; cursor: pointer; } div.header.open:first-of-type { border-bottom: 1px solid var(--divider-color); } div.header.open:last-of-type { border-top: 1px solid var(--divider-color); } :host([narrow]) div.header { border-top: 1px solid var(--divider-color); border-bottom: none; } div.header span { display: flex; flex-grow: 1; } div.seperator { height: 1px; background: var(--divider-color); } `}};t([le({type:Boolean,reflect:!0})],rs.prototype,"narrow",void 0),t([le()],rs.prototype,"header",void 0),t([le()],rs.prototype,"open",void 0),rs=t([re("collapsible-section")],rs);let os=class extends(et(ne)){constructor(){super(...arguments),this.areas={}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){if(!this.hass)return;const e=await Ge(this.hass);this.config=e,this.areas=await Ze(this.hass),this.selection=e.mqtt}firstUpdated(){(async()=>{await Ie()})()}render(){return this.hass&&this.selection?q`
${gi("panels.general.cards.mqtt.title",this.hass.language)}
${gi("panels.general.cards.mqtt.description",this.hass.language)}
${gi("panels.general.cards.mqtt.fields.state_topic.heading",this.hass.language)} ${gi("panels.general.cards.mqtt.fields.state_topic.description",this.hass.language)} {this.selection={...this.selection,state_topic:e.target.value}}} > ${Object.values(fi).filter(e=>Object.values(this.areas).some(t=>Li(e,t.modes))).map(e=>q` ${xi(e)} ${gi("panels.general.cards.mqtt.fields.state_payload.item",this.hass.language,"{state}",xi(e))} {this.selection=zi(this.selection,{state_payload:{[e]:t.target.value}})}} > `)} ${gi("panels.general.cards.mqtt.fields.event_topic.heading",this.hass.language)} ${gi("panels.general.cards.mqtt.fields.event_topic.description",this.hass.language)} {this.selection={...this.selection,event_topic:e.target.value}}} > ${gi("panels.general.cards.mqtt.fields.command_topic.heading",this.hass.language)} ${gi("panels.general.cards.mqtt.fields.command_topic.description",this.hass.language)} {this.selection={...this.selection,command_topic:e.target.value}}} > ${Object.values(_i).filter(e=>Object.values(this.areas).some(t=>Li((e=>{switch(e){case _i.COMMAND_ALARM_DISARM:return fi.STATE_ALARM_DISARMED;case _i.COMMAND_ALARM_ARM_HOME:return fi.STATE_ALARM_ARMED_HOME;case _i.COMMAND_ALARM_ARM_AWAY:return fi.STATE_ALARM_ARMED_AWAY;case _i.COMMAND_ALARM_ARM_NIGHT:return fi.STATE_ALARM_ARMED_NIGHT;case _i.COMMAND_ALARM_ARM_CUSTOM_BYPASS:return fi.STATE_ALARM_ARMED_CUSTOM_BYPASS;case _i.COMMAND_ALARM_ARM_VACATION:return fi.STATE_ALARM_ARMED_VACATION;default:return}})(e),t.modes))).map(e=>q` ${xi(e)} ${gi("panels.general.cards.mqtt.fields.command_payload.item",this.hass.language,"{command}",xi(e))} {this.selection=zi(this.selection,{command_payload:{[e]:t.target.value}})}} > `)} ${gi("panels.general.cards.mqtt.fields.require_code.heading",this.hass.language)} ${gi("panels.general.cards.mqtt.fields.require_code.description",this.hass.language)} {this.selection={...this.selection,require_code:e.target.checked}}} >
${this.hass.localize("ui.common.save")}
`:q``}saveClick(e){this.hass&&Be(this.hass,{mqtt:Object.assign(Object.assign({},this.selection),{enabled:!0})}).catch(t=>Di(t,e)).then(()=>{this.cancelClick()})}cancelClick(){Pe(0,Ui("general"),!0)}};os.styles=qi,t([le()],os.prototype,"narrow",void 0),t([le()],os.prototype,"config",void 0),t([le()],os.prototype,"areas",void 0),t([le()],os.prototype,"selection",void 0),os=t([re("mqtt-config-card")],os); /** * @license * Copyright 2017 Google LLC * SPDX-License-Identifier: BSD-3-Clause */ class ls extends Ki{constructor(e){if(super(e),this.vt=I,e.type!==Hi)throw Error(this.constructor.directiveName+"() can only be used in child bindings")}render(e){if(e===I)return this.Vt=void 0,this.vt=e;if(e===R)return e;if("string"!=typeof e)throw Error(this.constructor.directiveName+"() called with a non-string value");if(e===this.vt)return this.Vt;this.vt=e;const t=[e];return t.raw=t,this.Vt={_$litType$:this.constructor.resultType,strings:t,values:[]}}}ls.directiveName="unsafeHTML",ls.resultType=1;const ds=Bi(ls);let cs=class extends ne{render(){return q`
${this.renderCheckmark()} ${this.renderButton()}
`}renderCheckmark(){return this.checkmark?q`
`:q``}renderButton(){return this.cancellable?q`
`:void 0!==this.badge?q`
${this.badge}
`:q``}_toggleSelect(){if(!this.value||!this.clickable)return;this.selectable&&(this.checked=!this.checked);const e=new CustomEvent("value-changed",{detail:this.value});this.dispatchEvent(e)}_buttonClick(){const e=new CustomEvent("button-clicked",{detail:this.value});this.dispatchEvent(e)}static get styles(){return o` :host { margin: 4px; } .chip { display: flex; position: relative; height: 36px; padding: 0px 16px; align-items: center; color: var(--primary-text-color); user-select: none; font-weight: 400; z-index: 1; } :host([clickable]) .chip { cursor: pointer; color: var(--rgb-primary-color); opacity: 0.85; } .chip:before { position: absolute; top: 0; right: 0; bottom: 0; left: 0; pointer-events: none; content: ''; border-radius: 8px; border: 1px solid var(--primary-text-color); opacity: 0.24; z-index: -1; } :host([clickable]) .chip:hover, :host([clickable]) .chip:active { opacity: 1; } :host([clickable]) .chip:hover:before { opacity: 0.3; } :host([clickable]) .chip:active:before { opacity: 0.06; background: var(--primary-text-color); } :host .chip.selected:before, :host([cancellable]) .chip:before { background: rgba(var(--rgb-primary-color), 0.18); border: none; opacity: 1; } :host .chip.selected:hover:before { background: rgba(var(--rgb-primary-color), 0.24); opacity: 1; } :host .chip.selected:active:before { background: rgba(var(--rgb-primary-color), 0.3); opacity: 1; } .chip div.checkmark-container { width: 0px; height: 100%; transition: width 0.1s ease-in-out; overflow: hidden; display: flex; align-items: center; margin: 0px 4px 0px -4px; --mdc-icon-size: 18px; } .chip.selected div.checkmark-container { width: 18px; } .chip div.button-container { width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; margin: 0px -16px 0px 6px; cursor: pointer; --mdc-icon-size: 20px; position: relative; z-index: 1; opacity: 0.85; color: var(--dark-primary-color); } .chip div.button-container:before { position: absolute; top: 3px; right: 3px; bottom: 3px; left: 3px; pointer-events: none; content: ''; border-radius: 15px; z-index: -1; } .chip div.button-container:hover, .chip div.button-container:hover { opacity: 1; } .chip div.button-container:hover:before { background: rgba(var(--rgb-primary-color), 0.12); } .chip div.button-container:active:before { background: rgba(var(--rgb-primary-color), 0.24); } .chip div.badge-container { width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; margin: 0px -9px 0px 9px; position: relative; z-index: 1; font-size: 0.875em; } .chip div.badge-container:before { position: absolute; top: 0px; right: 0px; bottom: 0px; left: 0px; pointer-events: none; content: ''; border-radius: 50%; z-index: -1; } :host([table]) .chip { height: 40px; } :host([table]) .chip:before { border-radius: 4px; } `}};t([le({type:String})],cs.prototype,"value",void 0),t([le({type:Boolean})],cs.prototype,"checked",void 0),t([le({type:Boolean})],cs.prototype,"checkmark",void 0),t([le({type:Boolean})],cs.prototype,"selectable",void 0),t([le({type:Boolean})],cs.prototype,"clickable",void 0),t([le({type:Boolean})],cs.prototype,"cancellable",void 0),t([le({type:Number})],cs.prototype,"badge",void 0),t([le({type:Boolean})],cs.prototype,"table",void 0),cs=t([re("alarmo-chip")],cs);let hs=class extends ne{constructor(){super(...arguments),this.value=[]}render(){return this.items?q` ${Object.values(this.items).map(e=>q` ${e.name} `)} `:q``}_itemChanged(e){const t=e.target.checked,a=String(e.detail);if(this.selectable){this.value.includes(a)&&!t?this.value=this.value.filter(e=>e!=a):!this.value.includes(a)&&t&&(this.value=[...this.value,a]);const e=new CustomEvent("value-changed",{detail:this.value});this.dispatchEvent(e)}else{const e=new CustomEvent("value-changed",{detail:a});this.dispatchEvent(e)}}static get styles(){return o` :host { display: flex; flex-direction: row; flex: 1; margin: 0px -4px; flex-wrap: wrap; } `}};t([le()],hs.prototype,"items",void 0),t([le()],hs.prototype,"value",void 0),t([le({type:Boolean})],hs.prototype,"selectable",void 0),hs=t([re("alarmo-chip-set")],hs);let us=class extends ne{set filters(e){this.filterConfig||(this.filterConfig=e)}shouldUpdate(e){return e.get("filters")&&!this.filterConfig&&(this.filterConfig=e.get("filters")),!0}render(){if(!this.columns||!this.data)return q``;const e=this.data.filter(e=>this.filterTableData(e,this.filterConfig));return q` ${this.renderFilterRow()}
${this.renderHeaderRow()} ${e.length?e.map(e=>this.renderDataRow(e)):q`
`}
`}renderHeaderRow(){return this.columns?q`
${Object.values(this.columns).map(e=>e.hide?"":q`
${e.title||""}
`)}
`:q``}renderDataRow(e){return this.columns?q`
this.handleClick(String(e.id))} > ${Object.entries(this.columns).map(([t,a])=>a.hide?"":q`
${a.renderer?a.renderer(e):e[t]}
`)}
`:q``}filterTableData(e,t){return!t||Object.keys(t).every(a=>{if(!Object.keys(e).includes(a))return!0;const i=t[a].value;return!i||!i.length||(Array.isArray(e[a])?e[a].some(e=>i.includes(e)):i.includes(e[a]))})}_getFilteredItems(){return this.data.filter(e=>!this.filterTableData(e,this.filterConfig)).length}handleClick(e){if(!this.selectable)return;const t=new CustomEvent("row-click",{detail:{id:e}});this.dispatchEvent(t)}renderFilterRow(){var e;return this.filterConfig?q`
${this.renderFilterMenu()} ${this._getFilteredItems()?q` ${gi("components.table.filter.hidden_items",this.hass.language,"number",this._getFilteredItems())} `:""}
`:q``}_toggleFilterMenu(e){const t=e.target;this._menu.anchor=t,this._menu.open?this._menu.close():(this.filterSelection=Object.entries(this.filterConfig).reduce((e,[t,a])=>Object.assign(Object.assign({},e),{[t]:ji(a,["value"])}),{}),this._menu.show())}renderFilterMenu(){return this.filterConfig&&this.filterSelection?q` ${gi("components.table.filter.label",this.hass.language)} {this._menu.close(),setTimeout(()=>this._menu.anchor.blur(),50)}} > ${Object.keys(this.filterConfig).map(e=>{if(this.filterConfig[e].binary)return q` `;let t=this.filterConfig[e].items;t=t.map(t=>{var a;return t.badge&&"function"==typeof t.badge?{...t,badge:t.badge(null===(a=this.data)||void 0===a?void 0:a.filter(t=>this.filterTableData(t,Si(this.filterSelection,e))))}:t});const a=this.filterSelection[e].value;return q` `})} `:q``}_updateFilterSelection(e,t){"boolean"==typeof t&&(t=t?this.filterConfig[e].items[0].value:[],1==Object.keys(this.filterConfig).length&&(this._menu.close(),setTimeout(()=>this._menu.anchor.blur(),50))),this.filterSelection=Object.assign(Object.assign({},this.filterSelection),{[e]:{value:t}})}_clearFilters(){Object.keys(this.filterConfig).forEach(e=>{this.filterConfig=Object.assign(Object.assign({},this.filterConfig),{[e]:Object.assign(Object.assign({},this.filterConfig[e]),{value:[]})})})}_applyFilterSelection(){Object.keys(this.filterConfig).forEach(e=>{this.filterConfig=Object.assign(Object.assign({},this.filterConfig),{[e]:Object.assign(Object.assign({},this.filterConfig[e]),this.filterSelection[e])})})}};us.styles=o` :host { width: 100%; } div.table { display: inline-flex; flex-direction: column; box-sizing: border-box; width: 100%; } div.table .header { font-weight: bold; } div.table-row { display: flex; width: 100%; height: 52px; border-top: 1px solid var(--divider-color); flex-direction: row; position: relative; } div.table-cell { align-self: center; overflow: hidden; text-overflow: ellipsis; flex-shrink: 0; box-sizing: border-box; } div.table-cell.text { padding: 4px 16px; } div.table-cell.grow { flex-grow: 1; flex-shrink: 1; } div.table-cell > ha-switch { width: 68px; height: 48px; display: flex; align-items: center; justify-content: center; } div.table-cell.center { display: flex; align-items: center; justify-content: center; } div.table-cell.right { display: flex; align-items: center; justify-content: flex-end; } div.table-cell > ha-icon-button { color: var(--secondary-text-color); } div.table-cell > * { transition: color 0.2s ease-in-out; } div.table .header div.table-cell span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; } div.table-row.selectable { cursor: pointer; } .table-row::before { position: absolute; top: 0; right: 0; bottom: 0; left: 0; opacity: 0.12; pointer-events: none; content: ''; border-radius: 4px; } div.table-row.selectable:hover::before { background-color: rgba(var(--rgb-primary-text-color), 0.5); } div.table-row.warning::before { background-color: var(--error-color); opacity: 0.06; } div.table-row.warning:hover::before { background-color: var(--error-color); opacity: 0.12; } div.table-row.warning span { color: var(--error-color); } ha-icon { color: var(--state-icon-color); padding: 8px; } .secondary { color: var(--secondary-text-color); display: flex; padding-top: 4px; } a, a:visited { color: var(--primary-color); } span.disabled { color: var(--secondary-text-color); } span.secondary.disabled { color: var(--disabled-text-color); } ha-icon.disabled { color: var(--state-unavailable-color); } div.table-filter { display: flex; width: 100%; min-height: 52px; border-top: 1px solid var(--divider-color); box-sizing: border-box; padding: 2px 8px; flex: 1; position: relative; flex-direction: row; align-items: center; } mwc-menu .header { display: flex; padding: 8px 16px; font-weight: bold; } mwc-menu ha-icon-button { position: absolute; top: 8px; right: 8px; } div.dropdown-item { display: flex; flex-direction: column; flex-shrink: 0; padding: 8px 16px; width: 100%; min-height: 52px; box-sizing: border-box; } div.dropdown-item .name { display: inline-flex; } div.dropdown-item alarmo-chips { display: flex; flex-direction: row; } div.dropdown-item.checkbox { flex-direction: row; align-items: center; } ha-button-menu mwc-button { margin-left: 16px; } `,t([le()],us.prototype,"hass",void 0),t([le()],us.prototype,"columns",void 0),t([le()],us.prototype,"data",void 0),t([de()],us.prototype,"filterConfig",void 0),t([de()],us.prototype,"filterSelection",void 0),t([le({type:Boolean})],us.prototype,"selectable",void 0),t([ce("mwc-menu",!0)],us.prototype,"_menu",void 0),us=t([re("alarmo-table")],us);let ms=class extends ne{async showDialog(e){this._params=e,await this.updateComplete}async closeDialog(){this._params&&this._params.cancel(),this._params=void 0}render(){return this._params?q`
${this._params.title}
${this._params.description}
${this.hass.localize("ui.dialogs.generic.cancel")} ${this.hass.localize("ui.dialogs.generic.ok")}
`:q``}confirmClick(){this._params.confirm()}cancelClick(){this._params.cancel()}static get styles(){return o` ${qi} div.wrapper { color: var(--primary-text-color); } `}};t([le({attribute:!1})],ms.prototype,"hass",void 0),t([de()],ms.prototype,"_params",void 0),ms=t([re("confirm-delete-dialog")],ms);var ps=Object.freeze({__proto__:null,get ConfirmDeleteDialog(){return ms}});let gs=class extends(et(ne)){constructor(){super(...arguments),this.areas={},this.sensors={},this.automations={},this.name=""}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.areas=await Ze(this.hass),this.sensors=await Fe(this.hass),this.automations=await He(this.hass))}async showDialog(e){await this._fetchData(),this._params=e,e.area_id&&(this.area_id=e.area_id,this.name=this.areas[this.area_id].name),await this.updateComplete}async closeDialog(){this._params=void 0,this.area_id=void 0,this.name=""}render(){return this._params?q`
${this.area_id?gi("panels.general.dialogs.edit_area.title",this.hass.language,"{area}",this.areas[this.area_id].name):gi("panels.general.dialogs.create_area.title",this.hass.language)}
this.name=e.target.value} value="${this.name}" > ${this.area_id?q` ${gi("panels.general.dialogs.edit_area.name_warning",this.hass.language)} `:""} ${this.area_id?"":q` Object({value:e.area_id,name:e.name}))} value=${this.selectedArea} label="${gi("panels.general.dialogs.create_area.fields.copy_from",this.hass.language)}" clearable=${!0} @value-changed=${e=>this.selectedArea=e.target.value} > `}
${this.hass.localize("ui.common.save")} ${this.area_id?q` ${this.hass.localize("ui.common.delete")} `:""}
`:q``}saveClick(e){const t=this.name.trim();if(!t.length)return;let a={name:t};this.area_id?a=Object.assign(Object.assign({},a),{area_id:this.area_id}):this.selectedArea&&(a=Object.assign(Object.assign({},a),{modes:Object.assign({},this.areas[this.selectedArea].modes)})),Je(this.hass,a).catch(t=>Di(t,e)).then(()=>{this.closeDialog()})}async deleteClick(e){if(!this.area_id)return;const t=Object.values(this.sensors).filter(e=>e.area==this.area_id).length,a=Object.values(this.automations).filter(e=>{var t;return null===(t=e.triggers)||void 0===t?void 0:t.map(e=>e.area).includes(this.area_id)}).length;let i=!1;var s,n;i=!t&&!a||await new Promise(i=>{De(e.target,"show-dialog",{dialogTag:"confirm-delete-dialog",dialogImport:()=>Promise.resolve().then((function(){return ps})),dialogParams:{title:gi("panels.general.dialogs.remove_area.title",this.hass.language),description:gi("panels.general.dialogs.remove_area.description",this.hass.language,"sensors",String(t),"automations",String(a)),cancel:()=>i(!1),confirm:()=>i(!0)}})}),i&&(s=this.hass,n=this.area_id,s.callApi("POST","alarmo/area",{area_id:n,remove:!0})).catch(t=>Di(t,e)).then(()=>{this.closeDialog()})}static get styles(){return o` ${qi} div.wrapper { color: var(--primary-text-color); } span.note { color: var(--secondary-text-color); } ha-textfield { display: block; } alarmo-select { margin-top: 10px; } `}};t([le({attribute:!1})],gs.prototype,"hass",void 0),t([de()],gs.prototype,"_params",void 0),t([le()],gs.prototype,"areas",void 0),t([le()],gs.prototype,"sensors",void 0),t([le()],gs.prototype,"automations",void 0),t([le()],gs.prototype,"name",void 0),t([le()],gs.prototype,"area_id",void 0),t([le()],gs.prototype,"selectedArea",void 0),gs=t([re("create-area-dialog")],gs);var vs=Object.freeze({__proto__:null,get CreateAreaDialog(){return gs}});let fs=class extends(et(ne)){constructor(){super(...arguments),this.areas={},this.sensors={},this.automations={}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.areas=await Ze(this.hass),this.sensors=await Fe(this.hass),this.automations=await He(this.hass))}render(){if(!this.hass)return q``;const e=Object.values(this.areas);e.sort(Pi);const t={actions:{width:"48px"},name:{title:this.hass.localize("ui.components.area-picker.add_dialog.name"),width:"40%",grow:!0,text:!0},remarks:{title:gi("panels.general.cards.areas.table.remarks",this.hass.language),width:"60%",hide:this.narrow,text:!0}},a=Object.values(e).map(t=>{const a=Object.values(this.sensors).filter(e=>e.area==t.area_id).length,i=1==Object.values(e).length?Object.values(this.automations).filter(e=>{var a,i;return(null===(a=e.triggers)||void 0===a?void 0:a.map(e=>e.area).includes(t.area_id))||!(null===(i=e.triggers)||void 0===i?void 0:i.map(e=>e.area).length)}).length:Object.values(this.automations).filter(e=>{var a;return null===(a=e.triggers)||void 0===a?void 0:a.map(e=>e.area).includes(t.area_id)}).length,s=`${gi("panels.general.cards.areas.table.summary_sensors",this.hass.language,"number",a)}`,n=`${gi("panels.general.cards.areas.table.summary_automations",this.hass.language,"number",i)}`;return{id:t.area_id,actions:q` this.editClick(e,t.area_id)} .path=${"M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z"}> `,name:xi(t.name),remarks:ds(gi("panels.general.cards.areas.table.summary",this.hass.language,"summary_sensors",s,"summary_automations",n))}});return q`
${gi("panels.general.cards.areas.description",this.hass.language)}
${gi("panels.general.cards.areas.no_items",this.hass.language)}
${gi("panels.general.cards.areas.actions.add",this.hass.language)}
`}addClick(e){const t=e.target;De(t,"show-dialog",{dialogTag:"create-area-dialog",dialogImport:()=>Promise.resolve().then((function(){return vs})),dialogParams:{}})}editClick(e,t){const a=e.target;De(a,"show-dialog",{dialogTag:"create-area-dialog",dialogImport:()=>Promise.resolve().then((function(){return vs})),dialogParams:{area_id:t}})}};fs.styles=qi,t([le()],fs.prototype,"narrow",void 0),t([le()],fs.prototype,"path",void 0),t([le()],fs.prototype,"config",void 0),t([le()],fs.prototype,"areas",void 0),t([le()],fs.prototype,"sensors",void 0),t([le()],fs.prototype,"automations",void 0),fs=t([re("area-config-card")],fs);let _s=class extends ne{constructor(){super(...arguments),this.name=""}async showDialog(e){this._params=e;const t=await Ge(this.hass);this.name=t.master.name||"",await this.updateComplete}async closeDialog(){this._params=void 0}render(){return this._params?q`
${gi("panels.general.dialogs.edit_master.title",this.hass.language)}
this.name=e.target.value} value="${this.name}" > ${gi("panels.general.dialogs.edit_area.name_warning",this.hass.language)}
${this.hass.localize("ui.common.save")} ${this.hass.localize("ui.common.cancel")}
`:q``}saveClick(){const e=this.name.trim();e.length&&Be(this.hass,{master:{enabled:!0,name:e}}).catch().then(()=>{this.closeDialog()})}static get styles(){return o` div.wrapper { color: var(--primary-text-color); } span.note { color: var(--secondary-text-color); } ha-textfield { display: block; } `}};t([le({attribute:!1})],_s.prototype,"hass",void 0),t([de()],_s.prototype,"_params",void 0),t([le()],_s.prototype,"name",void 0),_s=t([re("edit-master-dialog")],_s);var bs=Object.freeze({__proto__:null,get EditMasterDialog(){return _s}});let ys=class extends(et(ne)){constructor(){super(...arguments),this.areas={},this.automations={}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.config=await Ge(this.hass),this.areas=await Ze(this.hass),this.automations=await He(this.hass),this.data=ji(this.config,["trigger_time","disarm_after_trigger","mqtt","master"]))}firstUpdated(){(async()=>{await Ie()})()}render(){var e,t,a,i,s,n,r,o;return this.hass&&this.config&&this.data?"mqtt_configuration"==this.path.subpage?q` `:this.path.params.edit_area?q` `:q`
${gi("panels.general.cards.general.description",this.hass.language)}
${gi("panels.general.cards.general.fields.disarm_after_trigger.heading",this.hass.language)} ${gi("panels.general.cards.general.fields.disarm_after_trigger.description",this.hass.language)} {this.saveData({disarm_after_trigger:e.target.checked})}} > ${gi("panels.general.cards.general.fields.enable_mqtt.heading",this.hass.language)} ${gi("panels.general.cards.general.fields.enable_mqtt.description",this.hass.language)} {this.saveData({mqtt:{...this.data.mqtt,enabled:e.target.checked}})}} > ${(null===(i=null===(a=this.data)||void 0===a?void 0:a.mqtt)||void 0===i?void 0:i.enabled)?q`
Pe(0,Ui("general","mqtt_configuration"),!0)} > ${gi("panels.general.cards.general.actions.setup_mqtt",this.hass.language)}
`:""} ${Object.keys(this.areas).length>=2?q` ${gi("panels.general.cards.general.fields.enable_master.heading",this.hass.language)} ${gi("panels.general.cards.general.fields.enable_master.description",this.hass.language)} =2} ?disabled=${Object.keys(this.areas).length<2} @change=${this.toggleEnableMaster} > `:""} ${(null===(o=null===(r=this.data)||void 0===r?void 0:r.master)||void 0===o?void 0:o.enabled)&&Object.keys(this.areas).length>=2?q`
${gi("panels.general.cards.general.actions.setup_master",this.hass.language)}
`:""}
`:q``}setupMasterClick(e){const t=e.target;De(t,"show-dialog",{dialogTag:"edit-master-dialog",dialogImport:()=>Promise.resolve().then((function(){return bs})),dialogParams:{}})}async toggleEnableMaster(e){const t=e.target;let a=t.checked;if(!a){const i=Object.values(this.automations).filter(e=>e.triggers.some(e=>!e.area));if(i.length){await new Promise(e=>{De(t,"show-dialog",{dialogTag:"confirm-delete-dialog",dialogImport:()=>Promise.resolve().then((function(){return ps})),dialogParams:{title:gi("panels.general.dialogs.disable_master.title",this.hass.language),description:gi("panels.general.dialogs.disable_master.description",this.hass.language,"automations",String(i.length)),cancel:()=>e(!1),confirm:()=>e(!0)}})})?!a&&i.length&&i.forEach(t=>{Xe(this.hass,t.automation_id).catch(t=>Di(t,e))}):(a=!0,t.checked=!0)}}this.saveData({master:Object.assign(Object.assign({},this.data.master),{enabled:a})})}saveData(e){this.hass&&this.data&&(this.data=Object.assign(Object.assign({},this.data),e),Be(this.hass,this.data).catch(e=>Di(e,this.shadowRoot.querySelector("ha-card"))).then())}};ys.styles=qi,t([le()],ys.prototype,"narrow",void 0),t([le()],ys.prototype,"path",void 0),t([le()],ys.prototype,"data",void 0),t([le()],ys.prototype,"config",void 0),t([le()],ys.prototype,"areas",void 0),t([le()],ys.prototype,"automations",void 0),ys=t([re("alarm-view-general")],ys);const ws=(e,t)=>{if("binary_sensor"==function(e){const t="string"==typeof e?e:e.entity_id;return String(t.split(".").shift())}(e.entity_id)){if(t)return!0;const a=e.attributes.device_class;return!!a&&!!["carbon_monoxide","door","garage_door","gas","heat","lock","moisture","motion","moving","occupancy","opening","presence","safety","smoke","sound","vibration","window"].includes(a)}return!1},ks=e=>{switch(e.attributes.device_class){case"door":case"garage_door":case"lock":return bi.Door;case"window":return bi.Window;case"carbon_monoxide":case"gas":case"heat":case"moisture":case"smoke":case"safety":return bi.Environmental;case"motion":case"moving":case"occupancy":case"presence":return bi.Motion;case"sound":case"opening":case"vibration":return bi.Tamper;default:return}},$s=e=>{const t=t=>t.filter(t=>e.includes(t));return{[bi.Door]:{modes:t([ki.ArmedAway,ki.ArmedHome,ki.ArmedNight,ki.ArmedVacation]),always_on:!1,allow_open:!1,arm_on_close:!1,use_entry_delay:!0,use_exit_delay:!1},[bi.Window]:{modes:t([ki.ArmedAway,ki.ArmedHome,ki.ArmedNight,ki.ArmedVacation]),always_on:!1,allow_open:!1,arm_on_close:!1,use_entry_delay:!1,use_exit_delay:!1},[bi.Motion]:{modes:t([ki.ArmedAway,ki.ArmedVacation]),always_on:!1,allow_open:!0,arm_on_close:!1,use_entry_delay:!0,use_exit_delay:!0},[bi.Tamper]:{modes:t([ki.ArmedAway,ki.ArmedHome,ki.ArmedNight,ki.ArmedVacation,ki.ArmedCustom]),always_on:!1,allow_open:!1,arm_on_close:!1,use_entry_delay:!1,use_exit_delay:!1},[bi.Environmental]:{modes:t([ki.ArmedAway,ki.ArmedHome,ki.ArmedNight,ki.ArmedVacation,ki.ArmedCustom]),always_on:!0,allow_open:!1,arm_on_close:!1,use_entry_delay:!1,use_exit_delay:!1}}};let As=class extends ne{async showDialog(e){this._params=e,await this.updateComplete}async closeDialog(){this._params=void 0}render(){return this._params?q`
${this.hass.localize("state_badge.default.error")}
${this._params.error||""}
${this.hass.localize("ui.dialogs.generic.ok")}
`:q``}static get styles(){return o` div.wrapper { color: var(--primary-text-color); } `}};t([le({attribute:!1})],As.prototype,"hass",void 0),t([de()],As.prototype,"_params",void 0),As=t([re("error-dialog")],As);var xs=Object.freeze({__proto__:null,get ErrorDialog(){return As}});let Os=class extends(et(ne)){constructor(){super(...arguments),this.sensorGroups={},this.sensors={}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.sensorGroups=await Ye(this.hass),this.sensors=await Fe(this.hass))}async showDialog(e){await this._fetchData(),this._params=e,e.group_id&&Object.keys(this.sensorGroups).includes(e.group_id)?this.data=Object.assign({},this.sensorGroups[e.group_id]):this.data={name:"",entities:[],timeout:600},await this.updateComplete}async closeDialog(){this._params=void 0}render(){return this._params?q`
${gi("panels.sensors.dialogs.create_group.fields.name.heading",this.hass.language)} ${gi("panels.sensors.dialogs.create_group.fields.name.description",this.hass.language)} this.data={...this.data,name:String(e.target.value).trim()}} value="${this.data.name}" > ${gi("panels.sensors.dialogs.create_group.fields.sensors.heading",this.hass.language)} ${gi("panels.sensors.dialogs.create_group.fields.sensors.description",this.hass.language)}
${this.renderSensorOptions()}
${gi("panels.sensors.dialogs.create_group.fields.timeout.heading",this.hass.language)} ${gi("panels.sensors.dialogs.create_group.fields.timeout.description",this.hass.language)} this.data={...this.data,timeout:Number(e.target.value)}} >
${this.hass.localize("ui.common.save")} ${this.data.group_id?q` ${this.hass.localize("ui.common.delete")} `:""}
`:q``}renderHeader(){return q` ${this.data.group_id?gi("panels.sensors.dialogs.edit_group.title",this.hass.language,"{name}",this.sensorGroups[this.data.group_id].name):gi("panels.sensors.dialogs.create_group.title",this.hass.language)} `}renderSensorOptions(){const e=Object.keys(this.sensors).filter(e=>!Ci(this.sensors[e].group)||this.sensors[e].group===this.data.group_id).map(e=>{const t=this.hass.states[e],a=Object.entries(bi).find(([,t])=>t==this.sensors[e].type)[0];return{value:e,name:xi(Oi(t)),icon:yi[a]}});return e.sort(Pi),e.length?q` this.data={...this.data,entities:e.detail}} > `:gi("panels.sensors.cards.sensors.no_items",this.hass.language)}saveClick(e){var t,a;this.data.name.length&&(this.data.group_id&&this.data.name==this.sensorGroups[this.data.group_id].name||!Object.values(this.sensorGroups).find(e=>e.name.toLowerCase()==this.data.name.toLowerCase()))?this.data.entities.length<2?Ni(e,gi("panels.sensors.dialogs.create_group.errors.insufficient_sensors",this.hass.language)):(t=this.hass,a=this.data,t.callApi("POST","alarmo/sensor_groups",a)).catch(t=>Di(t,e)).then(()=>{this.closeDialog()}):Ni(e,gi("panels.sensors.dialogs.create_group.errors.invalid_name",this.hass.language))}deleteClick(e){var t,a;this.data.group_id&&(t=this.hass,a=this.data.group_id,t.callApi("POST","alarmo/sensor_groups",{group_id:a,remove:!0})).catch(t=>Di(t,e)).then(()=>{this.closeDialog()})}static get styles(){return o` ${Ri} div.wrapper { color: var(--primary-text-color); } mwc-button.warning { --mdc-theme-primary: var(--error-color); } `}};t([le({attribute:!1})],Os.prototype,"hass",void 0),t([de()],Os.prototype,"_params",void 0),t([le()],Os.prototype,"sensorGroups",void 0),t([le()],Os.prototype,"sensors",void 0),t([le()],Os.prototype,"data",void 0),Os=t([re("create-sensor-group-dialog")],Os);var Es=Object.freeze({__proto__:null,get CreateSensorGroupDialog(){return Os}});let Ts=class extends(et(ne)){constructor(){super(...arguments),this.sensorGroups={},this.sensors={}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.sensorGroups=await Ye(this.hass),this.sensors=await Fe(this.hass))}async showDialog(e){await this._fetchData(),this._params=e,await this.updateComplete}async closeDialog(){this._params=void 0}render(){return this._params?q`
${gi("panels.sensors.dialogs.manage_groups.description",this.hass.language)}
${Object.keys(this.sensorGroups).length?Object.values(this.sensorGroups).map(e=>this.renderGroup(e)):gi("panels.sensors.dialogs.manage_groups.no_items",this.hass.language)}
${gi("panels.sensors.dialogs.manage_groups.actions.new_group",this.hass.language)}
`:q``}renderHeader(){return q` ${gi("panels.sensors.dialogs.manage_groups.title",this.hass.language)} `}renderGroup(e){return q` this.editGroupClick(t,e.group_id)} >
${e.name} ${gi("panels.general.cards.areas.table.summary_sensors",this.hass.language,"{number}",String(e.entities.length))}
`}createGroupClick(e){const t=e.target;De(t,"show-dialog",{dialogTag:"create-sensor-group-dialog",dialogImport:()=>Promise.resolve().then((function(){return Es})),dialogParams:{}})}editGroupClick(e,t){const a=e.target;De(a,"show-dialog",{dialogTag:"create-sensor-group-dialog",dialogImport:()=>Promise.resolve().then((function(){return Es})),dialogParams:{group_id:t}})}static get styles(){return o` ${Ri} div.wrapper { color: var(--primary-text-color); } div.container { display: flex; flex-wrap: wrap; } ha-card { width: 100%; text-align: center; margin: 4px; box-sizing: border-box; padding: 8px; color: var(--primary-text-color); font-size: 16px; cursor: pointer; display: flex; flex-direction: row; } ha-card:hover { background: rgba(var(--rgb-secondary-text-color), 0.1); } ha-card ha-icon { --mdc-icon-size: 24px; display: flex; flex: 0 0 40px; margin: 0px 10px; align-items: center; color: var(--state-icon-color); } ha-card ha-icon-button { --mdc-icon-size: 24px; display: flex; flex: 0 0 40px; margin: 0px 10px; align-items: center; } ha-card div { display: flex; flex-wrap: wrap; flex: 1; } ha-card span { display: flex; flex: 0 0 100%; } ha-card span.description { color: var(--secondary-text-color); } mwc-button ha-icon { padding-right: 11px; } `}};t([le({attribute:!1})],Ts.prototype,"hass",void 0),t([de()],Ts.prototype,"_params",void 0),t([le()],Ts.prototype,"sensorGroups",void 0),t([le()],Ts.prototype,"sensors",void 0),Ts=t([re("manage-sensor-groups-dialog")],Ts);var js=Object.freeze({__proto__:null,get ManageSensorGroupsDialog(){return Ts}});let Ss=class extends(et(ne)){constructor(){super(...arguments),this.showBypassModes=!1}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){var e;if(!this.hass)return;const t=await Ze(this.hass);this.areas=t;const a=await Ye(this.hass);this.sensorGroups=a;const i=await Fe(this.hass);this.data=Object.keys(i).includes(this.item)?i[this.item]:void 0,this.data&&!(null===(e=this.data)||void 0===e?void 0:e.area)&&1==Object.keys(t).length&&(this.data=Object.assign(Object.assign({},this.data),{area:Object.keys(this.areas)[0]}))}render(){if(!this.data)return q``;this.hass.states[this.data.entity_id];return q`
${gi("panels.sensors.cards.editor.title",this.hass.language)}
${gi("panels.sensors.cards.editor.description",this.hass.language,"{entity}",Oi(this.hass.states[this.item]))}
${Object.keys(this.areas).length>1?q` ${gi("panels.sensors.cards.editor.fields.area.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.area.description",this.hass.language)} Object({value:e.area_id,name:e.name}))} value=${this.data.area} label=${gi("panels.sensors.cards.editor.fields.area.heading",this.hass.language)} @value-changed=${e=>this.data={...this.data,area:e.target.value}} ?invalid=${!this.data.area} > `:""} ${gi("panels.sensors.cards.editor.fields.device_type.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.device_type.description",this.hass.language)} e!=bi.Other).map(([t,a])=>Object({value:a,name:gi(`panels.sensors.cards.editor.fields.device_type.choose.${a}.name`,e.language),description:gi(`panels.sensors.cards.editor.fields.device_type.choose.${a}.description`,e.language),icon:yi[t]}))} label=${gi("panels.sensors.cards.editor.fields.device_type.heading",this.hass.language)} clearable=${!0} icons=${!0} value=${this.data.type} @value-changed=${e=>this.setType(e.target.value||bi.Other)} > 3}> ${gi("panels.sensors.cards.editor.fields.modes.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.modes.description",this.hass.language)}
${this.modesByArea(this.data.area).map(e=>q` {this.setMode(e)}} ?disabled=${this.data.always_on} > ${gi("common.modes_short."+e,this.hass.language)} `)}
${gi("panels.sensors.cards.editor.fields.group.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.group.description",this.hass.language)}
${Object.keys(this.sensorGroups).length?q` {this.data={...this.data,group:e.detail.value}}} > `:""} ${gi("panels.sensors.cards.editor.actions.setup_groups",this.hass.language)}
${!this.data.type||[bi.Environmental,bi.Other].includes(this.data.type)?q` ${gi("panels.sensors.cards.editor.fields.always_on.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.always_on.description",this.hass.language)} this._SetData({always_on:e.target.checked})} > `:""} ${!this.data.type||[bi.Window,bi.Door,bi.Motion,bi.Other].includes(this.data.type)?q` ${gi("panels.sensors.cards.editor.fields.use_exit_delay.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.use_exit_delay.description",this.hass.language)} this._SetData({use_exit_delay:e.target.checked})} > ${this.data.type&&![bi.Motion,bi.Other].includes(this.data.type)||!this.data.use_exit_delay?"":q` ${gi("panels.sensors.cards.editor.fields.allow_open.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.allow_open.description",this.hass.language)} this._SetData({allow_open:e.target.checked})} > `} `:""} ${!this.data.type||[bi.Window,bi.Door,bi.Motion,bi.Other].includes(this.data.type)?q` ${gi("panels.sensors.cards.editor.fields.use_entry_delay.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.use_entry_delay.description",this.hass.language)} this._SetData({use_entry_delay:e.target.checked})} > `:""} ${!this.data.type||[bi.Door,bi.Other].includes(this.data.type)?q` ${gi("panels.sensors.cards.editor.fields.arm_on_close.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.arm_on_close.description",this.hass.language)} this._SetData({arm_on_close:e.target.checked})} > `:""} ${!this.data.type||[bi.Window,bi.Door,bi.Other].includes(this.data.type)?q` ${gi("panels.sensors.cards.editor.fields.auto_bypass.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.auto_bypass.description",this.hass.language)} this._SetData({auto_bypass:e.target.checked})} > ${this.data.auto_bypass?q` ${gi("panels.sensors.cards.editor.fields.auto_bypass.modes",this.hass.language)}
${this.modesByArea(this.data.area).map(e=>q` {this.setBypassMode(e)}} > ${gi("common.modes_short."+e,this.hass.language)} `)}
`:""} `:""} ${gi("panels.sensors.cards.editor.fields.trigger_unavailable.heading",this.hass.language)} ${gi("panels.sensors.cards.editor.fields.trigger_unavailable.description",this.hass.language)} this._SetData({trigger_unavailable:e.target.checked})} >
${this.hass.localize("ui.common.save")} ${gi("panels.sensors.cards.editor.actions.remove",this.hass.language)}
`;var e}modesByArea(e){const t=Object.keys(this.areas).reduce((e,t)=>Object.assign(e,{[t]:Object.entries(this.areas[t].modes).filter(([,e])=>e.enabled).map(([e])=>e)}),{});return e?t[e]:Object.values(t).reduce((e,t)=>e.filter(e=>t.includes(e)))}_SetData(e){if(this.data)for(const[t,a]of Object.entries(e))switch(t){case"always_on":this.data=Object.assign(Object.assign({},this.data),{always_on:1==a}),a&&(this.data=Object.assign(Object.assign({},this.data),{arm_on_close:!1,use_exit_delay:!1,use_entry_delay:!1,allow_open:!1,auto_bypass:!1}));break;case"use_entry_delay":this.data=Object.assign(Object.assign({},this.data),{use_entry_delay:1==a});break;case"use_exit_delay":this.data=Object.assign(Object.assign({},this.data),{use_exit_delay:1==a}),a&&(this.data=Object.assign(Object.assign({},this.data),{allow_open:!1}));break;case"arm_on_close":this.data=Object.assign(Object.assign({},this.data),{arm_on_close:1==a}),a&&(this.data=Object.assign(Object.assign({},this.data),{always_on:!1,allow_open:!1}));break;case"allow_open":this.data=Object.assign(Object.assign({},this.data),{allow_open:1==a}),a&&(this.data=Object.assign(Object.assign({},this.data),{arm_on_close:!1,always_on:!1,use_exit_delay:!0}));break;case"auto_bypass":this.data=Object.assign(Object.assign({},this.data),{auto_bypass:1==a}),a&&(this.data=Object.assign(Object.assign({},this.data),{always_on:!1}));break;case"trigger_unavailable":this.data=Object.assign(Object.assign({},this.data),{trigger_unavailable:1==a})}}setMode(e){this.data&&(this.data=Object.assign(Object.assign({},this.data),{modes:this.data.modes.includes(e)?Ti(this.data.modes,e):Ei(this.data.modes.concat([e]))}))}setBypassMode(e){this.data&&(this.data=Object.assign(Object.assign({},this.data),{auto_bypass_modes:this.data.auto_bypass_modes.includes(e)?Ti(this.data.auto_bypass_modes,e):Ei(this.data.auto_bypass_modes.concat([e]))}))}setType(e){if(!this.data)return;const t=e!=bi.Other?$s(this.modesByArea(this.data.area))[e]:{};this.data=Object.assign(Object.assign(Object.assign({},this.data),{type:e}),t)}deleteClick(e){var t,a;(t=this.hass,a=this.item,t.callApi("POST","alarmo/sensors",{entity_id:a,remove:!0})).catch(t=>Di(t,e)).then(()=>{this.cancelClick()})}saveClick(e){if(!this.data)return;const t=[];this.data=Object.assign(Object.assign({},this.data),{auto_bypass_modes:this.data.auto_bypass_modes.filter(e=>this.data.modes.includes(e))}),this.data.area||t.push(gi("panels.sensors.cards.editor.errors.no_area",this.hass.language)),this.data.modes.length||this.data.always_on||t.push(gi("panels.sensors.cards.editor.errors.no_modes",this.hass.language)),this.data.auto_bypass&&!this.data.auto_bypass_modes.length&&t.push(gi("panels.sensors.cards.editor.errors.no_auto_bypass_modes",this.hass.language)),t.length?Ni(e,q` ${gi("panels.sensors.cards.editor.errors.description",this.hass.language)} `):Ke(this.hass,Object.assign({},this.data)).catch(t=>Di(t,e)).then(()=>{this.cancelClick()})}cancelClick(){Pe(0,Ui("sensors"),!0)}manageGroupsClick(e){const t=e.target;De(t,"show-dialog",{dialogTag:"manage-sensor-groups-dialog",dialogImport:()=>Promise.resolve().then((function(){return js})),dialogParams:{}})}getSensorGroups(){return Object.keys(this.sensorGroups).map(e=>Object({value:e,name:this.sensorGroups[e].name}))}};Ss.styles=qi,t([le()],Ss.prototype,"hass",void 0),t([le()],Ss.prototype,"narrow",void 0),t([le()],Ss.prototype,"item",void 0),t([le()],Ss.prototype,"data",void 0),t([le()],Ss.prototype,"showBypassModes",void 0),Ss=t([re("sensor-editor-card")],Ss);const Cs=e=>Object.keys(e.modes).filter(t=>e.modes[t].enabled),Ms=e=>{let t=[];return Object.values(e).forEach(e=>{t=[...t,...Cs(e)]}),t=Ei(t),t.sort((e,t)=>{const a=Object.values(ki);return a.findIndex(t=>t==e)-a.findIndex(e=>e==t)}),t},Ns="no_area";let Ds=class extends(et(ne)){hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.areas=await Ze(this.hass),this.sensors=await Fe(this.hass))}async firstUpdated(){this.path&&2==this.path.length&&"filter"==this.path[0]&&(this.selectedArea=this.path[1])}render(){return this.hass&&this.areas&&this.sensors?q`
${gi("panels.sensors.cards.sensors.description",this.hass.language)}
{Pe(0,Ui("sensors",{params:{edit:e.detail.id}}),!0)}} > ${gi("panels.sensors.cards.sensors.table.no_items",this.hass.language)}
`:q``}tableColumns(){const e=()=>q` ${gi("panels.sensors.cards.sensors.table.no_area_warning",this.hass.language)} `;return{icon:{width:"40px",renderer:t=>{const a=this.hass.states[t.entity_id],i=Object.keys(bi).find(e=>bi[e]==t.type),s=a?yi[i]:"hass:help-circle-outline";return t.area==Ns?q` ${e()} `:q` ${a?gi(`panels.sensors.cards.editor.fields.device_type.choose.${t.type}.name`,this.hass.language):this.hass.localize("state_badge.default.entity_not_found")} `}},name:{title:this.hass.localize("ui.components.entity.entity-picker.entity"),width:"60%",grow:!0,text:!0,renderer:t=>q` ${t.area==Ns?e():""} ${t.name} ${t.entity_id} `},modes:{title:gi("panels.sensors.cards.sensors.table.arm_modes",this.hass.language),width:"25%",hide:this.narrow,text:!0,renderer:t=>q` ${t.area==Ns?e():""} ${t.always_on?gi("panels.sensors.cards.sensors.table.always_on",this.hass.language):t.modes.length?t.modes.map(e=>gi("common.modes_short."+e,this.hass.language)).join(", "):this.hass.localize("state_attributes.climate.preset_mode.none")} `},enabled:{title:gi("common.enabled",this.hass.language),width:"68px",align:"center",renderer:e=>q` {e.stopPropagation()}} ?checked=${e.enabled} @change=${t=>this.toggleEnabled(t,e.entity_id)} > `}}}getTableData(){let e=Object.keys(this.sensors).map(e=>{const t=this.hass.states[e],a=this.sensors[e],i=a.area?Cs(this.areas[a.area]):Ms(this.areas);return Object.assign(Object.assign({},a),{id:e,name:Oi(t),modes:a.always_on?i:a.modes.filter(e=>i.includes(e)),warning:!a.area,area:a.area||Ns})});return e.sort(Pi),e}toggleEnabled(e,t){const a=e.target.checked;Ke(this.hass,{entity_id:t,enabled:a}).catch(t=>Di(t,e)).then()}removeCustomName(e){let t={entity_id:e,name:""};Ke(this.hass,t)}getTableFilterOptions(){let e=Object.values(this.areas).map(e=>Object({value:e.area_id,name:e.name,badge:t=>t.filter(t=>t.area==e.area_id).length})).sort(Pi);Object.values(this.sensors).filter(e=>!e.area).length&&(e=[{value:Ns,name:this.hass.localize("state_attributes.climate.preset_mode.none"),badge:e=>e.filter(e=>e.area==Ns).length},...e]);const t=Ms(this.areas).map(e=>Object({value:e,name:gi("common.modes_short."+e,this.hass.language),badge:t=>t.filter(t=>t.modes.includes(e)).length}));return{area:{name:gi("components.table.filter.item",this.hass.language,"name",gi("panels.actions.cards.new_action.fields.area.heading",this.hass.language)),items:e,value:this.selectedArea?[this.selectedArea]:[]},modes:{name:gi("components.table.filter.item",this.hass.language,"name",gi("panels.actions.cards.new_action.fields.mode.heading",this.hass.language)),items:t,value:this.selectedMode?[this.selectedMode]:[]}}}};Ds.styles=qi,t([le()],Ds.prototype,"hass",void 0),t([le()],Ds.prototype,"narrow",void 0),t([le()],Ds.prototype,"areas",void 0),t([le()],Ds.prototype,"sensors",void 0),t([le()],Ds.prototype,"selectedArea",void 0),t([le()],Ds.prototype,"selectedMode",void 0),t([le()],Ds.prototype,"path",void 0),Ds=t([re("sensors-overview-card")],Ds);let Ls=class extends(et(ne)){constructor(){super(...arguments),this.addSelection=[],this.areas={},this.sensors={}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){this.hass&&(this.areas=await Ze(this.hass))}async firstUpdated(){this.areas=await Ze(this.hass),this.sensors=await Fe(this.hass)}render(){const e={checkbox:{width:"48px",renderer:e=>q` this.toggleSelect(t,e.id)} ?checked=${this.addSelection.includes(e.id)} > `},icon:{width:"40px",renderer:e=>q` `},name:{title:this.hass.localize("ui.components.entity.entity-picker.entity"),width:"40%",grow:!0,text:!0,renderer:e=>q` ${xi(e.name)} ${e.id} `},type:{title:gi("panels.sensors.cards.add_sensors.table.type",this.hass.language),width:"40%",hide:this.narrow,text:!0,renderer:e=>e.type?gi(`panels.sensors.cards.editor.fields.device_type.choose.${e.type}.name`,this.hass.language):this.hass.localize("state.default.unknown")}},t=((e,t,a=!1)=>{const i=Object.values(e.states).filter(e=>ws(e,a)).filter(e=>!t.includes(e.entity_id)).map(e=>Object({id:e.entity_id,name:Oi(e),icon:Ai(e)}));return i.sort(Pi),i})(this.hass,Object.keys(this.sensors),!0).map(e=>Object.assign(Object.assign({},e),{type:ks(this.hass.states[e.id]),isSupportedType:void 0!==ks(this.hass.states[e.id])?"true":"false"}));return q`
${gi("panels.sensors.cards.add_sensors.description",this.hass.language)}
${gi("panels.sensors.cards.add_sensors.no_items",this.hass.language)}
${gi("panels.sensors.cards.add_sensors.actions.add_to_alarm",this.hass.language)}
`}toggleSelect(e,t){const a=e.target.checked;this.addSelection=a&&!this.addSelection.includes(t)?[...this.addSelection,t]:a?this.addSelection:this.addSelection.filter(e=>e!=t)}addSelected(e){if(!this.hass)return;const t=Object.values(this.areas).map(e=>Object.entries(e.modes).filter(([,e])=>e.enabled).map(([e])=>e)).reduce((e,t)=>e.filter(e=>t.includes(e)));this.addSelection.map(e=>function(e,t){if(!e)return null;const a=Ce(e.entity_id);let i={entity_id:e.entity_id,modes:[],use_entry_delay:!0,use_exit_delay:!0,arm_on_close:!1,allow_open:!1,always_on:!1,auto_bypass:!1,auto_bypass_modes:[],trigger_unavailable:!1,type:bi.Other,enabled:!0};if("binary_sensor"==a){const a=ks(e);a&&(i=Object.assign(Object.assign(Object.assign({},i),{type:a}),$s(t)[a]))}return i}(this.hass.states[e],t)).map(e=>1==Object.keys(this.areas).length?Object.assign(e,{area:Object.keys(this.areas)[0]}):e).filter(e=>e).forEach(t=>{Ke(this.hass,t).catch(t=>Di(t,e)).then()}),this.addSelection=[]}getTableFilterOptions(){return{isSupportedType:{name:gi("panels.sensors.cards.add_sensors.actions.filter_supported",this.hass.language),items:[{value:"true",name:"true"}],value:["true"],binary:!0}}}};Ls.styles=qi,t([le()],Ls.prototype,"hass",void 0),t([le()],Ls.prototype,"narrow",void 0),t([le()],Ls.prototype,"addSelection",void 0),t([le()],Ls.prototype,"areas",void 0),t([le()],Ls.prototype,"sensors",void 0),Ls=t([re("add-sensors-card")],Ls);let zs=class extends ne{firstUpdated(){(async()=>{await Ie()})()}render(){var e,t;if(!this.hass)return q``;if(this.path.params.edit)return q` `;{const a=null===(e=this.path.filter)||void 0===e?void 0:e.area,i=null===(t=this.path.filter)||void 0===t?void 0:t.mode;return q` `}}};t([le()],zs.prototype,"hass",void 0),t([le()],zs.prototype,"narrow",void 0),t([le()],zs.prototype,"path",void 0),zs=t([re("alarm-view-sensors")],zs);let Ps=class extends ne{constructor(){super(...arguments),this.data={can_arm:!0,can_disarm:!0,is_override_code:!1},this.repeatCode="",this.areas={}}async firstUpdated(){if(this.users=await Ve(this.hass),this.areas=await Ze(this.hass),this.item){const e=this.users[this.item];this.data=Si(e,"code","code_format","code_length")}this.data=Object.assign(Object.assign({},this.data),{area_limit:(this.data.area_limit||[]).filter(e=>Object.keys(this.areas).includes(e))}),(this.data.area_limit||[]).length||(this.data=Object.assign(Object.assign({},this.data),{area_limit:Object.keys(this.areas)}))}render(){var e;return this.users?q`
${this.item?gi("panels.codes.cards.edit_user.title",this.hass.language):gi("panels.codes.cards.new_user.title",this.hass.language)}
${this.item?gi("panels.codes.cards.edit_user.description",this.hass.language,"{name}",this.users[this.item].name):gi("panels.codes.cards.new_user.description",this.hass.language)}
${gi("panels.codes.cards.new_user.fields.name.heading",this.hass.language)} ${gi("panels.codes.cards.new_user.fields.name.description",this.hass.language)} this.data={...this.data,name:e.target.value}} > ${this.item?q` ${gi("panels.codes.cards.edit_user.fields.old_code.heading",this.hass.language)} ${gi("panels.codes.cards.edit_user.fields.old_code.description",this.hass.language)} this.data={...this.data,old_code:String(e.target.value).trim()}} > `:""} ${this.item&&!(null===(e=this.data.old_code)||void 0===e?void 0:e.length)?"":q` ${gi("panels.codes.cards.new_user.fields.code.heading",this.hass.language)} ${gi("panels.codes.cards.new_user.fields.code.description",this.hass.language)} this.data={...this.data,code:String(e.target.value).trim()}} > ${gi("panels.codes.cards.new_user.fields.confirm_code.heading",this.hass.language)} ${gi("panels.codes.cards.new_user.fields.confirm_code.description",this.hass.language)} this.repeatCode=String(e.target.value).trim()} > `} ${gi("panels.codes.cards.new_user.fields.can_arm.heading",this.hass.language)} ${gi("panels.codes.cards.new_user.fields.can_arm.description",this.hass.language)} this.data={...this.data,can_arm:e.target.checked}} > ${gi("panels.codes.cards.new_user.fields.can_disarm.heading",this.hass.language)} ${gi("panels.codes.cards.new_user.fields.can_disarm.description",this.hass.language)} this.data={...this.data,can_disarm:e.target.checked}} > ${this.getAreaOptions().length>=2?q` ${gi("panels.codes.cards.new_user.fields.area_limit.heading",this.hass.language)} ${gi("panels.codes.cards.new_user.fields.area_limit.description",this.hass.language)}
${this.getAreaOptions().map(e=>{var t;const a=(this.data.area_limit||[]).includes(e.value)||!(null===(t=this.data.area_limit)||void 0===t?void 0:t.length);return q`
this.toggleSelectArea(e.value,t.target.checked)} ?disabled=${a&&(this.data.area_limit||[]).length<=1} ?checked=${a} > this.toggleSelectArea(e.value,!a)}> ${e.name}
`})}
`:""} ${gi("panels.codes.cards.new_user.fields.is_override_code.heading",this.hass.language)} ${gi("panels.codes.cards.new_user.fields.is_override_code.description",this.hass.language)} this.data={...this.data,is_override_code:e.target.checked}} >
${this.hass.localize("ui.common.save")} ${this.item?q` ${this.hass.localize("ui.common.delete")} `:""}
`:q``}getAreaOptions(){let e=Object.keys(this.areas||{}).map(e=>Object({value:e,name:this.areas[e].name}));return e.sort(Pi),e}toggleSelectArea(e,t){if((this.data.area_limit||[]).length<=1&&!t)return;let a=this.data.area_limit||[];a=t?a.includes(e)?a:[...a,e]:a.includes(e)?a.filter(t=>t!=e):a,this.data=Object.assign(Object.assign({},this.data),{area_limit:a})}deleteClick(e){var t,a;this.item&&(t=this.hass,a=this.item,t.callApi("POST","alarmo/users",{user_id:a,remove:!0})).catch(t=>Di(t,e)).then(()=>{this.cancelClick()})}saveClick(e){var t,a,i;let s=Object.assign({},this.data);(null===(t=s.name)||void 0===t?void 0:t.length)?(null===(a=s.code)||void 0===a?void 0:a.length)&&!(s.code.length<4)||this.item&&!(null===(i=s.old_code)||void 0===i?void 0:i.length)?(s.code||"").length&&s.code!==this.repeatCode?(Ni(e,gi("panels.codes.cards.new_user.errors.code_mismatch",this.hass.language)),this.data=Si(this.data,"code"),this.repeatCode=""):(this.item&&(s.old_code||"").length<4&&Si(s,"old_code","code"),this.getAreaOptions().length&&!this.getAreaOptions().every(e=>(this.data.area_limit||[]).includes(e.value))||(s=Object.assign(Object.assign({},s),{area_limit:[]})),Qe(this.hass,s).catch(t=>Di(t,e)).then(()=>{this.cancelClick()})):Ni(e,gi("panels.codes.cards.new_user.errors.no_code",this.hass.language)):Ni(e,gi("panels.codes.cards.new_user.errors.no_name",this.hass.language))}cancelClick(){Pe(0,Ui("codes"),!0)}static get styles(){return o` ${qi} div.checkbox-list { display: flex; flex-direction: row; } div.checkbox-list div { display: flex; align-items: center; } div.checkbox-list div span { cursor: pointer; } `}};t([le()],Ps.prototype,"hass",void 0),t([le()],Ps.prototype,"narrow",void 0),t([le()],Ps.prototype,"item",void 0),t([le()],Ps.prototype,"data",void 0),t([le()],Ps.prototype,"repeatCode",void 0),Ps=t([re("user-editor-card")],Ps);let qs=class extends(et(ne)){constructor(){super(...arguments),this.users={}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){if(!this.hass)return;const e=await Ge(this.hass);this.data=ji(e,["code_arm_required","code_disarm_required","code_format"]);const t=await Ve(this.hass);this.users=t}render(){return this.hass&&this.data?"new_user"==this.path.subpage?q` `:this.path.params.edit_user?q` `:q`
${gi("panels.codes.cards.codes.description",this.hass.language)}
${gi("panels.codes.cards.codes.fields.code_arm_required.heading",this.hass.language)} ${gi("panels.codes.cards.codes.fields.code_arm_required.description",this.hass.language)} {this.saveData({code_arm_required:e.target.checked})}} > ${gi("panels.codes.cards.codes.fields.code_disarm_required.heading",this.hass.language)} ${gi("panels.codes.cards.codes.fields.code_disarm_required.description",this.hass.language)} {this.saveData({code_disarm_required:e.target.checked})}} > ${gi("panels.codes.cards.codes.fields.code_format.heading",this.hass.language)} ${gi("panels.codes.cards.codes.fields.code_format.description",this.hass.language)} {this.saveData({code_format:"number"})}} ?disabled=${!this.data.code_arm_required&&!this.data.code_disarm_required} > ${gi("panels.codes.cards.codes.fields.code_format.code_format_number",this.hass.language)} {this.saveData({code_format:"text"})}} ?disabled=${!this.data.code_arm_required&&!this.data.code_disarm_required} > ${gi("panels.codes.cards.codes.fields.code_format.code_format_text",this.hass.language)}
${this.usersPanel()} `:q``}usersPanel(){if(!this.hass)return q``;const e=Object.values(this.users);e.sort(Pi);const t={icon:{width:"40px"},name:{title:this.hass.localize("ui.components.area-picker.add_dialog.name"),width:"40%",grow:!0,text:!0},code_format:{title:gi("panels.codes.cards.codes.fields.code_format.heading",this.hass.language),width:"40%",hide:this.narrow,text:!0},enabled:{title:gi("common.enabled",this.hass.language),width:"68px",align:"center"}},a=e.map(e=>({id:e.user_id,icon:q` `,name:xi(e.name),code_format:"number"==e.code_format?xi(gi("panels.codes.cards.codes.fields.code_format.code_format_number",this.hass.language)):"text"==e.code_format?xi(gi("panels.codes.cards.codes.fields.code_format.code_format_text",this.hass.language)):this.hass.localize("state.default.unknown"),enabled:q` {e.stopPropagation()}} ?checked=${e.enabled} @change=${t=>this.toggleEnabled(t,e.user_id)} > `}));return q`
${gi("panels.codes.cards.user_management.description",this.hass.language)}
{const t=String(e.detail.id);Pe(0,Ui("codes",{params:{edit_user:t}}),!0)}} > ${gi("panels.codes.cards.user_management.no_items",this.hass.language)}
${gi("panels.codes.cards.user_management.actions.new_user",this.hass.language)}
`}addUserClick(){Pe(0,Ui("codes","new_user"),!0)}saveData(e){this.hass&&(this.data=Object.assign(Object.assign({},this.data),e),Be(this.hass,this.data).catch(e=>Di(e,this.shadowRoot.querySelector("ha-card"))).then())}toggleEnabled(e,t){const a=e.target.checked;Qe(this.hass,{user_id:t,enabled:a}).catch(t=>Di(t,e)).then()}};qs.styles=qi,t([le()],qs.prototype,"hass",void 0),t([le()],qs.prototype,"narrow",void 0),t([le()],qs.prototype,"path",void 0),t([le()],qs.prototype,"data",void 0),t([le()],qs.prototype,"users",void 0),qs=t([re("alarm-view-codes")],qs);const Rs=(e,t)=>{switch(e){case ki.ArmedAway:return{value:ki.ArmedAway,name:gi("common.modes_short.armed_away",t.language),icon:vi.ArmedAway};case ki.ArmedHome:return{value:ki.ArmedHome,name:gi("common.modes_short.armed_home",t.language),icon:vi.ArmedHome};case ki.ArmedNight:return{value:ki.ArmedNight,name:gi("common.modes_short.armed_night",t.language),icon:vi.ArmedNight};case ki.ArmedCustom:return{value:ki.ArmedCustom,name:gi("common.modes_short.armed_custom_bypass",t.language),icon:vi.ArmedCustom};case ki.ArmedVacation:return{value:ki.ArmedVacation,name:gi("common.modes_short.armed_vacation",t.language),icon:vi.ArmedVacation}}},Is=(e,t)=>{switch(e){case $i.Armed:return{value:$i.Armed,name:gi("panels.actions.cards.new_notification.fields.event.choose.armed.name",t.language),description:gi("panels.actions.cards.new_notification.fields.event.choose.armed.description",t.language),icon:"hass:shield-check-outline"};case $i.Disarmed:return{value:$i.Disarmed,name:gi("panels.actions.cards.new_notification.fields.event.choose.disarmed.name",t.language),description:gi("panels.actions.cards.new_notification.fields.event.choose.disarmed.description",t.language),icon:"hass:shield-off-outline"};case $i.Triggered:return{value:$i.Triggered,name:gi("panels.actions.cards.new_notification.fields.event.choose.triggered.name",t.language),description:gi("panels.actions.cards.new_notification.fields.event.choose.triggered.description",t.language),icon:"hass:bell-alert-outline"};case $i.Untriggered:return{value:$i.Untriggered,name:gi("panels.actions.cards.new_notification.fields.event.choose.untriggered.name",t.language),description:gi("panels.actions.cards.new_notification.fields.event.choose.untriggered.description",t.language),icon:"hass:bell-off-outline"};case $i.ArmFailure:return{value:$i.ArmFailure,name:gi("panels.actions.cards.new_notification.fields.event.choose.arm_failure.name",t.language),description:gi("panels.actions.cards.new_notification.fields.event.choose.arm_failure.description",t.language),icon:"hass:alert-outline"};case $i.Arming:return{value:$i.Arming,name:gi("panels.actions.cards.new_notification.fields.event.choose.arming.name",t.language),description:gi("panels.actions.cards.new_notification.fields.event.choose.arming.description",t.language),icon:"hass:home-export-outline"};case $i.Pending:return{value:$i.Pending,name:gi("panels.actions.cards.new_notification.fields.event.choose.pending.name",t.language),description:gi("panels.actions.cards.new_notification.fields.event.choose.pending.description",t.language),icon:"hass:home-import-outline"}}},Us=(e,t,a)=>0==e?{name:a.master.name,value:0}:Object.keys(t).includes(String(e))?{name:t[e].name,value:e}:{name:String(e),value:e},Gs=(e,...t)=>{const a=t.map(t=>{if(!t)return null;const a=Ce(t),i=Me(t);let s={value:t,name:i.replace(/_/g," ").split(" ").map(e=>e.substring(0,1).toUpperCase()+e.substring(1)).join(" "),icon:"hass:home",description:t};switch(a){case"notify":const t=e.states["device_tracker."+i.replace("mobile_app_","")];s=t?Object.assign(Object.assign({},s),{name:t.attributes.friendly_name||Me(t.entity_id),icon:t.attributes.icon||"hass:cellphone-text"}):Object.assign(Object.assign({},s),{icon:"hass:comment-alert"});break;case"tts":s=Object.assign(Object.assign({},s),{icon:"hass:microphone"})}return s}).filter(Ci);return a.sort((e,t)=>{const a=Ce(e.value),i=Ce(t.value);return a!=i?Pi(a,i):Pi(e,t)}),a},Fs=(e,t)=>{let a=[];const i=Object.keys(e).filter(t=>Object.values(e[t].modes).some(e=>e.enabled));return t.master.enabled&&i.length>1&&(a=[...a,0]),a=[...a,...i],a},Vs=(e,t)=>{const a=e=>Object.keys(e.modes).filter(t=>e.modes[t].enabled);if(Ci(e)&&Object.keys(t).includes(String(e)))return a(t[e]);{const e=Object.keys(t).map(e=>a(t[e]));return e[0].filter(t=>e.every(e=>e.includes(t)))}},Hs=(e,t)=>e.map(e=>({value:e,name:e in t.states?t.states[e].attributes.friendly_name||Me(e):e,icon:e in t.states?t.states[e].attributes.icon||ze(Ce(e)):void 0,description:e})),Ys=e=>{let t=[];return"notify"in e.services&&(t=[...t,...Object.keys(e.services.notify).map(e=>"notify."+e)]),"tts"in e.services&&(t=[...t,...Object.keys(e.services.tts).filter(e=>"clear_cache"!=e).map(e=>"tts."+e)]),t},Bs=(...e)=>{if(!e.length||!e.every(e=>e.length))return[];if(1==e.length&&e[0].length>1&&Ei(e[0].map(Ce)).length>1)return Bs(...e[0].map(e=>Array(e)));let t=[...e[0]];return e.forEach(e=>{t=t.map(t=>e.includes(t)?t:"script"==Ce(t)&&e.map(Ce).includes("script")?"script.script":e.map(Me).includes(Me(t))?"homeassistant."+Me(t):null).filter(Ci)}),t},Ks=(e,t,a=1)=>{if(a>10)return[];if(Array.isArray(e)){const i=e.map(e=>Ks(e,t,a+1));return Bs(...i)}if(!Ci(e))return[];const i=Ce(e);switch(i){case"light":case"switch":case"input_boolean":case"siren":return[i+".turn_on",i+".turn_off"];case"script":return[e];case"lock":return["lock.lock","lock.unlock"];case"group":const s=e in t.states?t.states[e]:void 0,n=(null==s?void 0:s.attributes.entity_id)||[];return Ks(n,t,a+1);default:return[]}},Qs=(e,t)=>{let a=[...Object.keys(e.states).filter(t=>Ks(t,e).length)];return t&&t.length&&(a=[...a,...t.filter(e=>!a.includes(e))]),a.sort(Pi),a},Ws=e=>{let t=[...Object.keys(e.states).filter(e=>"media_player"==Ce(e))];return t.sort(Pi),t},Xs=e=>{let t=[{value:"{{arm_mode}}",name:e.translationMetadata.translations.en.nativeName}];return"en"!=e.language&&(t=[...t,{value:`{{arm_mode|lang=${e.language}}}`,name:e.translationMetadata.translations[e.language].nativeName}]),t},Zs=e=>"string"==typeof e&&e.trim().length,Js=(e,t)=>Zs(e)&&t.services[Ce(e)]&&t.services[Ce(e)][Me(e)],en=(e,t)=>Zs(e)&&t.states[e],tn=e=>"object"==typeof e&&null!==e&&!Array.isArray(e),an=e=>"string"==typeof e;let sn=class extends ne{constructor(){super(...arguments),this.items=[],this.value=[],this.label="",this.invalid=!1}shouldUpdate(e){return e.get("items")&&(Mi(this.items,e.get("items"))||this.firstUpdated()),!0}firstUpdated(){this.value.some(e=>!this.items.map(e=>e.value).includes(e))&&(this.value=this.value.filter(e=>this.items.map(e=>e.value).includes(e)),De(this,"value-changed",{value:this.value}))}render(){return q`
${this.value.length?this.value.map(e=>this.items.find(t=>t.value==e)).filter(Ci).map(e=>q`
${e.name} this._removeClick(e.value)}>
`):""}
!this.value.includes(e.value))} ?disabled=${this.value.length==this.items.length} label=${this.label} icons=${!0} @value-changed=${this._addClick} ?invalid=${this.invalid&&this.value.length!=this.items.length} > `}_removeClick(e){this.value=this.value.filter(t=>t!==e),De(this,"value-changed",{value:this.value})}_addClick(e){e.stopPropagation();const t=e.target,a=t.value;this.value.includes(a)||(this.value=[...this.value,a]),t.value="",De(this,"value-changed",{value:[...this.value]})}static get styles(){return o` div.chip-set { margin: 0px -4px; } div.chip { height: 32px; border-radius: 16px; border: 2px solid rgb(168, 232, 251); line-height: 1.25rem; font-size: 0.875rem; font-weight: 400; padding: 0px 12px; display: inline-flex; align-items: center; box-sizing: border-box; margin: 4px; } .icon { vertical-align: middle; outline: none; display: flex; align-items: center; border-radius: 50%; padding: 6px; color: rgba(0, 0, 0, 0.54); background: rgb(168, 232, 251); --mdc-icon-size: 20px; margin-left: -14px !important; } .label { margin: 0px 4px; } .button { cursor: pointer; background: var(--secondary-text-color); border-radius: 50%; --mdc-icon-size: 14px; color: var(--card-background-color); width: 16px; height: 16px; padding: 1px; box-sizing: border-box; display: inline-flex; align-items: center; margin-right: -6px !important; } `}};var nn;t([le()],sn.prototype,"hass",void 0),t([le()],sn.prototype,"items",void 0),t([le({type:Array})],sn.prototype,"value",void 0),t([le()],sn.prototype,"label",void 0),t([le({type:Boolean})],sn.prototype,"invalid",void 0),sn=t([re("alarmo-selector")],sn),function(e){e[e.Yaml=0]="Yaml",e[e.UI=1]="UI"}(nn||(nn={}));let rn=class extends ne{constructor(){super(...arguments),this.config={type:wi.Notification,triggers:[{}],actions:[{}]},this.viewMode=nn.UI,this.errors={}}async firstUpdated(){if(await Ue(),this.areas=await Ze(this.hass),this.alarmoConfig=await Ge(this.hass),this.item){let e=this.item.actions.map(e=>Si(e,"entity_id"));this.config=Object.assign(Object.assign({},this.item),{actions:[e[0],...e.slice(1)]}),this.config.triggers.length>1&&(this.config=Object.assign(Object.assign({},this.config),{triggers:[this.config.triggers[0]]}));let t=this.config.triggers[0].area;Ci(t)&&!Fs(this.areas,this.alarmoConfig).includes(t)?t=void 0:null===t&&(t=0),this._setArea(new CustomEvent("value-changed",{detail:{value:t}}))}if(!Ci(this.config.triggers[0].area)){const e=Fs(this.areas,this.alarmoConfig);1==e.length?this._setArea(new CustomEvent("value-changed",{detail:{value:e[0]}})):e.includes(0)&&this._setArea(new CustomEvent("value-changed",{detail:{value:0}}))}}render(){var e,t,a,i;return this.hass&&this.areas&&this.alarmoConfig?q`
${gi("panels.actions.cards.new_notification.title",this.hass.language)}
${gi("panels.actions.cards.new_notification.description",this.hass.language)}
${gi("panels.actions.cards.new_notification.trigger",this.hass.language)}
${gi("panels.actions.cards.new_notification.fields.event.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.event.description",this.hass.language)} Is(e,this.hass))} label=${gi("panels.actions.cards.new_action.fields.event.heading",this.hass.language)} icons=${!0} .value=${this.config.triggers[0].event} @value-changed=${this._setEvent} ?invalid=${this.errors.event} > ${Object.keys(this.areas).length>1?q` ${gi("panels.actions.cards.new_action.fields.area.heading",this.hass.language)} ${gi("panels.actions.cards.new_action.fields.area.description",this.hass.language)} Us(e,this.areas,this.alarmoConfig))} clearable=${!0} label=${gi("panels.actions.cards.new_action.fields.area.heading",this.hass.language)} .value=${this.config.triggers[0].area} @value-changed=${this._setArea} ?invalid=${this.errors.area||!this.config.triggers[0].area&&!this.alarmoConfig.master.enabled} > `:""} ${gi("panels.actions.cards.new_notification.fields.mode.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.mode.description",this.hass.language)} Rs(e,this.hass))} label=${gi("panels.actions.cards.new_action.fields.mode.heading",this.hass.language)} .value=${this.config.triggers[0].modes||[]} @value-changed=${this._setModes} ?invalid=${this.errors.modes} >
${gi("panels.actions.cards.new_notification.action",this.hass.language)}
${this.viewMode==nn.UI?q` ${gi("panels.actions.cards.new_notification.fields.target.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.target.description",this.hass.language)} ${this.config.actions[0].service&&"notify"!=Ce(this.config.actions[0].service)?"":q` ${gi("panels.actions.cards.new_notification.fields.title.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.title.description",this.hass.language)} `} ${this.config.actions[0].service&&"tts"==Ce(this.config.actions[0].service)?q` ${gi("panels.actions.cards.new_action.fields.entity.heading",this.hass.language)} ${gi("panels.actions.cards.new_action.fields.entity.description",this.hass.language)} `:""} ${gi("panels.actions.cards.new_notification.fields.message.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.message.description",this.hass.language)} this._setMessage(e.target.value)} ?invalid=${this.errors.message} > ${this.config.triggers[0].event?q`
${gi("panels.actions.cards.new_notification.fields.message.insert_wildcard",this.hass.language)}: {let a=[];return a=[],e&&![$i.Pending,$i.Triggered,$i.ArmFailure].includes(e)||(a=[...a,{name:"Open Sensors",value:"{{open_sensors}}"}]),e&&![$i.Armed].includes(e)||(a=[...a,{name:"Bypassed Sensors",value:"{{bypassed_sensors}}"}]),(!e||(null==t?void 0:t.code_arm_required)&&[$i.Armed,$i.Arming,$i.ArmFailure].includes(e)||(null==t?void 0:t.code_disarm_required)&&[$i.Disarmed,$i.Untriggered].includes(e))&&(a=[...a,{name:"Changed By",value:"{{changed_by}}"}]),e&&![$i.Armed,$i.Arming,$i.Pending,$i.Triggered,$i.ArmFailure].includes(e)||(a=[...a,{name:"Arm Mode",value:"{{arm_mode}}"}]),a})(this.config.triggers[0].event,this.alarmoConfig)} @value-changed=${e=>this._insertWildCard(e.detail)} >
`:""}
${null!==this._getOpenSensorsFormat()?q` ${gi("panels.actions.cards.new_notification.fields.open_sensors_format.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.open_sensors_format.description",this.hass.language)} {let t=[];return t="en"!=e.language?[...t,{value:"{{open_sensors}}",name:`${gi("panels.actions.cards.new_notification.fields.open_sensors_format.options.default",e.language)} (${e.translationMetadata.translations.en.nativeName})`},{value:`{{open_sensors|lang=${e.language}}}`,name:`${gi("panels.actions.cards.new_notification.fields.open_sensors_format.options.default",e.language)} (${e.translationMetadata.translations[e.language].nativeName})`}]:[...t,{value:"{{open_sensors}}",name:gi("panels.actions.cards.new_notification.fields.open_sensors_format.options.default",e.language)}],t=[...t,{value:"{{open_sensors|format=short}}",name:gi("panels.actions.cards.new_notification.fields.open_sensors_format.options.short",e.language)}],t})(this.hass)} .value=${this._getOpenSensorsFormat(!0)} @value-changed=${this._setOpenSensorsFormat} > `:""} ${null!==this._getArmModeFormat()&&(Xs(this.hass).length>1||1==Xs(this.hass).length&&Xs(this.hass)[0].value!=this._getArmModeFormat())?q` ${gi("panels.actions.cards.new_notification.fields.arm_mode_format.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.arm_mode_format.description",this.hass.language)} `:""} `:q`

${gi("components.editor.edit_in_yaml",this.hass.language)}

${this.errors.service||this.errors.title||this.errors.message?q` ${this.hass.localize("ui.errors.config.key_missing","key",Object.entries(this.errors).find(([e,t])=>t&&["service","title","message","entity"].includes(e))[0])} `:""} `}
${this.viewMode==nn.Yaml?gi("components.editor.ui_mode",this.hass.language):gi("components.editor.yaml_mode",this.hass.language)}
${gi("panels.actions.cards.new_notification.actions.test",this.hass.language)}
${gi("panels.actions.cards.new_notification.options",this.hass.language)}
${gi("panels.actions.cards.new_notification.fields.name.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.name.description",this.hass.language)} ${(null===(i=this.item)||void 0===i?void 0:i.automation_id)?q` ${gi("panels.actions.cards.new_notification.fields.delete.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.delete.description",this.hass.language)}
${this.hass.localize("ui.common.delete")}
`:""}
${this.hass.localize("ui.common.save")}
`:q``}_setEvent(e){e.stopPropagation();const t=e.detail.value;let a=this.config.triggers;Object.assign(a,{0:Object.assign(Object.assign({},a[0]),{event:t})}),this.config=Object.assign(Object.assign({},this.config),{triggers:a}),Object.keys(this.errors).includes("event")&&this._validateConfig()}_setArea(e){var t;e.stopPropagation();const a=e.detail.value;let i=this.config.triggers;Object.assign(i,{0:Object.assign(Object.assign({},i[0]),{area:a})});const s=Vs(a,this.areas);(null===(t=i[0].modes)||void 0===t?void 0:t.length)&&this._setModes(new CustomEvent("value-changed",{detail:{value:i[0].modes.filter(e=>s.includes(e))}})),this.config=Object.assign(Object.assign({},this.config),{triggers:i}),Object.keys(this.errors).includes("area")&&this._validateConfig()}_setModes(e){e.stopPropagation();const t=e.detail.value;let a=this.config.triggers;Object.assign(a,{0:Object.assign(Object.assign({},a[0]),{modes:t})}),this.config=Object.assign(Object.assign({},this.config),{triggers:a}),Object.keys(this.errors).includes("modes")&&this._validateConfig()}_setService(e){e.stopPropagation();const t=String(e.detail.value);let a=this.config.actions;Object.assign(a,{0:Object.assign(Object.assign(Object.assign({},a[0]),{service:t}),Si(a[0],"service"))}),(a[0].data||{}).entity_id&&"notify"==Ce(t)&&Object.assign(a,{0:Object.assign(Object.assign({},a[0]),{data:Si(a[0].data||{},"entity_id")})}),this.config=Object.assign(Object.assign({},this.config),{actions:a}),Object.keys(this.errors).includes("service")&&this._validateConfig()}_setTitle(e){e.stopPropagation();const t=e.target.value;let a=this.config.actions;Object.assign(a,{0:Object.assign(Object.assign({},a[0]),{service:a[0].service||"",data:Object.assign(Object.assign({},a[0].data||{}),{title:t})})}),this.config=Object.assign(Object.assign({},this.config),{actions:a}),Object.keys(this.errors).includes("title")&&this._validateConfig()}_setEntity(e){e.stopPropagation();const t=e.target.value;let a=this.config.actions;Object.assign(a,{0:Object.assign(Object.assign({},a[0]),{service:a[0].service||"",data:Object.assign(Object.assign({},a[0].data||{}),{entity_id:t})})}),this.config=Object.assign(Object.assign({},this.config),{actions:a}),Object.keys(this.errors).includes("entity")&&this._validateConfig()}_setMessage(e){let t=this.config.actions;Object.assign(t,{0:Object.assign(Object.assign({},t[0]),{service:t[0].service||"",data:Object.assign(Object.assign({},t[0].data||{}),{message:e})})}),this.config=Object.assign(Object.assign({},this.config),{actions:t}),Object.keys(this.errors).includes("message")&&this._validateConfig()}_setName(e){e.stopPropagation();const t=e.target.value;this.config=Object.assign(Object.assign({},this.config),{name:t})}_setYaml(e){const t=e.detail.value;let a={};an(null==t?void 0:t.service)&&(a=Object.assign(Object.assign({},a),{service:String(t.service)})),tn(null==t?void 0:t.data)&&(a=Object.assign(Object.assign({},a),{data:t.data})),Object.keys(a).length&&(this.config=Object.assign(Object.assign({},this.config),{actions:Object.assign(this.config.actions,{0:Object.assign(Object.assign({},this.config.actions[0]),a)})})),Object.keys(this.errors).some(e=>["service","message","title"].includes(e))&&this._validateConfig()}_validateConfig(){var e;this.errors={};const t=this._parseAutomation(),a=t.triggers[0];a.event&&Object.values($i).includes(a.event)||(this.errors=Object.assign(Object.assign({},this.errors),{event:!0})),Ci(a.area)&&Fs(this.areas,this.alarmoConfig).includes(a.area)||(this.errors=Object.assign(Object.assign({},this.errors),{area:!0})),(a.modes||[]).every(e=>Vs(a.area,this.areas).includes(e))||(this.errors=Object.assign(Object.assign({},this.errors),{modes:!0}));const i=t.actions[0];return!i.service||!Ys(this.hass).includes(i.service)&&"script"!=Ce(i.service)?this.errors=Object.assign(Object.assign({},this.errors),{service:!0}):!i.service||"tts"!=Ce(i.service)||Object.keys(i.data||{}).includes("entity_id")&&Ws(this.hass).includes(i.data.entity_id)||(this.errors=Object.assign(Object.assign({},this.errors),{entity:!0})),Zs(null===(e=i.data)||void 0===e?void 0:e.message)||(this.errors=Object.assign(Object.assign({},this.errors),{message:!0})),Zs(t.name)||(this.errors=Object.assign(Object.assign({},this.errors),{name:!0})),!Object.values(this.errors).length}_validAction(){var e;const t=this._parseAutomation().actions[0];return t.service&&("script"==Ce(t.service)||Ys(this.hass).includes(t.service))&&Zs(null===(e=t.data)||void 0===e?void 0:e.message)}_insertWildCard(e){var t;const a=this.shadowRoot.querySelector("#message");a&&a.focus();let i=(null===(t=this.config.actions[0].data)||void 0===t?void 0:t.message)||"";i=a&&null!==a.selectionStart&&null!==a.selectionEnd?i.substring(0,a.selectionStart)+e+i.substring(a.selectionEnd,i.length):i+e,this._setMessage(i)}_toggleYamlMode(){if(this.viewMode=this.viewMode==nn.UI?nn.Yaml:nn.UI,this.viewMode==nn.Yaml){let e=Object.assign({},this.config.actions[0]),t="object"==typeof e.data&&Ci(e.data)?e.data:{};e=Object.assign(Object.assign({},e),{service:e.service||""}),t.message||(t=Object.assign(Object.assign({},t),{message:""})),Ys(this.hass).includes(e.service)&&("notify"!=Ce(e.service)||t.title||(t=Object.assign(Object.assign({},t),{title:""})),"tts"!=Ce(e.service)||t.entity_id||(t=Object.assign(Object.assign({},t),{entity_id:""}))),e=Object.assign(Object.assign({},e),{data:t}),this.config=Object.assign(Object.assign({},this.config),{actions:Object.assign(this.config.actions,{0:e})})}}_namePlaceholder(){const e=this.config.triggers[0].event,t=this.config.actions[0].service?Ce(this.config.actions[0].service):null;if(!e)return"";if("notify"==t){const t=Gs(this.hass,this.config.actions[0].service);return t.length?gi("panels.actions.cards.new_notification.fields.name.placeholders."+e,this.hass.language,"{target}",t[0].name):""}if("tts"==t){const t="object"==typeof this.config.actions[0].data&&Ci(this.config.actions[0].data)?this.config.actions[0].data.entity_id:null;if(!t||!this.hass.states[t])return"";const a=Oi(this.hass.states[t]);return gi("panels.actions.cards.new_notification.fields.name.placeholders."+e,this.hass.language,"{target}",a)}return""}_messagePlaceholder(){const e=this.config.triggers[0].event;return e?gi("panels.actions.cards.new_notification.fields.message.placeholders."+e,this.hass.language):""}_parseAutomation(){var e;let t=Object.assign({},this.config),a=t.actions[0];return!Zs(null===(e=a.data)||void 0===e?void 0:e.message)&&this.viewMode==nn.UI&&this._messagePlaceholder()&&(a=Object.assign(Object.assign({},a),{data:Object.assign(Object.assign({},a.data),{message:this._messagePlaceholder()})}),Object.assign(t,{actions:Object.assign(t.actions,{0:a})})),!Zs(t.name)&&this._namePlaceholder()&&(t=Object.assign(Object.assign({},t),{name:this._namePlaceholder()})),t}_getOpenSensorsFormat(e=!1){var t;const a=((null===(t=this.config.actions[0].data)||void 0===t?void 0:t.message)||"").match(/{{open_sensors(\|[^}]+)?}}/);return null!==a?a[0]:e?"{{open_sensors}}":null}_setOpenSensorsFormat(e){var t;e.stopPropagation();const a=String(e.detail.value);let i=(null===(t=this.config.actions[0].data)||void 0===t?void 0:t.message)||"";i=i.replace(/{{open_sensors(\|[^}]+)?}}/,a);let s=this.config.actions;Object.assign(s,{0:Object.assign(Object.assign({},s[0]),{service:s[0].service||"",data:Object.assign(Object.assign({},s[0].data||{}),{message:i})})}),this.config=Object.assign(Object.assign({},this.config),{actions:s})}_getArmModeFormat(e=!1){var t;const a=((null===(t=this.config.actions[0].data)||void 0===t?void 0:t.message)||"").match(/{{arm_mode(\|[^}]+)?}}/);return null!==a?a[0]:e?"{{arm_mode}}":null}_setArmModeFormat(e){var t;e.stopPropagation();const a=String(e.detail.value);let i=(null===(t=this.config.actions[0].data)||void 0===t?void 0:t.message)||"";i=i.replace(/{{arm_mode(\|[^}]+)?}}/,a);let s=this.config.actions;Object.assign(s,{0:Object.assign(Object.assign({},s[0]),{service:s[0].service||"",data:Object.assign(Object.assign({},s[0].data||{}),{message:i})})}),this.config=Object.assign(Object.assign({},this.config),{actions:s})}_saveClick(e){if(!this._validateConfig())return;let t=this._parseAutomation();Vs(t.triggers[0].area,this.areas).every(e=>{var a;return null===(a=t.triggers[0].modes)||void 0===a?void 0:a.includes(e)})&&(t=Object.assign(Object.assign({},t),{triggers:Object.assign(t.triggers,{0:Object.assign(Object.assign({},t.triggers[0]),{modes:[]})})})),this.item&&(t=Object.assign(Object.assign({},t),{automation_id:this.item.automation_id})),We(this.hass,t).catch(t=>Di(t,e)).then(()=>this._cancelClick())}_deleteClick(e){var t;(null===(t=this.item)||void 0===t?void 0:t.automation_id)&&Xe(this.hass,this.item.automation_id).catch(t=>Di(t,e)).then(()=>this._cancelClick())}_testClick(e){const t=this._parseAutomation().actions[0],[a,i]=t.service.split(".");let s=t.data.message;s=s.replace("{{open_sensors|format=short}}","Some Example Sensor"),s=s.replace(/{{open_sensors(\|[^}]+)?}}/,"Some Example Sensor is open"),s=s.replace("{{bypassed_sensors}}","Some Bypassed Sensor"),s=s.replace(/{{arm_mode(\|[^}]+)?}}/,"Armed away"),s=s.replace("{{changed_by}}","Some Example User"),this.hass.callService(a,i,Object.assign(Object.assign({},t.data),{message:s})).then().catch(t=>{Ni(e,t.message)})}_cancelClick(){Pe(0,Ui("actions"),!0)}static get styles(){return o` div.content { padding: 28px 20px 0; max-width: 1040px; margin: 0 auto; display: flex; flex-direction: column; } div.header { font-size: 24px; font-weight: 400; letter-spacing: -0.012em; line-height: 32px; opacity: var(--dark-primary-opacity); } div.section-header { font-size: 18px; font-weight: 400; letter-spacing: -0.012em; line-height: 32px; opacity: var(--dark-primary-opacity); margin: 20px 0px 5px 10px; } div.actions { padding: 20px 0px 30px 0px; } mwc-button ha-icon { margin-right: 6px; --mdc-icon-size: 20px; } .toggle-button { position: absolute; right: 20px; top: 20px; } h2 { margin-top: 10px; font-size: 24px; font-weight: 400; letter-spacing: -0.012em; } span.error-message { color: var(--error-color); } mwc-button.warning { --mdc-theme-primary: var(--error-color); } mwc-button.save-button { --mdc-theme-primary: rgba(var(--rgb-primary-color), 0.8); } div.heading { display: grid; grid-template-areas: 'header icon' 'description icon'; grid-template-rows: 1fr 1fr; grid-template-columns: 1fr 48px; margin: 20px 0px 10px 10px; } div.heading .icon { grid-area: icon; } div.heading .header { grid-area: header; } div.heading .description { grid-area: description; } ha-textarea[invalid] { --mdc-text-field-idle-line-color: var(--mdc-theme-error); --mdc-text-field-label-ink-color: var(--mdc-theme-error); } `}};var on;t([le({attribute:!1})],rn.prototype,"hass",void 0),t([le()],rn.prototype,"narrow",void 0),t([le()],rn.prototype,"config",void 0),t([le()],rn.prototype,"item",void 0),t([le()],rn.prototype,"areas",void 0),t([le()],rn.prototype,"alarmoConfig",void 0),t([le()],rn.prototype,"viewMode",void 0),t([le()],rn.prototype,"errors",void 0),rn=t([re("notification-editor-card")],rn),function(e){e[e.Yaml=0]="Yaml",e[e.UI=1]="UI"}(on||(on={}));let ln=class extends ne{constructor(){super(...arguments),this.config={type:wi.Action,triggers:[{}],actions:[{}]},this.viewMode=on.UI,this.errors={}}async firstUpdated(){if(await Ue(),this.areas=await Ze(this.hass),this.alarmoConfig=await Ge(this.hass),this.item){let e=this.item.actions.map(e=>e.entity_id?e:Si(e,"entity_id"));this.config=Object.assign(Object.assign({},this.item),{actions:[e[0],...e.slice(1)]}),this.config.triggers.length>1&&(this.config=Object.assign(Object.assign({},this.config),{triggers:[this.config.triggers[0]]}));let t=this.config.triggers[0].area;Ci(t)&&!Fs(this.areas,this.alarmoConfig).includes(t)?t=void 0:null===t&&(t=0),this._setArea(new CustomEvent("value-changed",{detail:{value:t}})),this._hasCustomEntities()&&(this.viewMode=on.Yaml)}if(!Ci(this.config.triggers[0].area)){const e=Fs(this.areas,this.alarmoConfig);1==e.length?this._setArea(new CustomEvent("value-changed",{detail:{value:e[0]}})):e.includes(0)&&this._setArea(new CustomEvent("value-changed",{detail:{value:0}}))}!this.item||this.config.triggers[0].area||this.alarmoConfig.master.enabled||(this.errors=Object.assign(Object.assign({},this.errors),{area:!0}))}render(){var e;return this.hass&&this.areas&&this.alarmoConfig?q`
${gi("panels.actions.cards.new_action.title",this.hass.language)}
${gi("panels.actions.cards.new_action.description",this.hass.language)}
${gi("panels.actions.cards.new_notification.trigger",this.hass.language)}
${gi("panels.actions.cards.new_action.fields.event.heading",this.hass.language)} ${gi("panels.actions.cards.new_action.fields.event.description",this.hass.language)} Is(e,this.hass))} label=${gi("panels.actions.cards.new_action.fields.event.heading",this.hass.language)} icons=${!0} .value=${this.config.triggers[0].event} @value-changed=${this._setEvent} ?invalid=${this.errors.event} > ${Object.keys(this.areas).length>1?q` ${gi("panels.actions.cards.new_action.fields.area.heading",this.hass.language)} ${gi("panels.actions.cards.new_action.fields.area.description",this.hass.language)} Us(e,this.areas,this.alarmoConfig))} clearable=${!0} label=${gi("panels.actions.cards.new_action.fields.area.heading",this.hass.language)} .value=${this.config.triggers[0].area} @value-changed=${this._setArea} ?invalid=${this.errors.area} > `:""} ${gi("panels.actions.cards.new_notification.fields.mode.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.mode.description",this.hass.language)} Rs(e,this.hass))} label=${gi("panels.actions.cards.new_action.fields.mode.heading",this.hass.language)} .value=${this.config.triggers[0].modes||[]} @value-changed=${this._setModes} ?invalid=${this.errors.modes} >
${gi("panels.actions.cards.new_notification.action",this.hass.language)}
${this.viewMode==on.UI?q` ${gi("panels.actions.cards.new_action.fields.entity.heading",this.hass.language)} ${gi("panels.actions.cards.new_action.fields.entity.description",this.hass.language)} ${this._getEntities().length?q` ${gi("panels.actions.cards.new_action.fields.action.heading",this.hass.language)} ${gi("panels.actions.cards.new_action.fields.action.description",this.hass.language)}
${this.renderActions()||gi("panels.actions.cards.new_action.fields.action.no_common_actions",this.hass.language)}
${this.errors.service?q` ${this.hass.localize("ui.common.error_required",this.hass.language)} `:""}
`:""} `:q`

${gi("components.editor.edit_in_yaml",this.hass.language)}

${this.errors.service||this.errors.entity_id?q` ${this.hass.localize("ui.errors.config.key_missing","key",Object.entries(this.errors).find(([e,t])=>t&&["service","entity_id"].includes(e))[0])} `:""} `}
${this.viewMode==on.Yaml?gi("components.editor.ui_mode",this.hass.language):gi("components.editor.yaml_mode",this.hass.language)}
${gi("panels.actions.cards.new_notification.actions.test",this.hass.language)}
${gi("panels.actions.cards.new_notification.options",this.hass.language)}
${gi("panels.actions.cards.new_action.fields.name.heading",this.hass.language)} ${gi("panels.actions.cards.new_action.fields.name.description",this.hass.language)} ${(null===(e=this.item)||void 0===e?void 0:e.automation_id)?q` ${gi("panels.actions.cards.new_notification.fields.delete.heading",this.hass.language)} ${gi("panels.actions.cards.new_notification.fields.delete.description",this.hass.language)}
${this.hass.localize("ui.common.delete")}
`:""}
${this.hass.localize("ui.common.save")}
`:q``}renderActions(){let e=this.config.actions.map(e=>e.entity_id),t=Ks(e,this.hass);if(!t.length)return;return t.map(e=>q` this._setAction(e)} > ${((e,t)=>{let a=Me(e);switch("script"==Ce(e)&&(a="run"),a){case"turn_on":return t.localize("ui.card.media_player.turn_on");case"turn_off":return t.localize("ui.card.media_player.turn_off");case"lock":return t.localize("ui.card.lock.lock");case"unlock":return t.localize("ui.card.lock.unlock");case"run":return t.localize("ui.card.script.run");default:return a}})(e,this.hass)} `)}_selectedAction(){let e=this.config.actions.map(e=>e.service);return e.every(Ci)?(e=Ei(Bs(e.filter(Ci))),1==e.length?e[0]:null):null}_setEvent(e){e.stopPropagation();const t=e.detail.value;let a=this.config.triggers;Object.assign(a,{0:Object.assign(Object.assign({},a[0]),{event:t})}),this.config=Object.assign(Object.assign({},this.config),{triggers:a}),Object.keys(this.errors).includes("event")&&this._validateConfig()}_setArea(e){var t;e.stopPropagation();const a=e.detail.value;let i=this.config.triggers;Object.assign(i,{0:Object.assign(Object.assign({},i[0]),{area:a})});const s=Vs(a,this.areas);(null===(t=i[0].modes)||void 0===t?void 0:t.length)&&this._setModes(new CustomEvent("value-changed",{detail:{value:i[0].modes.filter(e=>s.includes(e))}})),this.config=Object.assign(Object.assign({},this.config),{triggers:i}),Object.keys(this.errors).includes("area")&&this._validateConfig()}_setModes(e){e.stopPropagation();const t=e.detail.value,a=this.config.triggers;Object.assign(a,{0:Object.assign(Object.assign({},a[0]),{modes:t})}),this.config=Object.assign(Object.assign({},this.config),{triggers:a}),Object.keys(this.errors).includes("service")&&this._validateConfig()}_setEntity(e){e.stopPropagation();const t=e.detail.value;let a=this.config.actions,i=null;if(t.length>a.length&&this._selectedAction()&&(i=this._selectedAction()),a.length>t.length){let e=a.findIndex(e=>!t.includes(e.entity_id||""));e<0&&(e=a.length-1),a.splice(e,1)}t.length||Object.assign(a,{0:Si(a[0],"entity_id")}),t.forEach((e,t)=>{let i=a.length>t?Object.assign({},a[t]):{};i=i.entity_id==e?Object.assign({},i):{entity_id:e},Object.assign(a,{[t]:i})}),this.config=Object.assign(Object.assign({},this.config),{actions:a}),i&&this._setAction(i),Object.keys(this.errors).includes("entity_id")&&this._validateConfig()}_setAction(e){let t=this.config.actions,a=this.config.actions.map(e=>e.entity_id);Ks(a,this.hass).length&&(t.forEach((a,i)=>{let s=Ks(a.entity_id,this.hass),n=(r=e,s.find(e=>e==r||"turn_on"==Me(r)&&"turn_on"==Me(e)||"turn_off"==Me(r)&&"turn_off"==Me(e)||"script"==Ce(r)&&"script"==Ce(e)));var r;Object.assign(t,{[i]:Object.assign({service:n},Si(a,"service"))})}),this.config=Object.assign(Object.assign({},this.config),{actions:t}),Object.keys(this.errors).includes("service")&&this._validateConfig())}_setName(e){e.stopPropagation();const t=e.target.value;this.config=Object.assign(Object.assign({},this.config),{name:t})}_setYaml(e){let t=e.detail.value,a=[{}];var i;tn(t)&&(t=[t]),"object"==typeof(i=t)&&null!==i&&Array.isArray(i)&&(t.forEach((e,t)=>{let i={};tn(e)&&an(e.service)&&(i=Object.assign(Object.assign({},i),{service:e.service})),tn(e)&&an(e.entity_id)&&(i=Object.assign(Object.assign({},i),{entity_id:e.entity_id})),tn(e)&&tn(e.data)&&(i=Object.assign(Object.assign({},i),{data:e.data})),Object.assign(a,{[t]:i})}),this.config=Object.assign(Object.assign({},this.config),{actions:a}))}_validateConfig(){this.errors={};const e=this._parseAutomation(),t=e.triggers[0];t.event&&Object.values($i).includes(t.event)||(this.errors=Object.assign(Object.assign({},this.errors),{event:!0})),Ci(t.area)&&Fs(this.areas,this.alarmoConfig).includes(t.area)||(this.errors=Object.assign(Object.assign({},this.errors),{area:!0})),(t.modes||[]).every(e=>Vs(t.area,this.areas).includes(e))||(this.errors=Object.assign(Object.assign({},this.errors),{modes:!0}));let a=e.actions.map(e=>e.entity_id);this.viewMode==on.Yaml&&(a=a.filter(Ci)),e.actions.length&&a.every(e=>en(e,this.hass))||(this.errors=Object.assign(Object.assign({},this.errors),{entity_id:!0}));const i=e.actions.map(e=>e.service).filter(Ci);if(!i.length||!i.every(e=>Js(e,this.hass))){this.errors=Object.assign(Object.assign({},this.errors),{service:!0}),!Ks(a,this.hass).length&&i.length&&(this.viewMode=on.Yaml)}return Zs(e.name)||(this.errors=Object.assign(Object.assign({},this.errors),{name:!0})),!Object.values(this.errors).length}_validAction(){const e=this._parseAutomation(),t=e.actions.map(e=>e.service);let a=e.actions.map(e=>e.entity_id);return this.viewMode==on.Yaml&&(a=a.filter(Ci)),t.length&&t.every(e=>Js(e,this.hass))&&a.every(e=>en(e,this.hass))}_toggleYamlMode(){this.viewMode=this.viewMode==on.UI?on.Yaml:on.UI,this.viewMode==on.Yaml&&(this.config=Object.assign(Object.assign({},this.config),{actions:Object.assign(this.config.actions,{0:Object.assign(Object.assign({},this.config.actions[0]),{service:this.config.actions[0].service||"",data:Object.assign({},this.config.actions[0].data||{})})})}))}_namePlaceholder(){var e,t,a,i;if(!this._validAction)return"";const s=this.config.triggers[0].event,n=this.config.actions.map(e=>e.entity_id).filter(Ci),r=Hs(n,this.hass).map(e=>e.name).join(", "),o=Ei(this.config.actions.map(e=>e.service).filter(Ci).map(e=>Me(e)));let l=void 0;return 1==o.length&&(null===(e=o[0])||void 0===e?void 0:e.includes("turn_on"))&&(l=this.hass.localize("state.default.on")),1==o.length&&(null===(t=o[0])||void 0===t?void 0:t.includes("turn_off"))&&(l=this.hass.localize("state.default.off")),1==o.length&&(null===(a=o[0])||void 0===a?void 0:a.includes("lock"))&&(l=this.hass.localize("component.lock.state._.locked")),1==o.length&&(null===(i=o[0])||void 0===i?void 0:i.includes("unlock"))&&(l=this.hass.localize("component.lock.state._.unlocked")),s&&r&&l?gi("panels.actions.cards.new_action.fields.name.placeholders."+s,this.hass.language,"entity",r,"state",l):""}_getEntities(){return Ei(this.config.actions.map(e=>e.entity_id).filter(Ci))||[]}_hasCustomEntities(){return this._getEntities().some(e=>!Qs(this.hass).includes(e))}_parseAutomation(){let e=Object.assign({},this.config);return!Zs(e.name)&&this._namePlaceholder()&&(e=Object.assign(Object.assign({},e),{name:this._namePlaceholder()})),e}_saveClick(e){if(!this._validateConfig())return;let t=this._parseAutomation();Vs(t.triggers[0].area,this.areas).every(e=>{var a;return null===(a=t.triggers[0].modes)||void 0===a?void 0:a.includes(e)})&&(t=Object.assign(Object.assign({},t),{triggers:Object.assign(t.triggers,{0:Object.assign(Object.assign({},t.triggers[0]),{modes:[]})})})),We(this.hass,t).catch(t=>Di(t,e)).then(()=>this._cancelClick())}_deleteClick(e){var t;(null===(t=this.item)||void 0===t?void 0:t.automation_id)&&Xe(this.hass,this.item.automation_id).catch(t=>Di(t,e)).then(()=>this._cancelClick())}_testClick(e){this._parseAutomation().actions.forEach(t=>{const[a,i]=t.service.split(".");let s=Object.assign({},t.data);t.entity_id&&(s=Object.assign(Object.assign({},s),{entity_id:t.entity_id})),this.hass.callService(a,i,s).then().catch(t=>{Ni(e,t.message)})})}_cancelClick(){Pe(0,Ui("actions"),!0)}static get styles(){return o` div.content { padding: 28px 20px 0; max-width: 1040px; margin: 0 auto; display: flex; flex-direction: column; } div.header { font-size: 24px; font-weight: 400; letter-spacing: -0.012em; line-height: 32px; opacity: var(--dark-primary-opacity); } div.section-header { font-size: 18px; font-weight: 400; letter-spacing: -0.012em; line-height: 32px; opacity: var(--dark-primary-opacity); margin: 20px 0px 5px 10px; } div.actions { padding: 20px 0px 30px 0px; } mwc-button ha-icon { margin-right: 6px; --mdc-icon-size: 20px; } .toggle-button { position: absolute; right: 20px; top: 20px; } h2 { margin-top: 10px; font-size: 24px; font-weight: 400; letter-spacing: -0.012em; } span.error-message { color: var(--error-color); font-size: 0.875rem; display: flex; margin-top: 10px; } mwc-button.warning { --mdc-theme-primary: var(--error-color); } mwc-button.save-button { --mdc-theme-primary: rgba(var(--rgb-primary-color), 0.8); } mwc-button.active { background: var(--primary-color); --mdc-theme-primary: var(--text-primary-color); border-radius: 4px; } mwc-button[disabled].active { background: var(--disabled-text-color); --mdc-button-disabled-ink-color: var(--text-primary-color); } div.heading { display: grid; grid-template-areas: 'header icon' 'description icon'; grid-template-rows: 1fr 1fr; grid-template-columns: 1fr 48px; margin: 20px 0px 10px 10px; } div.heading .icon { grid-area: icon; } div.heading .header { grid-area: header; } div.heading .description { grid-area: description; } `}};t([le({attribute:!1})],ln.prototype,"hass",void 0),t([le()],ln.prototype,"narrow",void 0),t([le()],ln.prototype,"config",void 0),t([le()],ln.prototype,"item",void 0),t([le()],ln.prototype,"areas",void 0),t([le()],ln.prototype,"alarmoConfig",void 0),t([le()],ln.prototype,"viewMode",void 0),t([le()],ln.prototype,"errors",void 0),ln=t([re("automation-editor-card")],ln);let dn=class extends(et(ne)){constructor(){super(...arguments),this.areas={},this.getAreaForAutomation=e=>{if(!this.config)return;const t=Fs(this.areas,this.config);let a=e.triggers[0].area;return Ci(a)&&t.includes(a)?a:void 0}}hassSubscribe(){return this._fetchData(),[this.hass.connection.subscribeMessage(()=>this._fetchData(),{type:"alarmo_config_updated"})]}async _fetchData(){if(!this.hass)return;const e=await He(this.hass);this.automations=Object.values(e),this.areas=await Ze(this.hass),this.config=await Ge(this.hass)}firstUpdated(){var e;this.path.filter&&(this.selectedArea=null===(e=this.path.filter)||void 0===e?void 0:e.area),(async()=>{await Ie()})()}render(){if(!this.hass||!this.automations||!this.config)return q``;if("new_notification"==this.path.subpage)return q` `;if(this.path.params.edit_notification){const e=this.automations.find(e=>e.automation_id==this.path.params.edit_notification&&e.type==wi.Notification);return q` `}if("new_action"==this.path.subpage)return q` `;if(this.path.params.edit_action){const e=this.automations.find(e=>e.automation_id==this.path.params.edit_action&&e.type==wi.Action);return q` `}{const e=()=>q` ${gi("panels.actions.cards.notifications.table.no_area_warning",this.hass.language)} `,t={type:{width:"40px",renderer:t=>"no_area"!=t.area||this.config.master.enabled?t.type==wi.Notification?q` `:q` `:q` ${e()} `},name:{title:this.hass.localize("ui.components.area-picker.add_dialog.name"),renderer:t=>q` ${"no_area"!=t.area||this.config.master.enabled?"":e()} ${t.name} `,width:"40%",grow:!0,text:!0},enabled:{title:gi("common.enabled",this.hass.language),width:"68px",align:"center",renderer:e=>q` {t.stopPropagation(),this.toggleEnable(t,e.automation_id)}} > `}},a=this.automations.filter(e=>e.type==wi.Notification).map(e=>Object(Object.assign(Object.assign({},e),{id:e.automation_id,warning:!this.config.master.enabled&&!this.getAreaForAutomation(e),area:this.getAreaForAutomation(e)||"no_area"}))),i=this.automations.filter(e=>e.type==wi.Action).map(e=>Object(Object.assign(Object.assign({},e),{id:e.automation_id,warning:!this.config.master.enabled&&!this.getAreaForAutomation(e),area:this.getAreaForAutomation(e)||"no_area"})));return q`
${gi("panels.actions.cards.notifications.description",this.hass.language)}
Pe(0,Ui("actions",{params:{edit_notification:e.detail.id}}),!0)} > ${gi("panels.actions.cards.notifications.table.no_items",this.hass.language)}
${gi("panels.actions.cards.notifications.actions.new_notification",this.hass.language)}
${gi("panels.actions.cards.actions.description",this.hass.language)}
Pe(0,Ui("actions",{params:{edit_action:e.detail.id}}),!0)} > ${gi("panels.actions.cards.actions.table.no_items",this.hass.language)}
${gi("panels.actions.cards.actions.actions.new_action",this.hass.language)}
`}}toggleEnable(e,t){We(this.hass,{automation_id:t,enabled:!e.target.checked}).catch(t=>Di(t,e)).then()}getTableFilterOptions(){if(!this.hass)return;let e=Object.values(this.areas).map(e=>Object({value:e.area_id,name:e.name,badge:t=>t.filter(t=>t.area==e.area_id).length})).sort(Pi);Object.values(this.automations||[]).filter(e=>!this.getAreaForAutomation(e)).length&&(e=[{value:"no_area",name:this.config.master.enabled?this.config.master.name:this.hass.localize("state_attributes.climate.preset_mode.none"),badge:e=>e.filter(e=>"no_area"==e.area).length},...e]);return{area:{name:gi("components.table.filter.item",this.hass.language,"name",gi("panels.actions.cards.new_action.fields.area.heading",this.hass.language)),items:e,value:this.selectedArea?[this.selectedArea]:[]}}}addNotificationClick(){Pe(0,Ui("actions","new_notification"),!0)}addActionClick(){Pe(0,Ui("actions","new_action"),!0)}};dn.styles=qi,t([le()],dn.prototype,"hass",void 0),t([le()],dn.prototype,"narrow",void 0),t([le()],dn.prototype,"path",void 0),t([le()],dn.prototype,"alarmEntity",void 0),t([le()],dn.prototype,"automations",void 0),t([le()],dn.prototype,"areas",void 0),t([le()],dn.prototype,"config",void 0),t([le()],dn.prototype,"selectedArea",void 0),dn=t([re("alarm-view-actions")],dn),e.MyAlarmPanel=class extends ne{async firstUpdated(){window.addEventListener("location-changed",()=>{this.requestUpdate()}),await Ie(),this.userConfig=await Ve(this.hass),this.requestUpdate()}render(){if(!customElements.get("ha-app-layout")||!this.userConfig)return q` loading... `;const e=Ii();return q`
${gi("title",this.hass.language)}
v${"1.9.5"}
${gi("panels.general.title",this.hass.language)} ${gi("panels.sensors.title",this.hass.language)} ${gi("panels.codes.title",this.hass.language)} ${gi("panels.actions.title",this.hass.language)}
${this.getView(e)}
`}getView(e){switch(e.page){case"general":return q` `;case"sensors":return q` `;case"codes":return q` `;case"actions":return q` `;default:return q`
The page you are trying to reach cannot be found. Please select a page from the menu above to continue.
`}}handlePageSelected(e){const t=e.detail.item.getAttribute("page-name");t!==Ii()?(Pe(0,Ui(t)),this.requestUpdate()):scrollTo(0,0)}static get styles(){return o` ${qi} :host { color: var(--primary-text-color); --paper-card-header-color: var(--primary-text-color); } app-header, app-toolbar { background-color: var(--app-header-background-color); font-weight: 400; color: var(--app-header-text-color, white); } app-toolbar { height: var(--header-height); } ha-app-layout { display: block; z-index: 2; } app-toolbar [main-title] { margin-left: 20px; } ha-tabs { margin-left: max(env(safe-area-inset-left), 24px); margin-right: max(env(safe-area-inset-right), 24px); --paper-tabs-selection-bar-color: var(--app-header-selection-bar-color, var(--app-header-text-color, #fff)); text-transform: uppercase; } .view { height: calc(100vh - 112px); display: flex; justify-content: center; } .view > * { width: 600px; max-width: 600px; } .view > *:last-child { margin-bottom: 20px; } .version { font-size: 14px; font-weight: 500; color: rgba(var(--rgb-text-primary-color), 0.9); } `}},t([le()],e.MyAlarmPanel.prototype,"hass",void 0),t([le({type:Boolean,reflect:!0})],e.MyAlarmPanel.prototype,"narrow",void 0),t([le()],e.MyAlarmPanel.prototype,"userConfig",void 0),e.MyAlarmPanel=t([re("alarm-panel")],e.MyAlarmPanel)}({}); ================================================ FILE: custom_components/alarmo/helpers.py ================================================ import logging from homeassistant.core import ( HomeAssistant, ) from . import const _LOGGER = logging.getLogger(__name__) def friendly_name_for_entity_id(entity_id: str, hass: HomeAssistant): """helper to get friendly name for entity""" state = hass.states.get(entity_id) if state and state.attributes["friendly_name"]: return state.attributes["friendly_name"] return entity_id def omit(obj: dict, blacklisted_keys: list): return { key: val for key, val in obj.items() if key not in blacklisted_keys } ================================================ FILE: custom_components/alarmo/manifest.json ================================================ { "domain": "alarmo", "name": "Alarmo", "documentation": "https://github.com/nielsfaber/alarmo", "issue_tracker": "https://github.com/nielsfaber/alarmo/issues", "version": "v1.9.5", "dependencies": [ "http", "panel_custom" ], "after_dependencies": [ "mqtt", "notify" ], "codeowners": [ "@nielsfaber" ], "requirements": [], "config_flow": true, "iot_class": "local_push" } ================================================ FILE: custom_components/alarmo/mqtt.py ================================================ import json import logging from homeassistant.core import ( HomeAssistant, callback, ) from homeassistant.components.mqtt import ( DOMAIN as ATTR_MQTT, CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, ) import homeassistant.components.mqtt as mqtt from homeassistant.helpers.json import JSONEncoder from homeassistant.util import slugify from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import const from .helpers import ( friendly_name_for_entity_id, ) _LOGGER = logging.getLogger(__name__) CONF_EVENT_TOPIC = "event_topic" class MqttHandler: def __init__(self, hass: HomeAssistant): self.hass = hass self._config = None self._subscribed_topics = [] self._subscriptions = [] async def async_update_config(_args=None): """mqtt config updated, reload the configuration.""" old_config = self._config new_config = self.hass.data[const.DOMAIN]["coordinator"].store.async_get_config() if old_config and old_config[ATTR_MQTT] == new_config[ATTR_MQTT]: # only update MQTT config if some parameters are changed return self._config = new_config if not old_config or old_config[ATTR_MQTT][CONF_COMMAND_TOPIC] != new_config[ATTR_MQTT][CONF_COMMAND_TOPIC]: # re-subscribing is only needed if the command topic has changed await self._async_subscribe_topics() _LOGGER.debug("MQTT config was (re)loaded") self._subscriptions.append( async_dispatcher_connect(hass, "alarmo_config_updated", async_update_config) ) self.hass.async_add_job(async_update_config) @callback def async_alarm_state_changed(area_id: str, old_state: str, new_state: str): if not self._config[ATTR_MQTT][const.ATTR_ENABLED]: return topic = self._config[ATTR_MQTT][CONF_STATE_TOPIC] if not topic: # do not publish if no topic is provided return if area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1: # handle the sending of a state update for a specific area area = self.hass.data[const.DOMAIN]["areas"][area_id] topic = topic.rsplit('/', 1) topic.insert(1, slugify(area.name)) topic = "/".join(topic) payload_config = self._config[ATTR_MQTT][const.ATTR_STATE_PAYLOAD] if new_state in payload_config and payload_config[new_state]: message = payload_config[new_state] else: message = new_state hass.async_create_task(mqtt.async_publish(self.hass, topic, message, retain=True)) _LOGGER.debug("Published state '{}' on topic '{}'".format(message, topic)) self._subscriptions.append( async_dispatcher_connect(self.hass, "alarmo_state_updated", async_alarm_state_changed) ) @callback def async_handle_event(event: str, area_id: str, args: dict = {}): if not self._config[ATTR_MQTT][const.ATTR_ENABLED]: return topic = self._config[ATTR_MQTT][CONF_EVENT_TOPIC] if not topic: # do not publish if no topic is provided return if area_id and len(self.hass.data[const.DOMAIN]["areas"]) > 1: # handle the sending of a state update for a specific area area = self.hass.data[const.DOMAIN]["areas"][area_id] topic = topic.rsplit('/', 1) topic.insert(1, slugify(area.name)) topic = "/".join(topic) if event == const.EVENT_ARM: payload = { "event": "{}_{}".format( event.upper(), args["arm_mode"].split("_", 1).pop(1).upper() ), "delay": args["delay"], } elif event == const.EVENT_TRIGGER: payload = { "event": event.upper(), "delay": args["delay"], "sensors": [ { "entity_id": entity, "name": friendly_name_for_entity_id(entity, self.hass), } for (entity, state) in args["open_sensors"].items() ] } elif event == const.EVENT_FAILED_TO_ARM: payload = { "event": event.upper(), "sensors": [ { "entity_id": entity, "name": friendly_name_for_entity_id(entity, self.hass), } for (entity, state) in args["open_sensors"].items() ] } elif event == const.EVENT_COMMAND_NOT_ALLOWED: payload = { "event": event.upper(), "state": args["state"], "command": args["command"].upper() } elif event in [const.EVENT_INVALID_CODE_PROVIDED, const.EVENT_NO_CODE_PROVIDED]: payload = { "event": event.upper() } else: return payload = json.dumps(payload, cls=JSONEncoder) hass.async_create_task(mqtt.async_publish(self.hass, topic, payload)) self._subscriptions.append( async_dispatcher_connect(self.hass, "alarmo_event", async_handle_event) ) def __del__(self): """prepare for removal""" while len(self._subscribed_topics): self._subscribed_topics.pop()() while len(self._subscriptions): self._subscriptions.pop()() async def _async_subscribe_topics(self): """install a listener for the command topic.""" if len(self._subscribed_topics): while len(self._subscribed_topics): self._subscribed_topics.pop()() _LOGGER.debug("Removed subscribed topics") if not self._config[ATTR_MQTT][const.ATTR_ENABLED]: return self._subscribed_topics.append( await mqtt.async_subscribe( self.hass, self._config[ATTR_MQTT][CONF_COMMAND_TOPIC], self.async_message_received, ) ) _LOGGER.debug("Subscribed to topic {}".format(self._config[ATTR_MQTT][CONF_COMMAND_TOPIC])) @callback async def async_message_received(self, msg): command = None code = None area = None try: payload = json.loads(msg.payload) payload = {k.lower(): v for k, v in payload.items()} if "command" in payload: command = payload["command"] elif "cmd" in payload: command = payload["cmd"] elif "action" in payload: command = payload["action"] elif "state" in payload: command = payload["state"] if "code" in payload: code = payload["code"] elif "pin" in payload: code = payload["pin"] elif "password" in payload: code = payload["password"] elif "pincode" in payload: code = payload["pincode"] if "area" in payload and payload["area"]: area = payload["area"] except ValueError: # no JSON structure found command = msg.payload code = None if type(command) is str: command = command.lower() else: _LOGGER.warning("Received unexpected command") return payload_config = self._config[ATTR_MQTT][const.ATTR_COMMAND_PAYLOAD] skip_code = not self._config[ATTR_MQTT][const.ATTR_REQUIRE_CODE] command_payloads = {} for item in const.COMMANDS: if item in payload_config and payload_config[item]: command_payloads[item] = payload_config[item].lower() elif item not in payload_config: command_payloads[item] = item.lower() if command not in list(command_payloads.values()): _LOGGER.warning("Received unexpected command: %s", command) return if area: res = list(filter(lambda el: slugify(el.name) == area, self.hass.data[const.DOMAIN]["areas"].values())) if not res: _LOGGER.warning("Area {} does not exist".format(area)) return entity = res[0] else: if self._config[const.ATTR_MASTER][const.ATTR_ENABLED] and len(self.hass.data[const.DOMAIN]["areas"]) > 1: entity = self.hass.data[const.DOMAIN]["master"] elif len(self.hass.data[const.DOMAIN]["areas"]) == 1: entity = list(self.hass.data[const.DOMAIN]["areas"].values())[0] else: _LOGGER.warning("No area specified") return _LOGGER.debug("Received command {}".format(command)) if command == command_payloads[const.COMMAND_DISARM]: await entity.async_alarm_disarm(code=code, skip_code=skip_code) elif command == command_payloads[const.COMMAND_ARM_AWAY]: await entity.async_alarm_arm_away(code, skip_code) elif command == command_payloads[const.COMMAND_ARM_NIGHT]: await entity.async_alarm_arm_night(code, skip_code) elif command == command_payloads[const.COMMAND_ARM_HOME]: await entity.async_alarm_arm_home(code, skip_code) elif command == command_payloads[const.COMMAND_ARM_CUSTOM_BYPASS]: await entity.async_alarm_arm_custom_bypass(code, skip_code) elif command == command_payloads[const.COMMAND_ARM_VACATION]: await entity.async_alarm_arm_vacation(code, skip_code) ================================================ FILE: custom_components/alarmo/panel.py ================================================ import os import logging from homeassistant.components import frontend from homeassistant.components import panel_custom from .const import ( CUSTOM_COMPONENTS, INTEGRATION_FOLDER, PANEL_FOLDER, PANEL_URL, PANEL_TITLE, PANEL_ICON, PANEL_NAME, PANEL_FILENAME, DOMAIN, ) _LOGGER = logging.getLogger(__name__) async def async_register_panel(hass): root_dir = os.path.join(hass.config.path(CUSTOM_COMPONENTS), INTEGRATION_FOLDER) panel_dir = os.path.join(root_dir, PANEL_FOLDER) view_url = os.path.join(panel_dir, PANEL_FILENAME) hass.http.register_static_path( PANEL_URL, view_url, cache_headers=False ) await panel_custom.async_register_panel( hass, webcomponent_name=PANEL_NAME, frontend_url_path=DOMAIN, module_url=PANEL_URL, sidebar_title=PANEL_TITLE, sidebar_icon=PANEL_ICON, require_admin=True, config={}, ) def async_unregister_panel(hass): frontend.async_remove_panel(hass, DOMAIN) _LOGGER.debug("Removing panel") ================================================ FILE: custom_components/alarmo/sensors.py ================================================ import logging import homeassistant.util.dt as dt_util from homeassistant.core import ( HomeAssistant, callback, CoreState, ) from homeassistant.helpers.event import ( async_track_state_change, async_track_point_in_time, ) from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, ) from homeassistant.const import ( EVENT_HOMEASSISTANT_STARTED, STATE_UNKNOWN, STATE_UNAVAILABLE, STATE_OPEN, STATE_CLOSED, STATE_ON, STATE_OFF, STATE_LOCKED, STATE_UNLOCKED, STATE_ALARM_PENDING, STATE_ALARM_ARMING, STATE_ALARM_TRIGGERED, ATTR_STATE, ATTR_LAST_TRIP_TIME, ATTR_NAME, ) from . import const ATTR_USE_EXIT_DELAY = "use_exit_delay" ATTR_USE_ENTRY_DELAY = "use_entry_delay" ATTR_ALWAYS_ON = "always_on" ATTR_ARM_ON_CLOSE = "arm_on_close" ATTR_ALLOW_OPEN = "allow_open" ATTR_TRIGGER_UNAVAILABLE = "trigger_unavailable" ATTR_AUTO_BYPASS = "auto_bypass" ATTR_AUTO_BYPASS_MODES = "auto_bypass_modes" ATTR_GROUP = "group" ATTR_GROUP_ID = "group_id" ATTR_TIMEOUT = "timeout" ATTR_EVENT_COUNT = "event_count" ATTR_ENTITIES = "entities" SENSOR_STATES_OPEN = [STATE_ON, STATE_OPEN, STATE_UNLOCKED] SENSOR_STATES_CLOSED = [STATE_OFF, STATE_CLOSED, STATE_LOCKED] SENSOR_TYPE_DOOR = "door" SENSOR_TYPE_WINDOW = "window" SENSOR_TYPE_MOTION = "motion" SENSOR_TYPE_TAMPER = "tamper" SENSOR_TYPE_ENVIRONMENTAL = "environmental" SENSOR_TYPE_OTHER = "other" SENSOR_TYPES = [ SENSOR_TYPE_DOOR, SENSOR_TYPE_WINDOW, SENSOR_TYPE_MOTION, SENSOR_TYPE_TAMPER, SENSOR_TYPE_ENVIRONMENTAL, SENSOR_TYPE_OTHER, ] _LOGGER = logging.getLogger(__name__) def parse_sensor_state(state): if not state or not state.state: return STATE_UNKNOWN elif state.state == STATE_UNAVAILABLE: return STATE_UNAVAILABLE elif state.state in SENSOR_STATES_OPEN: return STATE_OPEN elif state.state in SENSOR_STATES_CLOSED: return STATE_CLOSED else: return STATE_UNKNOWN def sensor_state_allowed(state, sensor_config, alarm_state): """return whether the sensor state is permitted or a state change should occur""" if state != STATE_OPEN and (state != STATE_UNAVAILABLE or not sensor_config[ATTR_TRIGGER_UNAVAILABLE]): # sensor has the safe state return True elif alarm_state == STATE_ALARM_TRIGGERED: # alarm is already triggered return True elif sensor_config[ATTR_ALWAYS_ON]: # alarm should always be triggered by always-on sensor return False elif alarm_state == STATE_ALARM_ARMING and not sensor_config[ATTR_USE_EXIT_DELAY]: # arming should be aborted if sensor without exit delay is active return False elif alarm_state in const.ARM_MODES: # normal triggering case return False elif alarm_state == STATE_ALARM_PENDING and not sensor_config[ATTR_USE_ENTRY_DELAY]: # triggering of immediate sensor while alarm is pending return False else: return True class SensorHandler: def __init__(self, hass: HomeAssistant): self._config = None self.hass = hass self._state_listener = None self._subscriptions = [] self._arm_timers = {} self._groups = {} self._group_events = {} self._startup_complete = False def async_update_sensor_config(): """sensor config updated, reload the configuration.""" self._config = self.hass.data[const.DOMAIN]["coordinator"].store.async_get_sensors() self._groups = self.hass.data[const.DOMAIN]["coordinator"].store.async_get_sensor_groups() self._group_events = {} self.async_watch_sensor_states() self._subscriptions.append( async_dispatcher_connect(hass, "alarmo_state_updated", self.async_watch_sensor_states) ) self._subscriptions.append( async_dispatcher_connect(hass, "alarmo_sensors_updated", async_update_sensor_config) ) async_update_sensor_config() def handle_startup(_event): self._startup_complete = True if hass.state == CoreState.running: self._startup_complete = True else: hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, handle_startup) def __del__(self): """prepare for removal""" if self._state_listener: self._state_listener() self._state_listener = None while len(self._subscriptions): self._subscriptions.pop()() def async_watch_sensor_states(self, area_id: str = None, old_state: str = None, state: str = None): """watch sensors based on the state of the alarm entities.""" sensors_list = [] for area in self.hass.data[const.DOMAIN]["areas"].keys(): sensors_list.extend(self.active_sensors_for_alarm_state(area)) if self._state_listener: self._state_listener() if len(sensors_list): self._state_listener = async_track_state_change( self.hass, sensors_list, self.async_sensor_state_changed ) else: self._state_listener = None # clear previous sensor group events which are not active for current alarm state for group_id in self._group_events.keys(): self._group_events[group_id] = dict(filter( lambda el: el[0] in sensors_list, self._group_events[group_id].items() )) # handle initial sensor states if area_id and old_state is None: sensors_list = self.active_sensors_for_alarm_state(area_id) for entity in sensors_list: sensor_state = parse_sensor_state(self.hass.states.get(entity)) if sensor_state != STATE_UNKNOWN: _LOGGER.debug("Initial state for {} is {}".format(entity, state)) def active_sensors_for_alarm_state(self, area_id: str, state: str = None): """Compose a list of sensors that are active for the state""" alarm_entity = self.hass.data[const.DOMAIN]["areas"][area_id] if not state: state = alarm_entity.arm_mode if alarm_entity.arm_mode else alarm_entity.state entities = [] for entity, config in self._config.items(): if config["area"] != area_id or not config["enabled"]: continue elif alarm_entity.bypassed_sensors and entity in alarm_entity.bypassed_sensors: continue elif ( state in config[const.ATTR_MODES] or config[ATTR_ALWAYS_ON] ): entities.append(entity) return entities def validate_arming_event(self, area_id: str, target_state: str = None, **kwargs): """check whether all sensors have the correct state prior to arming.""" use_delay = kwargs.get("use_delay", False) bypass_open_sensors = kwargs.get("bypass_open_sensors", False) sensors_list = self.active_sensors_for_alarm_state(area_id, target_state) open_sensors = {} bypassed_sensors = [] alarm_state = target_state if use_delay and alarm_state in const.ARM_MODES: alarm_state = STATE_ALARM_ARMING elif use_delay and alarm_state == STATE_ALARM_TRIGGERED: alarm_state = STATE_ALARM_PENDING for entity in sensors_list: sensor_config = self._config[entity] sensor_state = parse_sensor_state(self.hass.states.get(entity)) res = sensor_state_allowed(sensor_state, sensor_config, alarm_state) if not res and target_state in const.ARM_MODES: # sensor is active while arming if sensor_config[ATTR_ALLOW_OPEN]: # sensor is permitted to be open during/after arming continue elif bypass_open_sensors or ( sensor_config[ATTR_AUTO_BYPASS] and target_state in sensor_config[ATTR_AUTO_BYPASS_MODES] ): # sensor may be bypassed bypassed_sensors.append(entity) else: open_sensors[entity] = sensor_state return (open_sensors, bypassed_sensors) @callback async def async_sensor_state_changed(self, entity, old_state, new_state): """Callback fired when a sensor state has changed.""" old_state = parse_sensor_state(old_state) new_state = parse_sensor_state(new_state) if old_state == STATE_UNKNOWN: # sensor is unknown at startup, state which comes after is considered as initial state _LOGGER.debug("Initial state for {} is {}".format(entity, new_state)) return if old_state == new_state: # not a state change - ignore return _LOGGER.debug("entity {} changed: old_state={}, new_state={}".format(entity, old_state, new_state)) sensor_config = self._config[entity] alarm_entity = self.hass.data[const.DOMAIN]["areas"][sensor_config["area"]] alarm_state = alarm_entity.state res = sensor_state_allowed(new_state, sensor_config, alarm_state) if sensor_config[ATTR_ARM_ON_CLOSE] and alarm_state == STATE_ALARM_ARMING: # we are arming and sensor is configured to arm on closing if new_state == STATE_CLOSED: self.start_arm_timer(entity) else: self.stop_arm_timer(entity) if res: # nothing to do here, sensor state is OK return open_sensors = self.process_group_event(entity, new_state) if not open_sensors: # triggered sensor is part of a group and should be ignored return if sensor_config[ATTR_ALWAYS_ON]: # immediate trigger due to always on sensor _LOGGER.info("Alarm is triggered due to an always-on sensor: {}".format(entity)) await alarm_entity.async_trigger( skip_delay=True, open_sensors=open_sensors ) elif alarm_state == STATE_ALARM_ARMING: # sensor triggered while arming, abort arming _LOGGER.debug("Arming was aborted due to a sensor being active: {}".format(entity)) await alarm_entity.async_arm_failure(open_sensors) elif alarm_state in const.ARM_MODES: # standard alarm trigger _LOGGER.info("Alarm is triggered due to sensor: {}".format(entity)) await alarm_entity.async_trigger( skip_delay=(not sensor_config[ATTR_USE_ENTRY_DELAY]), open_sensors=open_sensors ) elif alarm_state == STATE_ALARM_PENDING: # immediate trigger while in pending state _LOGGER.info("Alarm is triggered due to sensor: {}".format(entity)) await alarm_entity.async_trigger( skip_delay=True, open_sensors=open_sensors ) def start_arm_timer(self, entity): """start timer for automatical arming""" @callback async def timer_finished(now): _LOGGER.debug("timer finished") sensor_config = self._config[entity] alarm_entity = self.hass.data[const.DOMAIN]["areas"][sensor_config["area"]] if alarm_entity.state == STATE_ALARM_ARMING: await alarm_entity.async_arm(alarm_entity.arm_mode, skip_delay=True) now = dt_util.utcnow() if entity in self._arm_timers: self.stop_arm_timer(entity) self._arm_timers[entity] = async_track_point_in_time( self.hass, timer_finished, now + const.SENSOR_ARM_TIME ) def stop_arm_timer(self, entity=None): """cancel timer(s) for automatical arming""" if entity and entity in self._arm_timers: self._arm_timers[entity]() elif not entity: for entity in self._arm_timers.keys(): self._arm_timers[entity]() def process_group_event(self, entity: str, state: str) -> dict: """check if sensor entity is member of a group and compare with previous events to evaluate trigger""" group_id = None for group in self._groups.values(): if entity in group[ATTR_ENTITIES]: group_id = group[ATTR_GROUP_ID] break open_sensors = { entity: state } if group_id is None: return open_sensors group = self._groups[group_id] group_events = self._group_events[group_id] if group_id in self._group_events.keys() else {} now = dt_util.now() group_events[entity] = { ATTR_STATE: state, ATTR_LAST_TRIP_TIME: now } self._group_events[group_id] = group_events recent_events = { entity: (now - event[ATTR_LAST_TRIP_TIME]).total_seconds() for (entity, event) in group_events.items() } recent_events = dict(filter(lambda el: el[1] <= group[ATTR_TIMEOUT], recent_events.items())) if len(recent_events.keys()) < group[ATTR_EVENT_COUNT]: _LOGGER.debug("tripped sensor {} was ignored since it belongs to group {}".format(entity, group[ATTR_NAME])) return {} else: for entity in recent_events.keys(): open_sensors[entity] = group_events[entity][ATTR_STATE] _LOGGER.debug("tripped sensor {} caused the triggering of group {}".format(entity, group[ATTR_NAME])) return open_sensors ================================================ FILE: custom_components/alarmo/services.yaml ================================================ arm: name: Arm description: "Arm an Alarmo entity with custom settings." fields: entity_id: name: Entity ID description: Name of entity that should be armed. example: "alarm_control_panel.alarm" required: true selector: entity: integration: alarmo domain: alarm_control_panel code: name: Code description: Code to arm the alarm with. example: "1234" required: false selector: text: mode: name: Mode description: "Mode to arm the alarm in." example: "away" required: false default: away selector: select: options: - away - night - home - vacation - custom skip_delay: name: Skip Delay description: "Skip the exit delay." example: true required: false default: false selector: boolean: force: name: Force description: "Automatically bypass all sensors that prevent the arming operation." example: true required: false default: false selector: boolean: disarm: name: Disarm description: "Disarm an Alarmo entity." fields: entity_id: name: Entity ID description: Name of entity that should be disarmed. example: "alarm_control_panel.alarm" required: true selector: entity: integration: alarmo domain: alarm_control_panel code: name: Code description: Code to disarm the alarm with. example: "1234" required: false selector: text: enable_user: name: Enable User description: "Allow a user to arm/disarm alarmo." fields: name: name: Name description: Name of the user to enable. example: "Frank" required: true selector: text: disable_user: name: Disable User description: "Block a user from arming/disarming alarmo." fields: name: name: Name description: Name of the user to disable. example: "Frank" required: true selector: text: ================================================ FILE: custom_components/alarmo/store.py ================================================ import logging import time import attr from collections import OrderedDict from typing import MutableMapping, cast from homeassistant.loader import bind_hass from homeassistant.core import (callback, HomeAssistant) from homeassistant.helpers.storage import Store from homeassistant.const import ( STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION ) from homeassistant.components.alarm_control_panel import ( FORMAT_NUMBER as CODE_FORMAT_NUMBER, ) from .const import DOMAIN from .sensors import ( SENSOR_TYPE_OTHER, ) from .helpers import omit _LOGGER = logging.getLogger(__name__) DATA_REGISTRY = f"{DOMAIN}_storage" STORAGE_KEY = f"{DOMAIN}.storage" STORAGE_VERSION = 6 SAVE_DELAY = 10 @attr.s(slots=True, frozen=True) class ModeEntry: """Mode storage Entry.""" enabled = attr.ib(type=bool, default=False) exit_time = attr.ib(type=int, default=0) entry_time = attr.ib(type=int, default=0) trigger_time = attr.ib(type=int, default=0) @attr.s(slots=True, frozen=True) class MqttConfig: """MQTT storage Entry.""" enabled = attr.ib(type=bool, default=False) state_topic = attr.ib(type=str, default="alarmo/state") state_payload = attr.ib(type=dict, default={}) command_topic = attr.ib(type=str, default="alarmo/command") command_payload = attr.ib(type=dict, default={}) require_code = attr.ib(type=bool, default=True) event_topic = attr.ib(type=str, default="alarmo/event") @attr.s(slots=True, frozen=True) class MasterConfig: """Master storage Entry.""" enabled = attr.ib(type=bool, default=True) name = attr.ib(type=str, default="master") @attr.s(slots=True, frozen=True) class AreaEntry: """Area storage Entry.""" area_id = attr.ib(type=str, default=None) name = attr.ib(type=str, default=None) modes = attr.ib(type=[str, ModeEntry], default={ STATE_ALARM_ARMED_AWAY: ModeEntry(), STATE_ALARM_ARMED_HOME: ModeEntry(), STATE_ALARM_ARMED_NIGHT: ModeEntry(), STATE_ALARM_ARMED_CUSTOM_BYPASS: ModeEntry(), STATE_ALARM_ARMED_VACATION: ModeEntry() }) @attr.s(slots=True, frozen=True) class Config: """(General) Config storage Entry.""" code_arm_required = attr.ib(type=bool, default=False) code_disarm_required = attr.ib(type=bool, default=False) code_format = attr.ib(type=str, default=CODE_FORMAT_NUMBER) disarm_after_trigger = attr.ib(type=bool, default=False) master = attr.ib(type=MasterConfig, default=MasterConfig()) mqtt = attr.ib(type=MqttConfig, default=MqttConfig()) @attr.s(slots=True, frozen=True) class SensorEntry: """Sensor storage Entry.""" entity_id = attr.ib(type=str, default=None) type = attr.ib(type=str, default=SENSOR_TYPE_OTHER) modes = attr.ib(type=list, default=[]) use_exit_delay = attr.ib(type=bool, default=True) use_entry_delay = attr.ib(type=bool, default=True) always_on = attr.ib(type=bool, default=False) arm_on_close = attr.ib(type=bool, default=False) allow_open = attr.ib(type=bool, default=False) trigger_unavailable = attr.ib(type=bool, default=False) auto_bypass = attr.ib(type=bool, default=False) auto_bypass_modes = attr.ib(type=list, default=[]) area = attr.ib(type=str, default=None) enabled = attr.ib(type=bool, default=True) @attr.s(slots=True, frozen=True) class UserEntry: """User storage Entry.""" user_id = attr.ib(type=str, default=None) name = attr.ib(type=str, default="") enabled = attr.ib(type=bool, default=True) code = attr.ib(type=str, default="") can_arm = attr.ib(type=bool, default=False) can_disarm = attr.ib(type=bool, default=False) is_override_code = attr.ib(type=bool, default=False) code_format = attr.ib(type=str, default="") code_length = attr.ib(type=int, default=0) area_limit = attr.ib(type=list, default=[]) @attr.s(slots=True, frozen=True) class AlarmoTriggerEntry: """Trigger storage Entry.""" event = attr.ib(type=str, default="") area = attr.ib(type=str, default=None) modes = attr.ib(type=list, default=[]) @attr.s(slots=True, frozen=True) class EntityTriggerEntry: """Trigger storage Entry.""" entity_id = attr.ib(type=str, default=None) state = attr.ib(type=str, default=None) @attr.s(slots=True, frozen=True) class ActionEntry: """Action storage Entry.""" service = attr.ib(type=str, default="") entity_id = attr.ib(type=str, default=None) data = attr.ib(type=dict, default={}) @attr.s(slots=True, frozen=True) class AutomationEntry: """Automation storage Entry.""" automation_id = attr.ib(type=str, default=None) type = attr.ib(type=str, default=None) name = attr.ib(type=str, default="") triggers = attr.ib(type=[AlarmoTriggerEntry], default=[]) actions = attr.ib(type=[ActionEntry], default=[]) enabled = attr.ib(type=bool, default=True) @attr.s(slots=True, frozen=True) class SensorGroupEntry: """Sensor group storage Entry.""" group_id = attr.ib(type=str, default=None) name = attr.ib(type=str, default="") entities = attr.ib(type=list, default=[]) timeout = attr.ib(type=int, default=0) event_count = attr.ib(type=int, default=2) def parse_automation_entry(data: dict): def create_trigger_entity(config: dict): if "event" in config: return AlarmoTriggerEntry(**config) else: return EntityTriggerEntry(**config) output = {} if "triggers" in data: output["triggers"] = list(map(create_trigger_entity, data["triggers"])) if "actions" in data: output["actions"] = list(map(lambda el: ActionEntry(**el), data["actions"])) if "automation_id" in data: output["automation_id"] = data["automation_id"] if "name" in data: output["name"] = data["name"] if "type" in data: output["type"] = data["type"] if "enabled" in data: output["enabled"] = data["enabled"] return output class MigratableStore(Store): async def _async_migrate_func(self, old_version, data: dict): if old_version == 1: area_id = str(int(time.time())) data["areas"] = [ attr.asdict(AreaEntry(**{ "name": "Alarmo", "modes": { mode: attr.asdict(ModeEntry( enabled=bool(config["enabled"]), exit_time=int(config["leave_time"]), entry_time=int(config["entry_time"]), trigger_time=int(data["config"]["trigger_time"]) )) for (mode, config) in data["config"]["modes"].items() } }, area_id=area_id)) ] if "sensors" in data: for sensor in data["sensors"]: sensor["area"] = area_id if old_version <= 2: data["automations"] = [ attr.asdict(AutomationEntry( **parse_automation_entry({ **automation, **{ "triggers": list(map( lambda el: attr.asdict(AlarmoTriggerEntry( event=el["state"] if "state" in el else el["event"], area=automation["area"] if "area" in el else None, modes=automation["modes"] )), automation["triggers"] )), "type": "notification" if "is_notification" in automation and automation["is_notification"] else "action" } }) )) for automation in data["automations"] ] if old_version <= 3: data["sensors"] = [ attr.asdict(SensorEntry( **{ **omit(sensor, ["immediate", "name"]), "use_exit_delay": not sensor["immediate"] and not sensor["always_on"], "use_entry_delay": not sensor["immediate"] and not sensor["always_on"], "auto_bypass_modes": sensor["modes"] if "auto_bypass" in sensor and sensor["auto_bypass"] else [], } )) for sensor in data["sensors"] ] if old_version <= 4: data["sensors"] = [ attr.asdict(SensorEntry( **omit(sensor, ["name"]), )) for sensor in data["sensors"] ] if old_version <= 5: data["automations"] = [ attr.asdict(AutomationEntry( **parse_automation_entry({ **automation, **{ "actions": list(map( lambda el: attr.asdict(ActionEntry( service=el["service"], entity_id=el["entity_id"], data=el["service_data"] )), automation["actions"] )) } }) )) for automation in data["automations"] ] return data class AlarmoStorage: """Class to hold alarmo configuration data.""" def __init__(self, hass: HomeAssistant) -> None: """Initialize the storage.""" self.hass = hass self.config: Config = Config() self.areas: MutableMapping[str, AreaEntry] = {} self.sensors: MutableMapping[str, SensorEntry] = {} self.users: MutableMapping[str, UserEntry] = {} self.automations: MutableMapping[str, AutomationEntry] = {} self.sensor_groups: MutableMapping[str, SensorGroupEntry] = {} self._store = MigratableStore(hass, STORAGE_VERSION, STORAGE_KEY) async def async_load(self) -> None: """Load the registry of schedule entries.""" data = await self._store.async_load() config: Config = Config() areas: "OrderedDict[str, AreaEntry]" = OrderedDict() sensors: "OrderedDict[str, SensorEntry]" = OrderedDict() users: "OrderedDict[str, UserEntry]" = OrderedDict() automations: "OrderedDict[str, AutomationEntry]" = OrderedDict() sensor_groups: "OrderedDict[str, SensorGroupEntry]" = OrderedDict() if data is not None: config = Config( code_arm_required=data["config"]["code_arm_required"], code_disarm_required=data["config"]["code_disarm_required"], code_format=data["config"]["code_format"], disarm_after_trigger=data["config"]["disarm_after_trigger"] ) if "mqtt" in data["config"]: config = attr.evolve(config, **{ "mqtt": MqttConfig(**data["config"]["mqtt"]), }) if "master" in data["config"]: config = attr.evolve(config, **{ "master": MasterConfig(**data["config"]["master"]), }) if "areas" in data: for area in data["areas"]: modes = { mode: ModeEntry( enabled=config["enabled"], exit_time=config["exit_time"], entry_time=config["entry_time"], trigger_time=config["trigger_time"] ) for (mode, config) in area["modes"].items() } areas[area["area_id"]] = AreaEntry( area_id=area["area_id"], name=area["name"], modes=modes ) if "sensors" in data: for sensor in data["sensors"]: sensors[sensor["entity_id"]] = SensorEntry(**sensor) if "users" in data: for user in data["users"]: users[user["user_id"]] = UserEntry(**omit(user, ["is_admin"])) if "automations" in data: for automation in data["automations"]: automations[automation["automation_id"]] = AutomationEntry(**parse_automation_entry(automation)) if "sensor_groups" in data: for group in data["sensor_groups"]: sensor_groups[group["group_id"]] = SensorGroupEntry(**group) self.config = config self.areas = areas self.sensors = sensors self.automations = automations self.users = users self.sensor_groups = sensor_groups if not areas: await self.async_factory_default() async def async_factory_default(self): self.async_create_area({ "name": "Alarmo", "modes": { STATE_ALARM_ARMED_AWAY: attr.asdict( ModeEntry( enabled=True, exit_time=60, entry_time=60, trigger_time=1800 ) ), STATE_ALARM_ARMED_HOME: attr.asdict( ModeEntry( enabled=True, trigger_time=1800 ) ) } }) @callback def async_schedule_save(self) -> None: """Schedule saving the registry of alarmo.""" self._store.async_delay_save(self._data_to_save, SAVE_DELAY) async def async_save(self) -> None: """Save the registry of alarmo.""" await self._store.async_save(self._data_to_save()) @callback def _data_to_save(self) -> dict: """Return data for the registry for alarmo to store in a file.""" store_data = { "config": attr.asdict(self.config), } store_data["areas"] = [ attr.asdict(entry) for entry in self.areas.values() ] store_data["sensors"] = [ attr.asdict(entry) for entry in self.sensors.values() ] store_data["users"] = [ attr.asdict(entry) for entry in self.users.values() ] store_data["automations"] = [ attr.asdict(entry) for entry in self.automations.values() ] store_data["sensor_groups"] = [ attr.asdict(entry) for entry in self.sensor_groups.values() ] return store_data async def async_delete(self): """Delete config.""" _LOGGER.warning("Removing alarmo configuration data!") await self._store.async_remove() self.config = Config() self.areas = {} self.sensors = {} self.users = {} self.automations = {} self.sensor_groups = {} await self.async_factory_default() @callback def async_get_config(self): return attr.asdict(self.config) @callback def async_update_config(self, changes: dict): """Update existing config.""" old = self.config new = self.config = attr.evolve(old, **changes) self.async_schedule_save() return attr.asdict(new) @callback def async_update_mode_config(self, mode: str, changes: dict): """Update existing config.""" modes = self.config.modes old = ( self.config.modes[mode] if mode in self.config.modes else ModeEntry() ) new = attr.evolve(old, **changes) modes[mode] = new self.config = attr.evolve(self.config, **{"modes": modes}) self.async_schedule_save() return new @callback def async_get_area(self, area_id) -> AreaEntry: """Get an existing AreaEntry by id.""" res = self.areas.get(area_id) return attr.asdict(res) if res else None @callback def async_get_areas(self): """Get an existing AreaEntry by id.""" res = {} for (key, val) in self.areas.items(): res[key] = attr.asdict(val) return res @callback def async_create_area(self, data: dict) -> AreaEntry: """Create a new AreaEntry.""" area_id = str(int(time.time())) new_area = AreaEntry(**data, area_id=area_id) self.areas[area_id] = new_area self.async_schedule_save() return attr.asdict(new_area) @callback def async_delete_area(self, area_id: str) -> None: """Delete AreaEntry.""" if area_id in self.areas: del self.areas[area_id] self.async_schedule_save() return True return False @callback def async_update_area(self, area_id: str, changes: dict) -> AreaEntry: """Update existing self.""" old = self.areas[area_id] new = self.areas[area_id] = attr.evolve(old, **changes) self.async_schedule_save() return attr.asdict(new) @callback def async_get_sensor(self, entity_id) -> SensorEntry: """Get an existing SensorEntry by id.""" res = self.sensors.get(entity_id) return attr.asdict(res) if res else None @callback def async_get_sensors(self): """Get an existing SensorEntry by id.""" res = {} for (key, val) in self.sensors.items(): res[key] = attr.asdict(val) return res @callback def async_create_sensor(self, entity_id: str, data: dict) -> SensorEntry: """Create a new SensorEntry.""" if entity_id in self.sensors: return False new_sensor = SensorEntry(**data, entity_id=entity_id) self.sensors[entity_id] = new_sensor self.async_schedule_save() return new_sensor @callback def async_delete_sensor(self, entity_id: str) -> None: """Delete SensorEntry.""" if entity_id in self.sensors: del self.sensors[entity_id] self.async_schedule_save() return True return False @callback def async_update_sensor(self, entity_id: str, changes: dict) -> SensorEntry: """Update existing SensorEntry.""" old = self.sensors[entity_id] new = self.sensors[entity_id] = attr.evolve(old, **changes) self.async_schedule_save() return new @callback def async_get_user(self, user_id) -> UserEntry: """Get an existing UserEntry by id.""" res = self.users.get(user_id) return attr.asdict(res) if res else None @callback def async_get_users(self): """Get an existing UserEntry by id.""" res = {} for (key, val) in self.users.items(): res[key] = attr.asdict(val) return res @callback def async_create_user(self, data: dict) -> UserEntry: """Create a new UserEntry.""" user_id = str(int(time.time())) new_user = UserEntry(**data, user_id=user_id) self.users[user_id] = new_user self.async_schedule_save() return new_user @callback def async_delete_user(self, user_id: str) -> None: """Delete UserEntry.""" if user_id in self.users: del self.users[user_id] self.async_schedule_save() return True return False @callback def async_update_user(self, user_id: str, changes: dict) -> UserEntry: """Update existing UserEntry.""" old = self.users[user_id] new = self.users[user_id] = attr.evolve(old, **changes) self.async_schedule_save() return new @callback def async_get_automations(self): """Get an existing AutomationEntry by id.""" res = {} for (key, val) in self.automations.items(): res[key] = attr.asdict(val) return res @callback def async_create_automation(self, data: dict) -> AutomationEntry: """Create a new AutomationEntry.""" automation_id = str(int(time.time())) new_automation = AutomationEntry(**parse_automation_entry(data), automation_id=automation_id) self.automations[automation_id] = new_automation self.async_schedule_save() return new_automation @callback def async_delete_automation(self, automation_id: str) -> None: """Delete AutomationEntry.""" if automation_id in self.automations: del self.automations[automation_id] self.async_schedule_save() return True return False @callback def async_update_automation(self, automation_id: str, changes: dict) -> AutomationEntry: """Update existing AutomationEntry.""" old = self.automations[automation_id] new = self.automations[automation_id] = attr.evolve(old, **parse_automation_entry(changes)) self.async_schedule_save() return new @callback def async_get_sensor_group(self, group_id) -> SensorGroupEntry: """Get an existing SensorGroupEntry by id.""" res = self.sensor_groups.get(group_id) return attr.asdict(res) if res else None @callback def async_get_sensor_groups(self): """Get an existing SensorGroupEntry by id.""" res = {} for (key, val) in self.sensor_groups.items(): res[key] = attr.asdict(val) return res @callback def async_create_sensor_group(self, data: dict) -> SensorGroupEntry: """Create a new SensorGroupEntry.""" group_id = str(int(time.time())) new_group = SensorGroupEntry(**data, group_id=group_id) self.sensor_groups[group_id] = new_group self.async_schedule_save() return group_id @callback def async_delete_sensor_group(self, group_id: str) -> None: """Delete SensorGroupEntry.""" if group_id in self.sensor_groups: del self.sensor_groups[group_id] self.async_schedule_save() return True return False @callback def async_update_sensor_group(self, group_id: str, changes: dict) -> SensorGroupEntry: """Update existing SensorGroupEntry.""" old = self.sensor_groups[group_id] new = self.sensor_groups[group_id] = attr.evolve(old, **changes) self.async_schedule_save() return new @bind_hass async def async_get_registry(hass: HomeAssistant) -> AlarmoStorage: """Return alarmo storage instance.""" task = hass.data.get(DATA_REGISTRY) if task is None: async def _load_reg() -> AlarmoStorage: registry = AlarmoStorage(hass) await registry.async_load() return registry task = hass.data[DATA_REGISTRY] = hass.async_create_task(_load_reg()) return cast(AlarmoStorage, await task) ================================================ FILE: custom_components/alarmo/websockets.py ================================================ import voluptuous as vol import logging from homeassistant.components import websocket_api from homeassistant.core import callback from homeassistant.components.http.data_validator import RequestDataValidator from homeassistant.helpers import config_validation as cv from homeassistant.components.http import HomeAssistantView from homeassistant.const import ( ATTR_ENTITY_ID, ATTR_CODE_FORMAT, ATTR_NAME, ATTR_CODE, ATTR_SERVICE, CONF_SERVICE_DATA, ATTR_STATE, STATE_ALARM_ARMED_AWAY, STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_ARMED_CUSTOM_BYPASS, STATE_ALARM_ARMED_VACATION, STATE_ALARM_DISARMED, STATE_ALARM_TRIGGERED, STATE_ALARM_PENDING, STATE_ALARM_DISARMING, STATE_ALARM_ARMING, ) from homeassistant.components.alarm_control_panel import ( CodeFormat, ATTR_CODE_ARM_REQUIRED, ) from homeassistant.components.websocket_api import (decorators, async_register_command) from homeassistant.helpers.dispatcher import ( async_dispatcher_connect, async_dispatcher_send, ) from . import const from homeassistant.components.mqtt import ( DOMAIN as ATTR_MQTT, CONF_STATE_TOPIC, CONF_COMMAND_TOPIC, ) import homeassistant.util.dt as dt_util from .mqtt import ( CONF_EVENT_TOPIC, ) from .sensors import ( ATTR_USE_EXIT_DELAY, ATTR_USE_ENTRY_DELAY, ATTR_ALWAYS_ON, ATTR_ARM_ON_CLOSE, ATTR_ALLOW_OPEN, ATTR_TRIGGER_UNAVAILABLE, ATTR_AUTO_BYPASS, ATTR_AUTO_BYPASS_MODES, ATTR_GROUP, ATTR_GROUP_ID, ATTR_TIMEOUT, ATTR_EVENT_COUNT, ATTR_ENTITIES, SENSOR_TYPES, ) _LOGGER = logging.getLogger(__name__) @callback @decorators.websocket_command({ vol.Required("type"): "alarmo_config_updated", }) @decorators.async_response async def handle_subscribe_updates(hass, connection, msg): """Handle subscribe updates.""" @callback def async_handle_event(): """Forward events to websocket.""" connection.send_message({ "id": msg["id"], "type": "event", }) connection.subscriptions[msg["id"]] = async_dispatcher_connect( hass, "alarmo_update_frontend", async_handle_event ) connection.send_result(msg["id"]) class AlarmoConfigView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/alarmo/config" name = "api:alarmo:config" @RequestDataValidator( vol.Schema( { vol.Optional(ATTR_CODE_ARM_REQUIRED): cv.boolean, vol.Optional(const.ATTR_CODE_DISARM_REQUIRED): cv.boolean, vol.Optional(ATTR_CODE_FORMAT): vol.In( [CodeFormat.NUMBER, CodeFormat.TEXT] ), vol.Optional(const.ATTR_TRIGGER_TIME): cv.positive_int, vol.Optional(const.ATTR_DISARM_AFTER_TRIGGER): cv.boolean, vol.Optional(ATTR_MQTT): vol.Schema({ vol.Required(const.ATTR_ENABLED): cv.boolean, vol.Required(CONF_STATE_TOPIC): cv.string, vol.Optional(const.ATTR_STATE_PAYLOAD): vol.Schema({ vol.Optional(STATE_ALARM_DISARMED): cv.string, vol.Optional(STATE_ALARM_ARMED_HOME): cv.string, vol.Optional(STATE_ALARM_ARMED_AWAY): cv.string, vol.Optional(STATE_ALARM_ARMED_NIGHT): cv.string, vol.Optional(STATE_ALARM_ARMED_CUSTOM_BYPASS): cv.string, vol.Optional(STATE_ALARM_ARMED_VACATION): cv.string, vol.Optional(STATE_ALARM_PENDING): cv.string, vol.Optional(STATE_ALARM_ARMING): cv.string, vol.Optional(STATE_ALARM_DISARMING): cv.string, vol.Optional(STATE_ALARM_TRIGGERED): cv.string }), vol.Required(CONF_COMMAND_TOPIC): cv.string, vol.Optional(const.ATTR_COMMAND_PAYLOAD): vol.Schema({ vol.Optional(const.COMMAND_ARM_AWAY): cv.string, vol.Optional(const.COMMAND_ARM_HOME): cv.string, vol.Optional(const.COMMAND_ARM_NIGHT): cv.string, vol.Optional(const.COMMAND_ARM_CUSTOM_BYPASS): cv.string, vol.Optional(const.COMMAND_ARM_VACATION): cv.string, vol.Optional(const.COMMAND_DISARM): cv.string, }), vol.Required(const.ATTR_REQUIRE_CODE): cv.boolean, vol.Required(CONF_EVENT_TOPIC): cv.string, }), vol.Optional(const.ATTR_MASTER): vol.Schema({ vol.Required(const.ATTR_ENABLED): cv.boolean, vol.Optional(ATTR_NAME): cv.string, }) } ) ) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] await coordinator.async_update_config(data) async_dispatcher_send(hass, "alarmo_update_frontend") return self.json({"success": True}) class AlarmoAreaView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/alarmo/area" name = "api:alarmo:area" mode_schema = vol.Schema({ vol.Required(const.ATTR_ENABLED): cv.boolean, vol.Required(const.ATTR_EXIT_TIME): cv.positive_int, vol.Required(const.ATTR_ENTRY_TIME): cv.positive_int, vol.Optional(const.ATTR_TRIGGER_TIME): cv.positive_int, }) @RequestDataValidator( vol.Schema( { vol.Optional("area_id"): cv.string, vol.Optional(ATTR_NAME): cv.string, vol.Optional(const.ATTR_REMOVE): cv.boolean, vol.Optional(const.ATTR_MODES): vol.Schema({ vol.Optional(STATE_ALARM_ARMED_AWAY): mode_schema, vol.Optional(STATE_ALARM_ARMED_HOME): mode_schema, vol.Optional(STATE_ALARM_ARMED_NIGHT): mode_schema, vol.Optional(STATE_ALARM_ARMED_CUSTOM_BYPASS): mode_schema, vol.Optional(STATE_ALARM_ARMED_VACATION): mode_schema }) } ) ) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] if "area_id" in data: area = data["area_id"] del data["area_id"] else: area = None await coordinator.async_update_area_config(area, data) async_dispatcher_send(hass, "alarmo_update_frontend") return self.json({"success": True}) class AlarmoSensorView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/alarmo/sensors" name = "api:alarmo:sensors" @RequestDataValidator( vol.Schema( { vol.Required(ATTR_ENTITY_ID): cv.entity_id, vol.Optional(const.ATTR_REMOVE): cv.boolean, vol.Optional(const.ATTR_TYPE): vol.In(SENSOR_TYPES), vol.Optional(const.ATTR_MODES): vol.All( cv.ensure_list, [vol.In(const.ARM_MODES)] ), vol.Optional(ATTR_USE_EXIT_DELAY): cv.boolean, vol.Optional(ATTR_USE_ENTRY_DELAY): cv.boolean, vol.Optional(ATTR_ARM_ON_CLOSE): cv.boolean, vol.Optional(ATTR_ALLOW_OPEN): cv.boolean, vol.Optional(ATTR_ALWAYS_ON): cv.boolean, vol.Optional(ATTR_TRIGGER_UNAVAILABLE): cv.boolean, vol.Optional(ATTR_AUTO_BYPASS): cv.boolean, vol.Optional(ATTR_AUTO_BYPASS_MODES): vol.All( cv.ensure_list, [vol.In(const.ARM_MODES)] ), vol.Optional(const.ATTR_AREA): cv.string, vol.Optional(const.ATTR_ENABLED): cv.boolean, vol.Optional(ATTR_GROUP): vol.Any( cv.string, None ) } ) ) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] entity = data[ATTR_ENTITY_ID] del data[ATTR_ENTITY_ID] coordinator.async_update_sensor_config(entity, data) async_dispatcher_send(hass, "alarmo_update_frontend") return self.json({"success": True}) class AlarmoUserView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/alarmo/users" name = "api:alarmo:users" @RequestDataValidator( vol.Schema( { vol.Optional(const.ATTR_USER_ID): cv.string, vol.Optional(const.ATTR_REMOVE): cv.boolean, vol.Optional(ATTR_NAME): cv.string, vol.Optional(const.ATTR_ENABLED): cv.boolean, vol.Optional(ATTR_CODE): cv.string, vol.Optional(const.ATTR_OLD_CODE): cv.string, vol.Optional(const.ATTR_CAN_ARM): cv.boolean, vol.Optional(const.ATTR_CAN_DISARM): cv.boolean, vol.Optional(const.ATTR_IS_OVERRIDE_CODE): cv.boolean, vol.Optional(const.ATTR_AREA_LIMIT): vol.All( cv.ensure_list, [cv.string] ) } ) ) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] user_id = None if const.ATTR_USER_ID in data: user_id = data[const.ATTR_USER_ID] del data[const.ATTR_USER_ID] coordinator.async_update_user_config(user_id, data) async_dispatcher_send(hass, "alarmo_update_frontend") return self.json({"success": True}) class AlarmoAutomationView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/alarmo/automations" name = "api:alarmo:automations" @RequestDataValidator( vol.Schema( { vol.Optional(const.ATTR_AUTOMATION_ID): cv.string, vol.Optional(ATTR_NAME): cv.string, vol.Optional(const.ATTR_TYPE): cv.string, vol.Optional(const.ATTR_TRIGGERS): vol.All( cv.ensure_list, [vol.Any( vol.Schema( { vol.Required(const.ATTR_EVENT): cv.string, vol.Optional(const.ATTR_AREA): vol.Any( int, cv.string, ), vol.Optional(const.ATTR_MODES): vol.All( cv.ensure_list, [vol.In(const.ARM_MODES)] ), } ), vol.Schema( { vol.Required(ATTR_ENTITY_ID): cv.string, vol.Required(ATTR_STATE): cv.string, } ) )] ), vol.Optional(const.ATTR_ACTIONS): vol.All( cv.ensure_list, [vol.Schema( { vol.Optional(ATTR_ENTITY_ID): cv.string, vol.Required(ATTR_SERVICE): cv.string, vol.Optional(CONF_SERVICE_DATA): dict, } )] ), vol.Optional(const.ATTR_ENABLED): cv.boolean, vol.Optional(const.ATTR_REMOVE): cv.boolean, } ) ) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] automation_id = None if const.ATTR_AUTOMATION_ID in data: automation_id = data[const.ATTR_AUTOMATION_ID] del data[const.ATTR_AUTOMATION_ID] coordinator.async_update_automation_config(automation_id, data) async_dispatcher_send(hass, "alarmo_update_frontend") return self.json({"success": True}) class AlarmoSensorGroupView(HomeAssistantView): """Login to Home Assistant cloud.""" url = "/api/alarmo/sensor_groups" name = "api:alarmo:sensor_groups" @RequestDataValidator( vol.Schema( { vol.Optional(ATTR_GROUP_ID): cv.string, vol.Optional(ATTR_NAME): cv.string, vol.Optional(ATTR_ENTITIES): vol.All( cv.ensure_list, vol.Unique(), [cv.string] ), vol.Optional(ATTR_TIMEOUT): cv.positive_int, vol.Optional(ATTR_EVENT_COUNT): cv.positive_int, vol.Optional(const.ATTR_REMOVE): cv.boolean, } ) ) async def post(self, request, data): """Handle config update request.""" hass = request.app["hass"] coordinator = hass.data[const.DOMAIN]["coordinator"] group_id = None if ATTR_GROUP_ID in data: group_id = data[ATTR_GROUP_ID] del data[ATTR_GROUP_ID] coordinator.async_update_sensor_group_config(group_id, data) async_dispatcher_send(hass, "alarmo_update_frontend") return self.json({"success": True}) @callback def websocket_get_config(hass, connection, msg): """Publish config data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] config = coordinator.store.async_get_config() connection.send_result(msg["id"], config) @callback def websocket_get_areas(hass, connection, msg): """Publish area data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] areas = coordinator.store.async_get_areas() connection.send_result(msg["id"], areas) @callback def websocket_get_sensors(hass, connection, msg): """Publish sensor data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] sensors = coordinator.store.async_get_sensors() for entity_id in sensors.keys(): group = coordinator.async_get_group_for_sensor(entity_id) sensors[entity_id]["group"] = group connection.send_result(msg["id"], sensors) @callback def websocket_get_users(hass, connection, msg): """Publish user data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] users = coordinator.store.async_get_users() connection.send_result(msg["id"], users) @callback def websocket_get_automations(hass, connection, msg): """Publish automations data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] automations = coordinator.store.async_get_automations() connection.send_result(msg["id"], automations) @callback def websocket_get_alarm_entities(hass, connection, msg): """Publish alarm entity data.""" result = [ { "entity_id": entity.entity_id, "area_id": area_id } for (area_id, entity) in hass.data[const.DOMAIN]["areas"].items() ] if hass.data[const.DOMAIN]["master"]: result.append({ "entity_id": hass.data[const.DOMAIN]["master"].entity_id, "area_id": 0 }) connection.send_result(msg["id"], result) @callback def websocket_get_sensor_groups(hass, connection, msg): """Publish sensor_group data.""" coordinator = hass.data[const.DOMAIN]["coordinator"] groups = coordinator.store.async_get_sensor_groups() connection.send_result(msg["id"], groups) @callback def websocket_get_countdown(hass, connection, msg): """Publish countdown time for alarm entity.""" entity_id = msg["entity_id"] item = next((entity for entity in hass.data[const.DOMAIN]["areas"].values() if entity.entity_id == entity_id), None) if hass.data[const.DOMAIN]["master"] and not item and hass.data[const.DOMAIN]["master"].entity_id == entity_id: item = hass.data[const.DOMAIN]["master"] data = { "delay": item.delay if item else 0, "remaining": round((item.expiration - dt_util.utcnow()).total_seconds(),2) if item and item.expiration else 0 } connection.send_result(msg["id"], data) async def async_register_websockets(hass): hass.http.register_view(AlarmoConfigView) hass.http.register_view(AlarmoSensorView) hass.http.register_view(AlarmoUserView) hass.http.register_view(AlarmoAutomationView) hass.http.register_view(AlarmoAreaView) hass.http.register_view(AlarmoSensorGroupView) async_register_command( hass, handle_subscribe_updates ) async_register_command( hass, "alarmo/config", websocket_get_config, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): "alarmo/config"} ), ) async_register_command( hass, "alarmo/areas", websocket_get_areas, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): "alarmo/areas"} ), ) async_register_command( hass, "alarmo/sensors", websocket_get_sensors, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): "alarmo/sensors"} ), ) async_register_command( hass, "alarmo/users", websocket_get_users, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): "alarmo/users"} ), ) async_register_command( hass, "alarmo/automations", websocket_get_automations, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): "alarmo/automations"} ), ) async_register_command( hass, "alarmo/entities", websocket_get_alarm_entities, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): "alarmo/entities"} ), ) async_register_command( hass, "alarmo/sensor_groups", websocket_get_sensor_groups, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( {vol.Required("type"): "alarmo/sensor_groups"} ), ) async_register_command( hass, "alarmo/countdown", websocket_get_countdown, websocket_api.BASE_COMMAND_MESSAGE_SCHEMA.extend( { vol.Required("type"): "alarmo/countdown", vol.Required("entity_id"): cv.entity_id } ), ) ================================================ FILE: custom_components/hacs/__init__.py ================================================ """ HACS gives you a powerful UI to handle downloads of all your custom needs. For more details about this integration, please refer to the documentation at https://hacs.xyz/ """ from __future__ import annotations import os from typing import Any from aiogithubapi import AIOGitHubAPIException, GitHub, GitHubAPI from aiogithubapi.const import ACCEPT_HEADERS from awesomeversion import AwesomeVersion from homeassistant.components.lovelace.system_health import system_health_info from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.const import Platform, __version__ as HAVERSION from homeassistant.core import HomeAssistant from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.discovery import async_load_platform from homeassistant.helpers.event import async_call_later from homeassistant.helpers.start import async_at_start from homeassistant.loader import async_get_integration import voluptuous as vol from .base import HacsBase from .const import DOMAIN, MINIMUM_HA_VERSION, STARTUP from .enums import ConfigurationType, HacsDisabledReason, HacsStage, LovelaceMode from .frontend import async_register_frontend from .utils.configuration_schema import hacs_config_combined from .utils.data import HacsData from .utils.platform_setup import async_setup_entity_platforms from .utils.queue_manager import QueueManager from .utils.version import version_left_higher_or_equal_then_right from .websocket import async_register_websocket_commands CONFIG_SCHEMA = vol.Schema({DOMAIN: hacs_config_combined()}, extra=vol.ALLOW_EXTRA) async def async_initialize_integration( hass: HomeAssistant, *, config_entry: ConfigEntry | None = None, config: dict[str, Any] | None = None, ) -> bool: """Initialize the integration""" hass.data[DOMAIN] = hacs = HacsBase() hacs.enable_hacs() if config is not None: if DOMAIN not in config: return True if hacs.configuration.config_type == ConfigurationType.CONFIG_ENTRY: return True hacs.configuration.update_from_dict( { "config_type": ConfigurationType.YAML, **config[DOMAIN], "config": config[DOMAIN], } ) if config_entry is not None: if config_entry.source == SOURCE_IMPORT: hass.async_create_task(hass.config_entries.async_remove(config_entry.entry_id)) return False hacs.configuration.update_from_dict( { "config_entry": config_entry, "config_type": ConfigurationType.CONFIG_ENTRY, **config_entry.data, **config_entry.options, } ) integration = await async_get_integration(hass, DOMAIN) hacs.set_stage(None) hacs.log.info(STARTUP, integration.version) clientsession = async_get_clientsession(hass) hacs.integration = integration hacs.version = integration.version hacs.configuration.dev = integration.version == "0.0.0" hacs.hass = hass hacs.queue = QueueManager(hass=hass) hacs.data = HacsData(hacs=hacs) hacs.system.running = True hacs.session = clientsession hacs.core.lovelace_mode = LovelaceMode.YAML try: lovelace_info = await system_health_info(hacs.hass) hacs.core.lovelace_mode = LovelaceMode(lovelace_info.get("mode", "yaml")) except BaseException: # lgtm [py/catch-base-exception] pylint: disable=broad-except # If this happens, the users YAML is not valid, we assume YAML mode pass hacs.log.debug("Configuration type: %s", hacs.configuration.config_type) hacs.core.config_path = hacs.hass.config.path() if hacs.core.ha_version is None: hacs.core.ha_version = AwesomeVersion(HAVERSION) ## Legacy GitHub client hacs.github = GitHub( hacs.configuration.token, clientsession, headers={ "User-Agent": f"HACS/{hacs.version}", "Accept": ACCEPT_HEADERS["preview"], }, ) ## New GitHub client hacs.githubapi = GitHubAPI( token=hacs.configuration.token, session=clientsession, **{"client_name": f"HACS/{hacs.version}"}, ) async def async_startup(): """HACS startup tasks.""" hacs.enable_hacs() for location in ( hass.config.path("custom_components/custom_updater.py"), hass.config.path("custom_components/custom_updater/__init__.py"), ): if os.path.exists(location): hacs.log.critical( "This cannot be used with custom_updater. " "To use this you need to remove custom_updater form %s", location, ) hacs.disable_hacs(HacsDisabledReason.CONSTRAINS) return False if not version_left_higher_or_equal_then_right( hacs.core.ha_version.string, MINIMUM_HA_VERSION, ): hacs.log.critical( "You need HA version %s or newer to use this integration.", MINIMUM_HA_VERSION, ) hacs.disable_hacs(HacsDisabledReason.CONSTRAINS) return False if not await hacs.data.restore(): hacs.disable_hacs(HacsDisabledReason.RESTORE) return False can_update = await hacs.async_can_update() hacs.log.debug("Can update %s repositories", can_update) hacs.set_active_categories() async_register_websocket_commands(hass) async_register_frontend(hass, hacs) if hacs.configuration.config_type == ConfigurationType.YAML: hass.async_create_task( async_load_platform(hass, Platform.SENSOR, DOMAIN, {}, hacs.configuration.config) ) hacs.log.info("Update entities are only supported when using UI configuration") else: await async_setup_entity_platforms( hacs, hass, config_entry, [Platform.SENSOR, Platform.UPDATE] if hacs.configuration.experimental else [Platform.SENSOR], ) hacs.set_stage(HacsStage.SETUP) if hacs.system.disabled: return False # Schedule startup tasks async_at_start(hass=hass, at_start_cb=hacs.startup_tasks) hacs.set_stage(HacsStage.WAITING) hacs.log.info("Setup complete, waiting for Home Assistant before startup tasks starts") return not hacs.system.disabled async def async_try_startup(_=None): """Startup wrapper for yaml config.""" try: startup_result = await async_startup() except AIOGitHubAPIException: startup_result = False if not startup_result: if ( hacs.configuration.config_type == ConfigurationType.YAML or hacs.system.disabled_reason != HacsDisabledReason.INVALID_TOKEN ): hacs.log.info("Could not setup HACS, trying again in 15 min") async_call_later(hass, 900, async_try_startup) return hacs.enable_hacs() await async_try_startup() # Mischief managed! return True async def async_setup(hass: HomeAssistant, config: dict[str, Any]) -> bool: """Set up this integration using yaml.""" return await async_initialize_integration(hass=hass, config=config) async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Set up this integration using UI.""" config_entry.async_on_unload(config_entry.add_update_listener(async_reload_entry)) setup_result = await async_initialize_integration(hass=hass, config_entry=config_entry) hacs: HacsBase = hass.data[DOMAIN] return setup_result and not hacs.system.disabled async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Handle removal of an entry.""" hacs: HacsBase = hass.data[DOMAIN] # Clear out pending queue hacs.queue.clear() for task in hacs.recuring_tasks: # Cancel all pending tasks task() # Store data await hacs.data.async_write(force=True) try: if hass.data.get("frontend_panels", {}).get("hacs"): hacs.log.info("Removing sidepanel") hass.components.frontend.async_remove_panel("hacs") except AttributeError: pass platforms = ["sensor"] if hacs.configuration.experimental: platforms.append("update") unload_ok = await hass.config_entries.async_unload_platforms(config_entry, platforms) hacs.set_stage(None) hacs.disable_hacs(HacsDisabledReason.REMOVED) hass.data.pop(DOMAIN, None) return unload_ok async def async_reload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None: """Reload the HACS config entry.""" await async_unload_entry(hass, config_entry) await async_setup_entry(hass, config_entry) ================================================ FILE: custom_components/hacs/base.py ================================================ """Base HACS class.""" from __future__ import annotations import asyncio from dataclasses import asdict, dataclass, field from datetime import timedelta import gzip import logging import math import os import pathlib import shutil from typing import TYPE_CHECKING, Any, Awaitable, Callable from aiogithubapi import ( AIOGitHubAPIException, GitHub, GitHubAPI, GitHubAuthenticationException, GitHubException, GitHubNotModifiedException, GitHubRatelimitException, ) from aiogithubapi.objects.repository import AIOGitHubAPIRepository from aiohttp.client import ClientSession, ClientTimeout from awesomeversion import AwesomeVersion from homeassistant.config_entries import ConfigEntry, ConfigEntryState from homeassistant.const import EVENT_HOMEASSISTANT_FINAL_WRITE, Platform from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.loader import Integration from homeassistant.util import dt from .const import TV from .enums import ( ConfigurationType, HacsCategory, HacsDisabledReason, HacsDispatchEvent, HacsGitHubRepo, HacsStage, LovelaceMode, ) from .exceptions import ( AddonRepositoryException, HacsException, HacsExecutionStillInProgress, HacsExpectedException, HacsRepositoryArchivedException, HacsRepositoryExistException, HomeAssistantCoreRepositoryException, ) from .repositories import RERPOSITORY_CLASSES from .utils.decode import decode_content from .utils.json import json_loads from .utils.logger import LOGGER from .utils.platform_setup import async_setup_entity_platforms from .utils.queue_manager import QueueManager from .utils.store import async_load_from_store, async_save_to_store if TYPE_CHECKING: from .repositories.base import HacsRepository from .utils.data import HacsData from .validate.manager import ValidationManager @dataclass class RemovedRepository: """Removed repository.""" repository: str | None = None reason: str | None = None link: str | None = None removal_type: str = None # archived, not_compliant, critical, dev, broken acknowledged: bool = False def update_data(self, data: dict): """Update data of the repository.""" for key in data: if data[key] is None: continue if key in ( "reason", "link", "removal_type", "acknowledged", ): self.__setattr__(key, data[key]) def to_json(self): """Return a JSON representation of the data.""" return { "repository": self.repository, "reason": self.reason, "link": self.link, "removal_type": self.removal_type, "acknowledged": self.acknowledged, } @dataclass class HacsConfiguration: """HacsConfiguration class.""" appdaemon_path: str = "appdaemon/apps/" appdaemon: bool = False config: dict[str, Any] = field(default_factory=dict) config_entry: ConfigEntry | None = None config_type: ConfigurationType | None = None country: str = "ALL" debug: bool = False dev: bool = False experimental: bool = False frontend_repo_url: str = "" frontend_repo: str = "" netdaemon_path: str = "netdaemon/apps/" netdaemon: bool = False plugin_path: str = "www/community/" python_script_path: str = "python_scripts/" python_script: bool = False release_limit: int = 5 sidepanel_icon: str = "hacs:hacs" sidepanel_title: str = "HACS" theme_path: str = "themes/" theme: bool = False token: str = None def to_json(self) -> str: """Return a json string.""" return asdict(self) def update_from_dict(self, data: dict) -> None: """Set attributes from dicts.""" if not isinstance(data, dict): raise HacsException("Configuration is not valid.") for key in data: self.__setattr__(key, data[key]) @dataclass class HacsCore: """HACS Core info.""" config_path: pathlib.Path | None = None ha_version: AwesomeVersion | None = None lovelace_mode = LovelaceMode("yaml") @dataclass class HacsCommon: """Common for HACS.""" categories: set[str] = field(default_factory=set) renamed_repositories: dict[str, str] = field(default_factory=dict) archived_repositories: list[str] = field(default_factory=list) ignored_repositories: list[str] = field(default_factory=list) skip: list[str] = field(default_factory=list) @dataclass class HacsStatus: """HacsStatus.""" startup: bool = True new: bool = False @dataclass class HacsSystem: """HACS System info.""" disabled_reason: HacsDisabledReason | None = None running: bool = False stage = HacsStage.SETUP action: bool = False @property def disabled(self) -> bool: """Return if HACS is disabled.""" return self.disabled_reason is not None @dataclass class HacsRepositories: """HACS Repositories.""" _default_repositories: set[str] = field(default_factory=set) _repositories: list[HacsRepository] = field(default_factory=list) _repositories_by_full_name: dict[str, str] = field(default_factory=dict) _repositories_by_id: dict[str, str] = field(default_factory=dict) _removed_repositories: list[RemovedRepository] = field(default_factory=list) @property def list_all(self) -> list[HacsRepository]: """Return a list of repositories.""" return self._repositories @property def list_removed(self) -> list[RemovedRepository]: """Return a list of removed repositories.""" return self._removed_repositories @property def list_downloaded(self) -> list[HacsRepository]: """Return a list of downloaded repositories.""" return [repo for repo in self._repositories if repo.data.installed] def register(self, repository: HacsRepository, default: bool = False) -> None: """Register a repository.""" repo_id = str(repository.data.id) if repo_id == "0": return if self.is_registered(repository_id=repo_id): return if repository not in self._repositories: self._repositories.append(repository) self._repositories_by_id[repo_id] = repository self._repositories_by_full_name[repository.data.full_name_lower] = repository if default: self.mark_default(repository) def unregister(self, repository: HacsRepository) -> None: """Unregister a repository.""" repo_id = str(repository.data.id) if repo_id == "0": return if not self.is_registered(repository_id=repo_id): return if self.is_default(repo_id): self._default_repositories.remove(repo_id) if repository in self._repositories: self._repositories.remove(repository) self._repositories_by_id.pop(repo_id, None) self._repositories_by_full_name.pop(repository.data.full_name_lower, None) def mark_default(self, repository: HacsRepository) -> None: """Mark a repository as default.""" repo_id = str(repository.data.id) if repo_id == "0": return if not self.is_registered(repository_id=repo_id): return self._default_repositories.add(repo_id) def set_repository_id(self, repository, repo_id): """Update a repository id.""" existing_repo_id = str(repository.data.id) if existing_repo_id == repo_id: return if existing_repo_id != "0": raise ValueError( f"The repo id for {repository.data.full_name_lower} " f"is already set to {existing_repo_id}" ) repository.data.id = repo_id self.register(repository) def is_default(self, repository_id: str | None = None) -> bool: """Check if a repository is default.""" if not repository_id: return False return repository_id in self._default_repositories def is_registered( self, repository_id: str | None = None, repository_full_name: str | None = None, ) -> bool: """Check if a repository is registered.""" if repository_id is not None: return repository_id in self._repositories_by_id if repository_full_name is not None: return repository_full_name in self._repositories_by_full_name return False def is_downloaded( self, repository_id: str | None = None, repository_full_name: str | None = None, ) -> bool: """Check if a repository is registered.""" if repository_id is not None: repo = self.get_by_id(repository_id) if repository_full_name is not None: repo = self.get_by_full_name(repository_full_name) if repo is None: return False return repo.data.installed def get_by_id(self, repository_id: str | None) -> HacsRepository | None: """Get repository by id.""" if not repository_id: return None return self._repositories_by_id.get(str(repository_id)) def get_by_full_name(self, repository_full_name: str | None) -> HacsRepository | None: """Get repository by full name.""" if not repository_full_name: return None return self._repositories_by_full_name.get(repository_full_name.lower()) def is_removed(self, repository_full_name: str) -> bool: """Check if a repository is removed.""" return repository_full_name in ( repository.repository for repository in self._removed_repositories ) def removed_repository(self, repository_full_name: str) -> RemovedRepository: """Get repository by full name.""" if self.is_removed(repository_full_name): if removed := [ repository for repository in self._removed_repositories if repository.repository == repository_full_name ]: return removed[0] removed = RemovedRepository(repository=repository_full_name) self._removed_repositories.append(removed) return removed class HacsBase: """Base HACS class.""" common = HacsCommon() configuration = HacsConfiguration() core = HacsCore() data: HacsData | None = None frontend_version: str | None = None github: GitHub | None = None githubapi: GitHubAPI | None = None hass: HomeAssistant | None = None integration: Integration | None = None log: logging.Logger = LOGGER queue: QueueManager | None = None recuring_tasks = [] repositories: HacsRepositories = HacsRepositories() repository: AIOGitHubAPIRepository | None = None session: ClientSession | None = None stage: HacsStage | None = None status = HacsStatus() system = HacsSystem() validation: ValidationManager | None = None version: str | None = None @property def integration_dir(self) -> pathlib.Path: """Return the HACS integration dir.""" return self.integration.file_path def set_stage(self, stage: HacsStage | None) -> None: """Set HACS stage.""" if stage and self.stage == stage: return self.stage = stage if stage is not None: self.log.info("Stage changed: %s", self.stage) self.async_dispatch(HacsDispatchEvent.STAGE, {"stage": self.stage}) def disable_hacs(self, reason: HacsDisabledReason) -> None: """Disable HACS.""" if self.system.disabled_reason == reason: return self.system.disabled_reason = reason if reason != HacsDisabledReason.REMOVED: self.log.error("HACS is disabled - %s", reason) if ( reason == HacsDisabledReason.INVALID_TOKEN and self.configuration.config_type == ConfigurationType.CONFIG_ENTRY ): self.configuration.config_entry.state = ConfigEntryState.SETUP_ERROR self.configuration.config_entry.reason = "Authentication failed" self.hass.add_job(self.configuration.config_entry.async_start_reauth, self.hass) def enable_hacs(self) -> None: """Enable HACS.""" if self.system.disabled_reason is not None: self.system.disabled_reason = None self.log.info("HACS is enabled") def enable_hacs_category(self, category: HacsCategory) -> None: """Enable HACS category.""" if category not in self.common.categories: self.log.info("Enable category: %s", category) self.common.categories.add(category) def disable_hacs_category(self, category: HacsCategory) -> None: """Disable HACS category.""" if category in self.common.categories: self.log.info("Disabling category: %s", category) self.common.categories.pop(category) async def async_save_file(self, file_path: str, content: Any) -> bool: """Save a file.""" def _write_file(): with open( file_path, mode="w" if isinstance(content, str) else "wb", encoding="utf-8" if isinstance(content, str) else None, errors="ignore" if isinstance(content, str) else None, ) as file_handler: file_handler.write(content) # Create gz for .js files if os.path.isfile(file_path): if file_path.endswith(".js"): with open(file_path, "rb") as f_in: with gzip.open(file_path + ".gz", "wb") as f_out: shutil.copyfileobj(f_in, f_out) # LEGACY! Remove with 2.0 if "themes" in file_path and file_path.endswith(".yaml"): filename = file_path.split("/")[-1] base = file_path.split("/themes/")[0] combined = f"{base}/themes/{filename}" if os.path.exists(combined): self.log.info("Removing old theme file %s", combined) os.remove(combined) try: await self.hass.async_add_executor_job(_write_file) except BaseException as error: # lgtm [py/catch-base-exception] pylint: disable=broad-except self.log.error("Could not write data to %s - %s", file_path, error) return False return os.path.exists(file_path) async def async_can_update(self) -> int: """Helper to calculate the number of repositories we can fetch data for.""" try: response = await self.async_github_api_method(self.githubapi.rate_limit) if ((limit := response.data.resources.core.remaining or 0) - 1000) >= 10: return math.floor((limit - 1000) / 10) reset = dt.as_local(dt.utc_from_timestamp(response.data.resources.core.reset)) self.log.info( "GitHub API ratelimited - %s remaining (%s)", response.data.resources.core.remaining, f"{reset.hour}:{reset.minute}:{reset.second}", ) self.disable_hacs(HacsDisabledReason.RATE_LIMIT) except BaseException as exception: # lgtm [py/catch-base-exception] pylint: disable=broad-except self.log.exception(exception) return 0 async def async_github_get_hacs_default_file(self, filename: str) -> list: """Get the content of a default file.""" response = await self.async_github_api_method( method=self.githubapi.repos.contents.get, repository=HacsGitHubRepo.DEFAULT, path=filename, ) if response is None: return [] return json_loads(decode_content(response.data.content)) async def async_github_api_method( self, method: Callable[[], Awaitable[TV]], *args, raise_exception: bool = True, **kwargs, ) -> TV | None: """Call a GitHub API method""" _exception = None try: return await method(*args, **kwargs) except GitHubAuthenticationException as exception: self.disable_hacs(HacsDisabledReason.INVALID_TOKEN) _exception = exception except GitHubRatelimitException as exception: self.disable_hacs(HacsDisabledReason.RATE_LIMIT) _exception = exception except GitHubNotModifiedException as exception: raise exception except GitHubException as exception: _exception = exception except BaseException as exception: # lgtm [py/catch-base-exception] pylint: disable=broad-except self.log.exception(exception) _exception = exception if raise_exception and _exception is not None: raise HacsException(_exception) return None async def async_register_repository( self, repository_full_name: str, category: HacsCategory, *, check: bool = True, ref: str | None = None, repository_id: str | None = None, default: bool = False, ) -> None: """Register a repository.""" if repository_full_name in self.common.skip: if repository_full_name != HacsGitHubRepo.INTEGRATION: raise HacsExpectedException(f"Skipping {repository_full_name}") if repository_full_name == "home-assistant/core": raise HomeAssistantCoreRepositoryException() if repository_full_name == "home-assistant/addons" or repository_full_name.startswith( "hassio-addons/" ): raise AddonRepositoryException() if category not in RERPOSITORY_CLASSES: raise HacsException(f"{category} is not a valid repository category.") if (renamed := self.common.renamed_repositories.get(repository_full_name)) is not None: repository_full_name = renamed repository: HacsRepository = RERPOSITORY_CLASSES[category](self, repository_full_name) if check: try: await repository.async_registration(ref) if self.status.new: repository.data.new = False if repository.validate.errors: self.common.skip.append(repository.data.full_name) if not self.status.startup: self.log.error("Validation for %s failed.", repository_full_name) if self.system.action: raise HacsException( f"::error:: Validation for {repository_full_name} failed." ) return repository.validate.errors if self.system.action: repository.logger.info("%s Validation completed", repository.string) else: repository.logger.info("%s Registration completed", repository.string) except (HacsRepositoryExistException, HacsRepositoryArchivedException): return except AIOGitHubAPIException as exception: self.common.skip.append(repository.data.full_name) raise HacsException( f"Validation for {repository_full_name} failed with {exception}." ) from exception if repository_id is not None: repository.data.id = repository_id if str(repository.data.id) != "0" and ( exists := self.repositories.get_by_id(repository.data.id) ): self.repositories.unregister(exists) else: if self.hass is not None and ((check and repository.data.new) or self.status.new): self.async_dispatch( HacsDispatchEvent.REPOSITORY, { "action": "registration", "repository": repository.data.full_name, "repository_id": repository.data.id, }, ) self.repositories.register(repository, default) async def startup_tasks(self, _=None) -> None: """Tasks that are started after setup.""" self.set_stage(HacsStage.STARTUP) try: repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION) if repository is None: await self.async_register_repository( repository_full_name=HacsGitHubRepo.INTEGRATION, category=HacsCategory.INTEGRATION, default=True, ) repository = self.repositories.get_by_full_name(HacsGitHubRepo.INTEGRATION) if repository is None: raise HacsException("Unknown error") repository.data.installed = True repository.data.installed_version = self.integration.version.string repository.data.new = False repository.data.releases = True self.repository = repository.repository_object self.repositories.mark_default(repository) except HacsException as exception: if "403" in str(exception): self.log.critical( "GitHub API is ratelimited, or the token is wrong.", ) else: self.log.critical("Could not load HACS! - %s", exception) self.disable_hacs(HacsDisabledReason.LOAD_HACS) if critical := await async_load_from_store(self.hass, "critical"): for repo in critical: if not repo["acknowledged"]: self.log.critical("URGENT!: Check the HACS panel!") self.hass.components.persistent_notification.create( title="URGENT!", message="**Check the HACS panel!**" ) break self.recuring_tasks.append( self.hass.helpers.event.async_track_time_interval( self.async_get_all_category_repositories, timedelta(hours=3) ) ) self.recuring_tasks.append( self.hass.helpers.event.async_track_time_interval( self.async_update_all_repositories, timedelta(hours=25) ) ) self.recuring_tasks.append( self.hass.helpers.event.async_track_time_interval( self.async_check_rate_limit, timedelta(minutes=5) ) ) self.recuring_tasks.append( self.hass.helpers.event.async_track_time_interval( self.async_prosess_queue, timedelta(minutes=10) ) ) self.recuring_tasks.append( self.hass.helpers.event.async_track_time_interval( self.async_update_downloaded_repositories, timedelta(hours=2) ) ) self.recuring_tasks.append( self.hass.helpers.event.async_track_time_interval( self.async_handle_critical_repositories, timedelta(hours=2) ) ) self.hass.bus.async_listen_once( EVENT_HOMEASSISTANT_FINAL_WRITE, self.data.async_force_write ) self.status.startup = False self.async_dispatch(HacsDispatchEvent.STATUS, {}) await self.async_handle_removed_repositories() await self.async_get_all_category_repositories() await self.async_update_downloaded_repositories() self.set_stage(HacsStage.RUNNING) self.async_dispatch(HacsDispatchEvent.RELOAD, {"force": True}) await self.async_handle_critical_repositories() await self.async_prosess_queue() self.async_dispatch(HacsDispatchEvent.STATUS, {}) async def async_download_file(self, url: str, *, headers: dict | None = None) -> bytes | None: """Download files, and return the content.""" if url is None: return None if "tags/" in url: url = url.replace("tags/", "") self.log.debug("Downloading %s", url) timeouts = 0 while timeouts < 5: try: request = await self.session.get( url=url, timeout=ClientTimeout(total=60), headers=headers, ) # Make sure that we got a valid result if request.status == 200: return await request.read() raise HacsException( f"Got status code {request.status} when trying to download {url}" ) except asyncio.TimeoutError: self.log.warning( "A timeout of 60! seconds was encountered while downloading %s, " "using over 60 seconds to download a single file is not normal. " "This is not a problem with HACS but how your host communicates with GitHub. " "Retrying up to 5 times to mask/hide your host/network problems to " "stop the flow of issues opened about it. " "Tries left %s", url, (4 - timeouts), ) timeouts += 1 await asyncio.sleep(1) continue except BaseException as exception: # lgtm [py/catch-base-exception] pylint: disable=broad-except self.log.exception("Download failed - %s", exception) return None async def async_recreate_entities(self) -> None: """Recreate entities.""" if self.configuration == ConfigurationType.YAML or not self.configuration.experimental: return platforms = [Platform.SENSOR, Platform.UPDATE] await self.hass.config_entries.async_unload_platforms( entry=self.configuration.config_entry, platforms=platforms, ) await async_setup_entity_platforms( self, self.hass, self.configuration.config_entry, platforms ) @callback def async_dispatch(self, signal: HacsDispatchEvent, data: dict | None = None) -> None: """Dispatch a signal with data.""" async_dispatcher_send(self.hass, signal, data) def set_active_categories(self) -> None: """Set the active categories.""" self.common.categories = set() for category in (HacsCategory.INTEGRATION, HacsCategory.PLUGIN): self.enable_hacs_category(HacsCategory(category)) if HacsCategory.PYTHON_SCRIPT in self.hass.config.components: self.enable_hacs_category(HacsCategory.PYTHON_SCRIPT) if self.hass.services.has_service("frontend", "reload_themes"): self.enable_hacs_category(HacsCategory.THEME) if self.configuration.appdaemon: self.enable_hacs_category(HacsCategory.APPDAEMON) if self.configuration.netdaemon: self.enable_hacs_category(HacsCategory.NETDAEMON) async def async_get_all_category_repositories(self, _=None) -> None: """Get all category repositories.""" if self.system.disabled: return self.log.info("Loading known repositories") await asyncio.gather( *[ self.async_get_category_repositories(HacsCategory(category)) for category in self.common.categories or [] ] ) async def async_get_category_repositories(self, category: HacsCategory) -> None: """Get repositories from category.""" if self.system.disabled: return try: repositories = await self.async_github_get_hacs_default_file(category) except HacsException: return for repo in repositories: if self.common.renamed_repositories.get(repo): repo = self.common.renamed_repositories[repo] if self.repositories.is_removed(repo): continue if repo in self.common.archived_repositories: continue repository = self.repositories.get_by_full_name(repo) if repository is not None: self.repositories.mark_default(repository) if self.status.new and self.configuration.dev: # Force update for new installations self.queue.add(repository.common_update()) continue self.queue.add( self.async_register_repository( repository_full_name=repo, category=category, default=True, ) ) async def async_update_all_repositories(self, _=None) -> None: """Update all repositories.""" if self.system.disabled: return self.log.debug("Starting recurring background task for all repositories") for repository in self.repositories.list_all: if repository.data.category in self.common.categories: self.queue.add(repository.common_update()) self.async_dispatch(HacsDispatchEvent.REPOSITORY, {"action": "reload"}) self.log.debug("Recurring background task for all repositories done") async def async_check_rate_limit(self, _=None) -> None: """Check rate limit.""" if not self.system.disabled or self.system.disabled_reason != HacsDisabledReason.RATE_LIMIT: return self.log.debug("Checking if ratelimit has lifted") can_update = await self.async_can_update() self.log.debug("Ratelimit indicate we can update %s", can_update) if can_update > 0: self.enable_hacs() await self.async_prosess_queue() async def async_prosess_queue(self, _=None) -> None: """Process the queue.""" if self.system.disabled: self.log.debug("HACS is disabled") return if not self.queue.has_pending_tasks: self.log.debug("Nothing in the queue") return if self.queue.running: self.log.debug("Queue is already running") return async def _handle_queue(): if not self.queue.has_pending_tasks: await self.data.async_write() return can_update = await self.async_can_update() self.log.debug( "Can update %s repositories, " "items in queue %s", can_update, self.queue.pending_tasks, ) if can_update != 0: try: await self.queue.execute(can_update) except HacsExecutionStillInProgress: return await _handle_queue() await _handle_queue() async def async_handle_removed_repositories(self, _=None) -> None: """Handle removed repositories.""" if self.system.disabled: return need_to_save = False self.log.info("Loading removed repositories") try: removed_repositories = await self.async_github_get_hacs_default_file( HacsCategory.REMOVED ) except HacsException: return for item in removed_repositories: removed = self.repositories.removed_repository(item["repository"]) removed.update_data(item) for removed in self.repositories.list_removed: if (repository := self.repositories.get_by_full_name(removed.repository)) is None: continue if repository.data.full_name in self.common.ignored_repositories: continue if repository.data.installed and removed.removal_type != "critical": self.log.warning( "You have '%s' installed with HACS " "this repository has been removed from HACS, please consider removing it. " "Removal reason (%s)", repository.data.full_name, removed.reason, ) else: need_to_save = True repository.remove() if need_to_save: await self.data.async_write() async def async_update_downloaded_repositories(self, _=None) -> None: """Execute the task.""" if self.system.disabled: return self.log.info("Starting recurring background task for downloaded repositories") for repository in self.repositories.list_downloaded: if repository.data.category in self.common.categories: self.queue.add(repository.update_repository(ignore_issues=True)) self.log.debug("Recurring background task for downloaded repositories done") async def async_handle_critical_repositories(self, _=None) -> None: """Handle critical repositories.""" critical_queue = QueueManager(hass=self.hass) instored = [] critical = [] was_installed = False try: critical = await self.async_github_get_hacs_default_file("critical") except GitHubNotModifiedException: return except HacsException: pass if not critical: self.log.debug("No critical repositories") return stored_critical = await async_load_from_store(self.hass, "critical") for stored in stored_critical or []: instored.append(stored["repository"]) stored_critical = [] for repository in critical: removed_repo = self.repositories.removed_repository(repository["repository"]) removed_repo.removal_type = "critical" repo = self.repositories.get_by_full_name(repository["repository"]) stored = { "repository": repository["repository"], "reason": repository["reason"], "link": repository["link"], "acknowledged": True, } if repository["repository"] not in instored: if repo is not None and repo.data.installed: self.log.critical( "Removing repository %s, it is marked as critical", repository["repository"], ) was_installed = True stored["acknowledged"] = False # Remove from HACS critical_queue.add(repo.uninstall()) repo.remove() stored_critical.append(stored) removed_repo.update_data(stored) # Uninstall await critical_queue.execute() # Save to FS await async_save_to_store(self.hass, "critical", stored_critical) # Restart HASS if was_installed: self.log.critical("Restarting Home Assistant") self.hass.async_create_task(self.hass.async_stop(100)) ================================================ FILE: custom_components/hacs/config_flow.py ================================================ """Adds config flow for HACS.""" from aiogithubapi import GitHubDeviceAPI, GitHubException from aiogithubapi.common.const import OAUTH_USER_LOGIN from awesomeversion import AwesomeVersion from homeassistant import config_entries from homeassistant.const import __version__ as HAVERSION from homeassistant.core import callback from homeassistant.helpers import aiohttp_client from homeassistant.helpers.event import async_call_later from homeassistant.loader import async_get_integration import voluptuous as vol from .base import HacsBase from .const import CLIENT_ID, DOMAIN, MINIMUM_HA_VERSION from .enums import ConfigurationType from .utils.configuration_schema import RELEASE_LIMIT, hacs_config_option_schema from .utils.logger import LOGGER class HacsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """Config flow for HACS.""" VERSION = 1 CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL def __init__(self): """Initialize.""" self._errors = {} self.device = None self.activation = None self.log = LOGGER self._progress_task = None self._login_device = None self._reauth = False async def async_step_user(self, user_input): """Handle a flow initialized by the user.""" self._errors = {} if self._async_current_entries(): return self.async_abort(reason="single_instance_allowed") if self.hass.data.get(DOMAIN): return self.async_abort(reason="single_instance_allowed") if user_input: if [x for x in user_input if not user_input[x]]: self._errors["base"] = "acc" return await self._show_config_form(user_input) return await self.async_step_device(user_input) ## Initial form return await self._show_config_form(user_input) async def async_step_device(self, _user_input): """Handle device steps""" async def _wait_for_activation(_=None): if self._login_device is None or self._login_device.expires_in is None: async_call_later(self.hass, 1, _wait_for_activation) return response = await self.device.activation(device_code=self._login_device.device_code) self.activation = response.data self.hass.async_create_task( self.hass.config_entries.flow.async_configure(flow_id=self.flow_id) ) if not self.activation: integration = await async_get_integration(self.hass, DOMAIN) if not self.device: self.device = GitHubDeviceAPI( client_id=CLIENT_ID, session=aiohttp_client.async_get_clientsession(self.hass), **{"client_name": f"HACS/{integration.version}"}, ) async_call_later(self.hass, 1, _wait_for_activation) try: response = await self.device.register() self._login_device = response.data return self.async_show_progress( step_id="device", progress_action="wait_for_device", description_placeholders={ "url": OAUTH_USER_LOGIN, "code": self._login_device.user_code, }, ) except GitHubException as exception: self.log.error(exception) return self.async_abort(reason="github") return self.async_show_progress_done(next_step_id="device_done") async def _show_config_form(self, user_input): """Show the configuration form to edit location data.""" if not user_input: user_input = {} if AwesomeVersion(HAVERSION) < MINIMUM_HA_VERSION: return self.async_abort( reason="min_ha_version", description_placeholders={"version": MINIMUM_HA_VERSION}, ) return self.async_show_form( step_id="user", data_schema=vol.Schema( { vol.Required("acc_logs", default=user_input.get("acc_logs", False)): bool, vol.Required("acc_addons", default=user_input.get("acc_addons", False)): bool, vol.Required( "acc_untested", default=user_input.get("acc_untested", False) ): bool, vol.Required("acc_disable", default=user_input.get("acc_disable", False)): bool, } ), errors=self._errors, ) async def async_step_device_done(self, _user_input): """Handle device steps""" if self._reauth: existing_entry = self.hass.config_entries.async_get_entry(self.context["entry_id"]) self.hass.config_entries.async_update_entry( existing_entry, data={"token": self.activation.access_token} ) await self.hass.config_entries.async_reload(existing_entry.entry_id) return self.async_abort(reason="reauth_successful") return self.async_create_entry(title="", data={"token": self.activation.access_token}) async def async_step_reauth(self, user_input=None): """Perform reauth upon an API authentication error.""" return await self.async_step_reauth_confirm() async def async_step_reauth_confirm(self, user_input=None): """Dialog that informs the user that reauth is required.""" if user_input is None: return self.async_show_form( step_id="reauth_confirm", data_schema=vol.Schema({}), ) self._reauth = True return await self.async_step_device(None) @staticmethod @callback def async_get_options_flow(config_entry): return HacsOptionsFlowHandler(config_entry) class HacsOptionsFlowHandler(config_entries.OptionsFlow): """HACS config flow options handler.""" def __init__(self, config_entry): """Initialize HACS options flow.""" self.config_entry = config_entry async def async_step_init(self, _user_input=None): """Manage the options.""" return await self.async_step_user() async def async_step_user(self, user_input=None): """Handle a flow initialized by the user.""" hacs: HacsBase = self.hass.data.get(DOMAIN) if user_input is not None: limit = int(user_input.get(RELEASE_LIMIT, 5)) if limit <= 0 or limit > 100: return self.async_abort(reason="release_limit_value") return self.async_create_entry(title="", data=user_input) if hacs is None or hacs.configuration is None: return self.async_abort(reason="not_setup") if hacs.configuration.config_type == ConfigurationType.YAML: schema = {vol.Optional("not_in_use", default=""): str} else: schema = hacs_config_option_schema(self.config_entry.options) del schema["frontend_repo"] del schema["frontend_repo_url"] return self.async_show_form(step_id="user", data_schema=vol.Schema(schema)) ================================================ FILE: custom_components/hacs/const.py ================================================ """Constants for HACS""" from typing import TypeVar from aiogithubapi.common.const import ACCEPT_HEADERS NAME_SHORT = "HACS" DOMAIN = "hacs" CLIENT_ID = "395a8e669c5de9f7c6e8" MINIMUM_HA_VERSION = "2022.4.0" TV = TypeVar("TV") PACKAGE_NAME = "custom_components.hacs" DEFAULT_CONCURRENT_TASKS = 15 DEFAULT_CONCURRENT_BACKOFF_TIME = 1 HACS_ACTION_GITHUB_API_HEADERS = { "User-Agent": "HACS/action", "Accept": ACCEPT_HEADERS["preview"], } VERSION_STORAGE = "6" STORENAME = "hacs" HACS_SYSTEM_ID = "0717a0cd-745c-48fd-9b16-c8534c9704f9-bc944b0f-fd42-4a58-a072-ade38d1444cd" STARTUP = """ ------------------------------------------------------------------- HACS (Home Assistant Community Store) Version: %s This is a custom integration If you have any issues with this you need to open an issue here: https://github.com/hacs/integration/issues ------------------------------------------------------------------- """ LOCALE = [ "ALL", "AF", "AL", "DZ", "AS", "AD", "AO", "AI", "AQ", "AG", "AR", "AM", "AW", "AU", "AT", "AZ", "BS", "BH", "BD", "BB", "BY", "BE", "BZ", "BJ", "BM", "BT", "BO", "BQ", "BA", "BW", "BV", "BR", "IO", "BN", "BG", "BF", "BI", "KH", "CM", "CA", "CV", "KY", "CF", "TD", "CL", "CN", "CX", "CC", "CO", "KM", "CG", "CD", "CK", "CR", "HR", "CU", "CW", "CY", "CZ", "CI", "DK", "DJ", "DM", "DO", "EC", "EG", "SV", "GQ", "ER", "EE", "ET", "FK", "FO", "FJ", "FI", "FR", "GF", "PF", "TF", "GA", "GM", "GE", "DE", "GH", "GI", "GR", "GL", "GD", "GP", "GU", "GT", "GG", "GN", "GW", "GY", "HT", "HM", "VA", "HN", "HK", "HU", "IS", "IN", "ID", "IR", "IQ", "IE", "IM", "IL", "IT", "JM", "JP", "JE", "JO", "KZ", "KE", "KI", "KP", "KR", "KW", "KG", "LA", "LV", "LB", "LS", "LR", "LY", "LI", "LT", "LU", "MO", "MK", "MG", "MW", "MY", "MV", "ML", "MT", "MH", "MQ", "MR", "MU", "YT", "MX", "FM", "MD", "MC", "MN", "ME", "MS", "MA", "MZ", "MM", "NA", "NR", "NP", "NL", "NC", "NZ", "NI", "NE", "NG", "NU", "NF", "MP", "NO", "OM", "PK", "PW", "PS", "PA", "PG", "PY", "PE", "PH", "PN", "PL", "PT", "PR", "QA", "RO", "RU", "RW", "RE", "BL", "SH", "KN", "LC", "MF", "PM", "VC", "WS", "SM", "ST", "SA", "SN", "RS", "SC", "SL", "SG", "SX", "SK", "SI", "SB", "SO", "ZA", "GS", "SS", "ES", "LK", "SD", "SR", "SJ", "SZ", "SE", "CH", "SY", "TW", "TJ", "TZ", "TH", "TL", "TG", "TK", "TO", "TT", "TN", "TR", "TM", "TC", "TV", "UG", "UA", "AE", "GB", "US", "UM", "UY", "UZ", "VU", "VE", "VN", "VG", "VI", "WF", "EH", "YE", "ZM", "ZW", ] ================================================ FILE: custom_components/hacs/diagnostics.py ================================================ """Diagnostics support for HACS.""" from __future__ import annotations from typing import Any from aiogithubapi import GitHubException from homeassistant.components.diagnostics import async_redact_data from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from .base import HacsBase from .const import DOMAIN from .utils.configuration_schema import TOKEN async def async_get_config_entry_diagnostics( hass: HomeAssistant, entry: ConfigEntry, ) -> dict[str, Any]: """Return diagnostics for a config entry.""" hacs: HacsBase = hass.data[DOMAIN] data = { "entry": entry.as_dict(), "hacs": { "stage": hacs.stage, "version": hacs.version, "disabled_reason": hacs.system.disabled_reason, "new": hacs.status.new, "startup": hacs.status.startup, "categories": hacs.common.categories, "renamed_repositories": hacs.common.renamed_repositories, "archived_repositories": hacs.common.archived_repositories, "ignored_repositories": hacs.common.ignored_repositories, "lovelace_mode": hacs.core.lovelace_mode, "configuration": {}, }, "custom_repositories": [ repo.data.full_name for repo in hacs.repositories.list_all if not hacs.repositories.is_default(str(repo.data.id)) ], "repositories": [], } for key in ( "appdaemon", "country", "debug", "dev", "experimental", "netdaemon", "python_script", "release_limit", "theme", ): data["hacs"]["configuration"][key] = getattr(hacs.configuration, key, None) for repository in hacs.repositories.list_downloaded: data["repositories"].append( { "data": repository.data.to_json(), "integration_manifest": repository.integration_manifest, "repository_manifest": repository.repository_manifest.to_dict(), "ref": repository.ref, "paths": { "localpath": repository.localpath.replace(hacs.core.config_path, "/config"), "local": repository.content.path.local.replace( hacs.core.config_path, "/config" ), "remote": repository.content.path.remote, }, } ) try: rate_limit_response = await hacs.githubapi.rate_limit() data["rate_limit"] = rate_limit_response.data.as_dict except GitHubException as exception: data["rate_limit"] = str(exception) return async_redact_data(data, (TOKEN,)) ================================================ FILE: custom_components/hacs/entity.py ================================================ """HACS Base entities.""" from __future__ import annotations from typing import TYPE_CHECKING, Any from homeassistant.core import callback from homeassistant.helpers.device_registry import DeviceEntryType from homeassistant.helpers.dispatcher import async_dispatcher_connect from homeassistant.helpers.entity import Entity from .const import DOMAIN, HACS_SYSTEM_ID, NAME_SHORT from .enums import HacsDispatchEvent, HacsGitHubRepo if TYPE_CHECKING: from .base import HacsBase from .repositories.base import HacsRepository def system_info(hacs: HacsBase) -> dict: """Return system info.""" return { "identifiers": {(DOMAIN, HACS_SYSTEM_ID)}, "name": NAME_SHORT, "manufacturer": "hacs.xyz", "model": "", "sw_version": str(hacs.version), "configuration_url": "homeassistant://hacs", "entry_type": DeviceEntryType.SERVICE, } class HacsBaseEntity(Entity): """Base HACS entity.""" repository: HacsRepository | None = None _attr_should_poll = False def __init__(self, hacs: HacsBase) -> None: """Initialize.""" self.hacs = hacs async def async_added_to_hass(self) -> None: """Register for status events.""" self.async_on_remove( async_dispatcher_connect( self.hass, HacsDispatchEvent.REPOSITORY, self._update_and_write_state, ) ) @callback def _update(self) -> None: """Update the sensor.""" async def async_update(self) -> None: """Manual updates of the sensor.""" self._update() @callback def _update_and_write_state(self, _: Any) -> None: """Update the entity and write state.""" self._update() self.async_write_ha_state() class HacsSystemEntity(HacsBaseEntity): """Base system entity.""" _attr_icon = "hacs:hacs" _attr_unique_id = HACS_SYSTEM_ID @property def device_info(self) -> dict[str, any]: """Return device information about HACS.""" return system_info(self.hacs) class HacsRepositoryEntity(HacsBaseEntity): """Base repository entity.""" def __init__( self, hacs: HacsBase, repository: HacsRepository, ) -> None: """Initialize.""" super().__init__(hacs=hacs) self.repository = repository self._attr_unique_id = str(repository.data.id) @property def available(self) -> bool: """Return True if entity is available.""" return self.hacs.repositories.is_downloaded(repository_id=str(self.repository.data.id)) @property def device_info(self) -> dict[str, any]: """Return device information about HACS.""" if self.repository.data.full_name == HacsGitHubRepo.INTEGRATION: return system_info(self.hacs) return { "identifiers": {(DOMAIN, str(self.repository.data.id))}, "name": self.repository.display_name, "model": self.repository.data.category, "manufacturer": ", ".join( author.replace("@", "") for author in self.repository.data.authors ), "configuration_url": "homeassistant://hacs", "entry_type": DeviceEntryType.SERVICE, } @callback def _update_and_write_state(self, data: dict) -> None: """Update the entity and write state.""" if data.get("repository_id") == self.repository.data.id: self._update() self.async_write_ha_state() ================================================ FILE: custom_components/hacs/enums.py ================================================ """Helper constants.""" # pylint: disable=missing-class-docstring from enum import Enum class HacsGitHubRepo(str, Enum): """HacsGitHubRepo.""" DEFAULT = "hacs/default" INTEGRATION = "hacs/integration" class HacsCategory(str, Enum): APPDAEMON = "appdaemon" INTEGRATION = "integration" LOVELACE = "lovelace" PLUGIN = "plugin" # Kept for legacy purposes NETDAEMON = "netdaemon" PYTHON_SCRIPT = "python_script" THEME = "theme" REMOVED = "removed" def __str__(self): return str(self.value) class HacsDispatchEvent(str, Enum): """HacsDispatchEvent.""" CONFIG = "hacs_dispatch_config" ERROR = "hacs_dispatch_error" RELOAD = "hacs_dispatch_reload" REPOSITORY = "hacs_dispatch_repository" REPOSITORY_DOWNLOAD_PROGRESS = "hacs_dispatch_repository_download_progress" STAGE = "hacs_dispatch_stage" STARTUP = "hacs_dispatch_startup" STATUS = "hacs_dispatch_status" class RepositoryFile(str, Enum): """Repository file names.""" HACS_JSON = "hacs.json" MAINIFEST_JSON = "manifest.json" class ConfigurationType(str, Enum): YAML = "yaml" CONFIG_ENTRY = "config_entry" class LovelaceMode(str, Enum): """Lovelace Modes.""" STORAGE = "storage" AUTO = "auto" AUTO_GEN = "auto-gen" YAML = "yaml" class HacsStage(str, Enum): SETUP = "setup" STARTUP = "startup" WAITING = "waiting" RUNNING = "running" BACKGROUND = "background" class HacsDisabledReason(str, Enum): RATE_LIMIT = "rate_limit" REMOVED = "removed" INVALID_TOKEN = "invalid_token" CONSTRAINS = "constrains" LOAD_HACS = "load_hacs" RESTORE = "restore" ================================================ FILE: custom_components/hacs/exceptions.py ================================================ """Custom Exceptions for HACS.""" class HacsException(Exception): """Super basic.""" class HacsRepositoryArchivedException(HacsException): """For repositories that are archived.""" class HacsNotModifiedException(HacsException): """For responses that are not modified.""" class HacsExpectedException(HacsException): """For stuff that are expected.""" class HacsRepositoryExistException(HacsException): """For repositories that are already exist.""" class HacsExecutionStillInProgress(HacsException): """Exception to raise if execution is still in progress.""" class AddonRepositoryException(HacsException): """Exception to raise when user tries to add add-on repository.""" exception_message = ( "The repository does not seem to be a integration, " "but an add-on repository. HACS does not manage add-ons." ) def __init__(self) -> None: super().__init__(self.exception_message) class HomeAssistantCoreRepositoryException(HacsException): """Exception to raise when user tries to add the home-assistant/core repository.""" exception_message = ( "You can not add homeassistant/core, to use core integrations " "check the Home Assistant documentation for how to add them." ) def __init__(self) -> None: super().__init__(self.exception_message) ================================================ FILE: custom_components/hacs/frontend.py ================================================ """"Starting setup task: Frontend".""" from __future__ import annotations from typing import TYPE_CHECKING from aiohttp import web from homeassistant.components.http import HomeAssistantView from homeassistant.core import HomeAssistant, callback from .const import DOMAIN from .hacs_frontend import locate_dir from .hacs_frontend.version import VERSION as FE_VERSION URL_BASE = "/hacsfiles" if TYPE_CHECKING: from .base import HacsBase @callback def async_register_frontend(hass: HomeAssistant, hacs: HacsBase) -> None: """Register the frontend.""" # Register themes hass.http.register_static_path(f"{URL_BASE}/themes", hass.config.path("themes")) # Register frontend if hacs.configuration.frontend_repo_url: hacs.log.warning( " Frontend development mode enabled. Do not run in production!" ) hass.http.register_view(HacsFrontendDev()) else: # hass.http.register_static_path(f"{URL_BASE}/frontend", locate_dir(), cache_headers=False) # Custom iconset hass.http.register_static_path( f"{URL_BASE}/iconset.js", str(hacs.integration_dir / "iconset.js") ) if "frontend_extra_module_url" not in hass.data: hass.data["frontend_extra_module_url"] = set() hass.data["frontend_extra_module_url"].add(f"{URL_BASE}/iconset.js") # Register www/community for all other files use_cache = hacs.core.lovelace_mode == "storage" hacs.log.info( " %s mode, cache for /hacsfiles/: %s", hacs.core.lovelace_mode, use_cache, ) hass.http.register_static_path( URL_BASE, hass.config.path("www/community"), cache_headers=use_cache, ) hacs.frontend_version = FE_VERSION # Add to sidepanel if needed if DOMAIN not in hass.data.get("frontend_panels", {}): hass.components.frontend.async_register_built_in_panel( component_name="custom", sidebar_title=hacs.configuration.sidepanel_title, sidebar_icon=hacs.configuration.sidepanel_icon, frontend_url_path=DOMAIN, config={ "_panel_custom": { "name": "hacs-frontend", "embed_iframe": True, "trust_external": False, "js_url": f"/hacsfiles/frontend/entrypoint.js?hacstag={FE_VERSION}", } }, require_admin=True, ) class HacsFrontendDev(HomeAssistantView): """Dev View Class for HACS.""" requires_auth = False name = "hacs_files:frontend" url = r"/hacsfiles/frontend/{requested_file:.+}" async def get(self, request, requested_file): # pylint: disable=unused-argument """Handle HACS Web requests.""" hacs: HacsBase = request.app["hass"].data.get(DOMAIN) requested = requested_file.split("/")[-1] request = await hacs.session.get(f"{hacs.configuration.frontend_repo_url}/{requested}") if request.status == 200: result = await request.read() response = web.Response(body=result) response.headers["Content-Type"] = "application/javascript" return response ================================================ FILE: custom_components/hacs/hacs_frontend/__init__.py ================================================ """HACS Frontend""" from .version import VERSION def locate_dir(): return __path__[0] ================================================ FILE: custom_components/hacs/hacs_frontend/c.004a7b01.js ================================================ var t=function(){if("undefined"!=typeof Map)return Map;function t(t,e){var n=-1;return t.some((function(t,r){return t[0]===e&&(n=r,!0)})),n}return function(){function e(){this.__entries__=[]}return Object.defineProperty(e.prototype,"size",{get:function(){return this.__entries__.length},enumerable:!0,configurable:!0}),e.prototype.get=function(e){var n=t(this.__entries__,e),r=this.__entries__[n];return r&&r[1]},e.prototype.set=function(e,n){var r=t(this.__entries__,e);~r?this.__entries__[r][1]=n:this.__entries__.push([e,n])},e.prototype.delete=function(e){var n=this.__entries__,r=t(n,e);~r&&n.splice(r,1)},e.prototype.has=function(e){return!!~t(this.__entries__,e)},e.prototype.clear=function(){this.__entries__.splice(0)},e.prototype.forEach=function(t,e){void 0===e&&(e=null);for(var n=0,r=this.__entries__;n0},t.prototype.connect_=function(){e&&!this.connected_&&(document.addEventListener("transitionend",this.onTransitionEnd_),window.addEventListener("resize",this.refresh),o?(this.mutationsObserver_=new MutationObserver(this.refresh),this.mutationsObserver_.observe(document,{attributes:!0,childList:!0,characterData:!0,subtree:!0})):(document.addEventListener("DOMSubtreeModified",this.refresh),this.mutationEventsAdded_=!0),this.connected_=!0)},t.prototype.disconnect_=function(){e&&this.connected_&&(document.removeEventListener("transitionend",this.onTransitionEnd_),window.removeEventListener("resize",this.refresh),this.mutationsObserver_&&this.mutationsObserver_.disconnect(),this.mutationEventsAdded_&&document.removeEventListener("DOMSubtreeModified",this.refresh),this.mutationsObserver_=null,this.mutationEventsAdded_=!1,this.connected_=!1)},t.prototype.onTransitionEnd_=function(t){var e=t.propertyName,n=void 0===e?"":e;i.some((function(t){return!!~n.indexOf(t)}))&&this.refresh()},t.getInstance=function(){return this.instance_||(this.instance_=new t),this.instance_},t.instance_=null,t}(),a=function(t,e){for(var n=0,r=Object.keys(e);n0},e}(),y="undefined"!=typeof WeakMap?new WeakMap:new t,g=function t(e){if(!(this instanceof t))throw new TypeError("Cannot call a class as a function.");if(!arguments.length)throw new TypeError("1 argument required, but only 0 present.");var n=s.getInstance(),r=new m(e,n,this);y.set(this,r)};["observe","unobserve","disconnect"].forEach((function(t){g.prototype[t]=function(){var e;return(e=y.get(this))[t].apply(e,arguments)}}));var w=void 0!==n.ResizeObserver?n.ResizeObserver:g;export{w as default}; ================================================ FILE: custom_components/hacs/hacs_frontend/c.01f18260.js ================================================ import{u as e,v as t,G as i,M as c,_ as o,i as r,e as n,t as a,B as d,$ as s,o as p,I as l,y as m,p as h,q as u,r as f,n as b,a as g,h as k,J as x,K as _,g as y,w as v,R as T,j as w,A as E}from"./main-7bc9a818.js";import{c as O,o as I}from"./c.5d9598b2.js";import{o as C}from"./c.8e28b461.js";var A,R,S={ANCHOR:"mdc-menu-surface--anchor",ANIMATING_CLOSED:"mdc-menu-surface--animating-closed",ANIMATING_OPEN:"mdc-menu-surface--animating-open",FIXED:"mdc-menu-surface--fixed",IS_OPEN_BELOW:"mdc-menu-surface--is-open-below",OPEN:"mdc-menu-surface--open",ROOT:"mdc-menu-surface"},F={CLOSED_EVENT:"MDCMenuSurface:closed",CLOSING_EVENT:"MDCMenuSurface:closing",OPENED_EVENT:"MDCMenuSurface:opened",FOCUSABLE_ELEMENTS:["button:not(:disabled)",'[href]:not([aria-disabled="true"])',"input:not(:disabled)","select:not(:disabled)","textarea:not(:disabled)",'[tabindex]:not([tabindex="-1"]):not([aria-disabled="true"])'].join(", ")},B={TRANSITION_OPEN_DURATION:120,TRANSITION_CLOSE_DURATION:75,MARGIN_TO_EDGE:32,ANCHOR_TO_MENU_SURFACE_WIDTH_RATIO:.67,TOUCH_EVENT_WAIT_MS:30};!function(e){e[e.BOTTOM=1]="BOTTOM",e[e.CENTER=2]="CENTER",e[e.RIGHT=4]="RIGHT",e[e.FLIP_RTL=8]="FLIP_RTL"}(A||(A={})),function(e){e[e.TOP_LEFT=0]="TOP_LEFT",e[e.TOP_RIGHT=4]="TOP_RIGHT",e[e.BOTTOM_LEFT=1]="BOTTOM_LEFT",e[e.BOTTOM_RIGHT=5]="BOTTOM_RIGHT",e[e.TOP_START=8]="TOP_START",e[e.TOP_END=12]="TOP_END",e[e.BOTTOM_START=9]="BOTTOM_START",e[e.BOTTOM_END=13]="BOTTOM_END"}(R||(R={}));var M=function(c){function o(e){var i=c.call(this,t(t({},o.defaultAdapter),e))||this;return i.isSurfaceOpen=!1,i.isQuickOpen=!1,i.isHoistedElement=!1,i.isFixedPosition=!1,i.isHorizontallyCenteredOnViewport=!1,i.maxHeight=0,i.openBottomBias=0,i.openAnimationEndTimerId=0,i.closeAnimationEndTimerId=0,i.animationRequestId=0,i.anchorCorner=R.TOP_START,i.originCorner=R.TOP_START,i.anchorMargin={top:0,right:0,bottom:0,left:0},i.position={x:0,y:0},i}return e(o,c),Object.defineProperty(o,"cssClasses",{get:function(){return S},enumerable:!1,configurable:!0}),Object.defineProperty(o,"strings",{get:function(){return F},enumerable:!1,configurable:!0}),Object.defineProperty(o,"numbers",{get:function(){return B},enumerable:!1,configurable:!0}),Object.defineProperty(o,"Corner",{get:function(){return R},enumerable:!1,configurable:!0}),Object.defineProperty(o,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},hasAnchor:function(){return!1},isElementInContainer:function(){return!1},isFocused:function(){return!1},isRtl:function(){return!1},getInnerDimensions:function(){return{height:0,width:0}},getAnchorDimensions:function(){return null},getWindowDimensions:function(){return{height:0,width:0}},getBodyDimensions:function(){return{height:0,width:0}},getWindowScroll:function(){return{x:0,y:0}},setPosition:function(){},setMaxHeight:function(){},setTransformOrigin:function(){},saveFocus:function(){},restoreFocus:function(){},notifyClose:function(){},notifyOpen:function(){},notifyClosing:function(){}}},enumerable:!1,configurable:!0}),o.prototype.init=function(){var e=o.cssClasses,t=e.ROOT,i=e.OPEN;if(!this.adapter.hasClass(t))throw new Error(t+" class required in root element.");this.adapter.hasClass(i)&&(this.isSurfaceOpen=!0)},o.prototype.destroy=function(){clearTimeout(this.openAnimationEndTimerId),clearTimeout(this.closeAnimationEndTimerId),cancelAnimationFrame(this.animationRequestId)},o.prototype.setAnchorCorner=function(e){this.anchorCorner=e},o.prototype.flipCornerHorizontally=function(){this.originCorner=this.originCorner^A.RIGHT},o.prototype.setAnchorMargin=function(e){this.anchorMargin.top=e.top||0,this.anchorMargin.right=e.right||0,this.anchorMargin.bottom=e.bottom||0,this.anchorMargin.left=e.left||0},o.prototype.setIsHoisted=function(e){this.isHoistedElement=e},o.prototype.setFixedPosition=function(e){this.isFixedPosition=e},o.prototype.isFixed=function(){return this.isFixedPosition},o.prototype.setAbsolutePosition=function(e,t){this.position.x=this.isFinite(e)?e:0,this.position.y=this.isFinite(t)?t:0},o.prototype.setIsHorizontallyCenteredOnViewport=function(e){this.isHorizontallyCenteredOnViewport=e},o.prototype.setQuickOpen=function(e){this.isQuickOpen=e},o.prototype.setMaxHeight=function(e){this.maxHeight=e},o.prototype.setOpenBottomBias=function(e){this.openBottomBias=e},o.prototype.isOpen=function(){return this.isSurfaceOpen},o.prototype.open=function(){var e=this;this.isSurfaceOpen||(this.adapter.saveFocus(),this.isQuickOpen?(this.isSurfaceOpen=!0,this.adapter.addClass(o.cssClasses.OPEN),this.dimensions=this.adapter.getInnerDimensions(),this.autoposition(),this.adapter.notifyOpen()):(this.adapter.addClass(o.cssClasses.ANIMATING_OPEN),this.animationRequestId=requestAnimationFrame((function(){e.dimensions=e.adapter.getInnerDimensions(),e.autoposition(),e.adapter.addClass(o.cssClasses.OPEN),e.openAnimationEndTimerId=setTimeout((function(){e.openAnimationEndTimerId=0,e.adapter.removeClass(o.cssClasses.ANIMATING_OPEN),e.adapter.notifyOpen()}),B.TRANSITION_OPEN_DURATION)})),this.isSurfaceOpen=!0))},o.prototype.close=function(e){var t=this;if(void 0===e&&(e=!1),this.isSurfaceOpen){if(this.adapter.notifyClosing(),this.isQuickOpen)return this.isSurfaceOpen=!1,e||this.maybeRestoreFocus(),this.adapter.removeClass(o.cssClasses.OPEN),this.adapter.removeClass(o.cssClasses.IS_OPEN_BELOW),void this.adapter.notifyClose();this.adapter.addClass(o.cssClasses.ANIMATING_CLOSED),requestAnimationFrame((function(){t.adapter.removeClass(o.cssClasses.OPEN),t.adapter.removeClass(o.cssClasses.IS_OPEN_BELOW),t.closeAnimationEndTimerId=setTimeout((function(){t.closeAnimationEndTimerId=0,t.adapter.removeClass(o.cssClasses.ANIMATING_CLOSED),t.adapter.notifyClose()}),B.TRANSITION_CLOSE_DURATION)})),this.isSurfaceOpen=!1,e||this.maybeRestoreFocus()}},o.prototype.handleBodyClick=function(e){var t=e.target;this.adapter.isElementInContainer(t)||this.close()},o.prototype.handleKeydown=function(e){var t=e.keyCode;("Escape"===e.key||27===t)&&this.close()},o.prototype.autoposition=function(){var e;this.measurements=this.getAutoLayoutmeasurements();var t=this.getoriginCorner(),i=this.getMenuSurfaceMaxHeight(t),c=this.hasBit(t,A.BOTTOM)?"bottom":"top",r=this.hasBit(t,A.RIGHT)?"right":"left",n=this.getHorizontalOriginOffset(t),a=this.getVerticalOriginOffset(t),d=this.measurements,s=d.anchorSize,p=d.surfaceSize,l=((e={})[r]=n,e[c]=a,e);s.width/p.width>B.ANCHOR_TO_MENU_SURFACE_WIDTH_RATIO&&(r="center"),(this.isHoistedElement||this.isFixedPosition)&&this.adjustPositionForHoistedElement(l),this.adapter.setTransformOrigin(r+" "+c),this.adapter.setPosition(l),this.adapter.setMaxHeight(i?i+"px":""),this.hasBit(t,A.BOTTOM)||this.adapter.addClass(o.cssClasses.IS_OPEN_BELOW)},o.prototype.getAutoLayoutmeasurements=function(){var e=this.adapter.getAnchorDimensions(),t=this.adapter.getBodyDimensions(),i=this.adapter.getWindowDimensions(),c=this.adapter.getWindowScroll();return e||(e={top:this.position.y,right:this.position.x,bottom:this.position.y,left:this.position.x,width:0,height:0}),{anchorSize:e,bodySize:t,surfaceSize:this.dimensions,viewportDistance:{top:e.top,right:i.width-e.right,bottom:i.height-e.bottom,left:e.left},viewportSize:i,windowScroll:c}},o.prototype.getoriginCorner=function(){var e,t,i=this.originCorner,c=this.measurements,r=c.viewportDistance,n=c.anchorSize,a=c.surfaceSize,d=o.numbers.MARGIN_TO_EDGE;this.hasBit(this.anchorCorner,A.BOTTOM)?(e=r.top-d+this.anchorMargin.bottom,t=r.bottom-d-this.anchorMargin.bottom):(e=r.top-d+this.anchorMargin.top,t=r.bottom-d+n.height-this.anchorMargin.top),!(t-a.height>0)&&e>t+this.openBottomBias&&(i=this.setBit(i,A.BOTTOM));var s,p,l=this.adapter.isRtl(),m=this.hasBit(this.anchorCorner,A.FLIP_RTL),h=this.hasBit(this.anchorCorner,A.RIGHT)||this.hasBit(i,A.RIGHT),u=!1;(u=l&&m?!h:h)?(s=r.left+n.width+this.anchorMargin.right,p=r.right-this.anchorMargin.right):(s=r.left+this.anchorMargin.left,p=r.right+n.width-this.anchorMargin.left);var f=s-a.width>0,b=p-a.width>0,g=this.hasBit(i,A.FLIP_RTL)&&this.hasBit(i,A.RIGHT);return b&&g&&l||!f&&g?i=this.unsetBit(i,A.RIGHT):(f&&u&&l||f&&!u&&h||!b&&s>=p)&&(i=this.setBit(i,A.RIGHT)),i},o.prototype.getMenuSurfaceMaxHeight=function(e){if(this.maxHeight>0)return this.maxHeight;var t=this.measurements.viewportDistance,i=0,c=this.hasBit(e,A.BOTTOM),r=this.hasBit(this.anchorCorner,A.BOTTOM),n=o.numbers.MARGIN_TO_EDGE;return c?(i=t.top+this.anchorMargin.top-n,r||(i+=this.measurements.anchorSize.height)):(i=t.bottom-this.anchorMargin.bottom+this.measurements.anchorSize.height-n,r&&(i-=this.measurements.anchorSize.height)),i},o.prototype.getHorizontalOriginOffset=function(e){var t=this.measurements.anchorSize,i=this.hasBit(e,A.RIGHT),c=this.hasBit(this.anchorCorner,A.RIGHT);if(i){var o=c?t.width-this.anchorMargin.left:this.anchorMargin.right;return this.isHoistedElement||this.isFixedPosition?o-(this.measurements.viewportSize.width-this.measurements.bodySize.width):o}return c?t.width-this.anchorMargin.right:this.anchorMargin.left},o.prototype.getVerticalOriginOffset=function(e){var t=this.measurements.anchorSize,i=this.hasBit(e,A.BOTTOM),c=this.hasBit(this.anchorCorner,A.BOTTOM);return i?c?t.height-this.anchorMargin.top:-this.anchorMargin.bottom:c?t.height+this.anchorMargin.bottom:this.anchorMargin.top},o.prototype.adjustPositionForHoistedElement=function(e){var t,c,o=this.measurements,r=o.windowScroll,n=o.viewportDistance,a=o.surfaceSize,d=o.viewportSize,s=Object.keys(e);try{for(var p=i(s),l=p.next();!l.done;l=p.next()){var m=l.value,h=e[m]||0;!this.isHorizontallyCenteredOnViewport||"left"!==m&&"right"!==m?(h+=n[m],this.isFixedPosition||("top"===m?h+=r.y:"bottom"===m?h-=r.y:"left"===m?h+=r.x:h-=r.x),e[m]=h):e[m]=(d.width-a.width)/2}}catch(e){t={error:e}}finally{try{l&&!l.done&&(c=p.return)&&c.call(p)}finally{if(t)throw t.error}}},o.prototype.maybeRestoreFocus=function(){var e=this,t=this.adapter.isFocused(),i=document.activeElement&&this.adapter.isElementInContainer(document.activeElement);(t||i)&&setTimeout((function(){e.adapter.restoreFocus()}),B.TOUCH_EVENT_WAIT_MS)},o.prototype.hasBit=function(e,t){return Boolean(e&t)},o.prototype.setBit=function(e,t){return e|t},o.prototype.unsetBit=function(e,t){return e^t},o.prototype.isFinite=function(e){return"number"==typeof e&&isFinite(e)},o}(c),z=M;const L={TOP_LEFT:R.TOP_LEFT,TOP_RIGHT:R.TOP_RIGHT,BOTTOM_LEFT:R.BOTTOM_LEFT,BOTTOM_RIGHT:R.BOTTOM_RIGHT,TOP_START:R.TOP_START,TOP_END:R.TOP_END,BOTTOM_START:R.BOTTOM_START,BOTTOM_END:R.BOTTOM_END};class N extends d{constructor(){super(...arguments),this.mdcFoundationClass=z,this.absolute=!1,this.fullwidth=!1,this.fixed=!1,this.x=null,this.y=null,this.quick=!1,this.open=!1,this.stayOpenOnBodyClick=!1,this.bitwiseCorner=R.TOP_START,this.previousMenuCorner=null,this.menuCorner="START",this.corner="TOP_START",this.styleTop="",this.styleLeft="",this.styleRight="",this.styleBottom="",this.styleMaxHeight="",this.styleTransformOrigin="",this.anchor=null,this.previouslyFocused=null,this.previousAnchor=null,this.onBodyClickBound=()=>{}}render(){const e={"mdc-menu-surface--fixed":this.fixed,"mdc-menu-surface--fullwidth":this.fullwidth},t={top:this.styleTop,left:this.styleLeft,right:this.styleRight,bottom:this.styleBottom,"max-height":this.styleMaxHeight,"transform-origin":this.styleTransformOrigin};return s`
`}createAdapter(){return Object.assign(Object.assign({},m(this.mdcRoot)),{hasAnchor:()=>!!this.anchor,notifyClose:()=>{const e=new CustomEvent("closed",{bubbles:!0,composed:!0});this.open=!1,this.mdcRoot.dispatchEvent(e)},notifyClosing:()=>{const e=new CustomEvent("closing",{bubbles:!0,composed:!0});this.mdcRoot.dispatchEvent(e)},notifyOpen:()=>{const e=new CustomEvent("opened",{bubbles:!0,composed:!0});this.open=!0,this.mdcRoot.dispatchEvent(e)},isElementInContainer:()=>!1,isRtl:()=>!!this.mdcRoot&&"rtl"===getComputedStyle(this.mdcRoot).direction,setTransformOrigin:e=>{this.mdcRoot&&(this.styleTransformOrigin=e)},isFocused:()=>h(this),saveFocus:()=>{const e=u(),t=e.length;t||(this.previouslyFocused=null),this.previouslyFocused=e[t-1]},restoreFocus:()=>{this.previouslyFocused&&"focus"in this.previouslyFocused&&this.previouslyFocused.focus()},getInnerDimensions:()=>{const e=this.mdcRoot;return e?{width:e.offsetWidth,height:e.offsetHeight}:{width:0,height:0}},getAnchorDimensions:()=>{const e=this.anchor;return e?e.getBoundingClientRect():null},getBodyDimensions:()=>({width:document.body.clientWidth,height:document.body.clientHeight}),getWindowDimensions:()=>({width:window.innerWidth,height:window.innerHeight}),getWindowScroll:()=>({x:window.pageXOffset,y:window.pageYOffset}),setPosition:e=>{this.mdcRoot&&(this.styleLeft="left"in e?`${e.left}px`:"",this.styleRight="right"in e?`${e.right}px`:"",this.styleTop="top"in e?`${e.top}px`:"",this.styleBottom="bottom"in e?`${e.bottom}px`:"")},setMaxHeight:async e=>{this.mdcRoot&&(this.styleMaxHeight=e,await this.updateComplete,this.styleMaxHeight=`var(--mdc-menu-max-height, ${e})`)}})}onKeydown(e){this.mdcFoundation&&this.mdcFoundation.handleKeydown(e)}onBodyClick(e){if(this.stayOpenOnBodyClick)return;-1===e.composedPath().indexOf(this)&&this.close()}registerBodyClick(){this.onBodyClickBound=this.onBodyClick.bind(this),document.body.addEventListener("click",this.onBodyClickBound,{passive:!0,capture:!0})}deregisterBodyClick(){document.body.removeEventListener("click",this.onBodyClickBound,{capture:!0})}close(){this.open=!1}show(){this.open=!0}}o([r(".mdc-menu-surface")],N.prototype,"mdcRoot",void 0),o([r("slot")],N.prototype,"slotElement",void 0),o([n({type:Boolean}),C((function(e){this.mdcFoundation&&!this.fixed&&this.mdcFoundation.setIsHoisted(e)}))],N.prototype,"absolute",void 0),o([n({type:Boolean})],N.prototype,"fullwidth",void 0),o([n({type:Boolean}),C((function(e){this.mdcFoundation&&!this.absolute&&this.mdcFoundation.setFixedPosition(e)}))],N.prototype,"fixed",void 0),o([n({type:Number}),C((function(e){this.mdcFoundation&&null!==this.y&&null!==e&&(this.mdcFoundation.setAbsolutePosition(e,this.y),this.mdcFoundation.setAnchorMargin({left:e,top:this.y,right:-e,bottom:this.y}))}))],N.prototype,"x",void 0),o([n({type:Number}),C((function(e){this.mdcFoundation&&null!==this.x&&null!==e&&(this.mdcFoundation.setAbsolutePosition(this.x,e),this.mdcFoundation.setAnchorMargin({left:this.x,top:e,right:-this.x,bottom:e}))}))],N.prototype,"y",void 0),o([n({type:Boolean}),C((function(e){this.mdcFoundation&&this.mdcFoundation.setQuickOpen(e)}))],N.prototype,"quick",void 0),o([n({type:Boolean,reflect:!0}),C((function(e,t){this.mdcFoundation&&(e?this.mdcFoundation.open():void 0!==t&&this.mdcFoundation.close())}))],N.prototype,"open",void 0),o([n({type:Boolean})],N.prototype,"stayOpenOnBodyClick",void 0),o([a(),C((function(e){this.mdcFoundation&&this.mdcFoundation.setAnchorCorner(e)}))],N.prototype,"bitwiseCorner",void 0),o([n({type:String}),C((function(e){if(this.mdcFoundation){const t="START"===e||"END"===e,i=null===this.previousMenuCorner,c=!i&&e!==this.previousMenuCorner,o=i&&"END"===e;t&&(c||o)&&(this.bitwiseCorner=this.bitwiseCorner^A.RIGHT,this.mdcFoundation.flipCornerHorizontally(),this.previousMenuCorner=e)}}))],N.prototype,"menuCorner",void 0),o([n({type:String}),C((function(e){if(this.mdcFoundation&&e){let t=L[e];"END"===this.menuCorner&&(t^=A.RIGHT),this.bitwiseCorner=t}}))],N.prototype,"corner",void 0),o([a()],N.prototype,"styleTop",void 0),o([a()],N.prototype,"styleLeft",void 0),o([a()],N.prototype,"styleRight",void 0),o([a()],N.prototype,"styleBottom",void 0),o([a()],N.prototype,"styleMaxHeight",void 0),o([a()],N.prototype,"styleTransformOrigin",void 0);const D=f`.mdc-menu-surface{display:none;position:absolute;box-sizing:border-box;max-width:calc(100vw - 32px);max-width:var(--mdc-menu-max-width, calc(100vw - 32px));max-height:calc(100vh - 32px);max-height:var(--mdc-menu-max-height, calc(100vh - 32px));margin:0;padding:0;transform:scale(1);transform-origin:top left;opacity:0;overflow:auto;will-change:transform,opacity;z-index:8;transition:opacity .03s linear,transform .12s cubic-bezier(0, 0, 0.2, 1),height 250ms cubic-bezier(0, 0, 0.2, 1);box-shadow:0px 5px 5px -3px rgba(0, 0, 0, 0.2),0px 8px 10px 1px rgba(0, 0, 0, 0.14),0px 3px 14px 2px rgba(0,0,0,.12);background-color:#fff;background-color:var(--mdc-theme-surface, #fff);color:#000;color:var(--mdc-theme-on-surface, #000);border-radius:4px;border-radius:var(--mdc-shape-medium, 4px);transform-origin-left:top left;transform-origin-right:top right}.mdc-menu-surface:focus{outline:none}.mdc-menu-surface--animating-open{display:inline-block;transform:scale(0.8);opacity:0}.mdc-menu-surface--open{display:inline-block;transform:scale(1);opacity:1}.mdc-menu-surface--animating-closed{display:inline-block;opacity:0;transition:opacity .075s linear}[dir=rtl] .mdc-menu-surface,.mdc-menu-surface[dir=rtl]{transform-origin-left:top right;transform-origin-right:top left}.mdc-menu-surface--anchor{position:relative;overflow:visible}.mdc-menu-surface--fixed{position:fixed}.mdc-menu-surface--fullwidth{width:100%}:host(:not([open])){display:none}.mdc-menu-surface{z-index:8;z-index:var(--mdc-menu-z-index, 8);min-width:112px;min-width:var(--mdc-menu-min-width, 112px)}`;let H=class extends N{};H.styles=[D],H=o([b("mwc-menu-surface")],H);var P,$={MENU_SELECTED_LIST_ITEM:"mdc-menu-item--selected",MENU_SELECTION_GROUP:"mdc-menu__selection-group",ROOT:"mdc-menu"},G={ARIA_CHECKED_ATTR:"aria-checked",ARIA_DISABLED_ATTR:"aria-disabled",CHECKBOX_SELECTOR:'input[type="checkbox"]',LIST_SELECTOR:".mdc-list,.mdc-deprecated-list",SELECTED_EVENT:"MDCMenu:selected",SKIP_RESTORE_FOCUS:"data-menu-item-skip-restore-focus"},U={FOCUS_ROOT_INDEX:-1};!function(e){e[e.NONE=0]="NONE",e[e.LIST_ROOT=1]="LIST_ROOT",e[e.FIRST_ITEM=2]="FIRST_ITEM",e[e.LAST_ITEM=3]="LAST_ITEM"}(P||(P={}));var j=function(i){function c(e){var o=i.call(this,t(t({},c.defaultAdapter),e))||this;return o.closeAnimationEndTimerId=0,o.defaultFocusState=P.LIST_ROOT,o.selectedIndex=-1,o}return e(c,i),Object.defineProperty(c,"cssClasses",{get:function(){return $},enumerable:!1,configurable:!0}),Object.defineProperty(c,"strings",{get:function(){return G},enumerable:!1,configurable:!0}),Object.defineProperty(c,"numbers",{get:function(){return U},enumerable:!1,configurable:!0}),Object.defineProperty(c,"defaultAdapter",{get:function(){return{addClassToElementAtIndex:function(){},removeClassFromElementAtIndex:function(){},addAttributeToElementAtIndex:function(){},removeAttributeFromElementAtIndex:function(){},getAttributeFromElementAtIndex:function(){return null},elementContainsClass:function(){return!1},closeSurface:function(){},getElementIndex:function(){return-1},notifySelected:function(){},getMenuItemCount:function(){return 0},focusItemAtIndex:function(){},focusListRoot:function(){},getSelectedSiblingOfItemAtIndex:function(){return-1},isSelectableItemAtIndex:function(){return!1}}},enumerable:!1,configurable:!0}),c.prototype.destroy=function(){this.closeAnimationEndTimerId&&clearTimeout(this.closeAnimationEndTimerId),this.adapter.closeSurface()},c.prototype.handleKeydown=function(e){var t=e.key,i=e.keyCode;("Tab"===t||9===i)&&this.adapter.closeSurface(!0)},c.prototype.handleItemAction=function(e){var t=this,i=this.adapter.getElementIndex(e);if(!(i<0)){this.adapter.notifySelected({index:i});var c="true"===this.adapter.getAttributeFromElementAtIndex(i,G.SKIP_RESTORE_FOCUS);this.adapter.closeSurface(c),this.closeAnimationEndTimerId=setTimeout((function(){var i=t.adapter.getElementIndex(e);i>=0&&t.adapter.isSelectableItemAtIndex(i)&&t.setSelectedIndex(i)}),M.numbers.TRANSITION_CLOSE_DURATION)}},c.prototype.handleMenuSurfaceOpened=function(){switch(this.defaultFocusState){case P.FIRST_ITEM:this.adapter.focusItemAtIndex(0);break;case P.LAST_ITEM:this.adapter.focusItemAtIndex(this.adapter.getMenuItemCount()-1);break;case P.NONE:break;default:this.adapter.focusListRoot()}},c.prototype.setDefaultFocusState=function(e){this.defaultFocusState=e},c.prototype.getSelectedIndex=function(){return this.selectedIndex},c.prototype.setSelectedIndex=function(e){if(this.validatedIndex(e),!this.adapter.isSelectableItemAtIndex(e))throw new Error("MDCMenuFoundation: No selection group at specified index.");var t=this.adapter.getSelectedSiblingOfItemAtIndex(e);t>=0&&(this.adapter.removeAttributeFromElementAtIndex(t,G.ARIA_CHECKED_ATTR),this.adapter.removeClassFromElementAtIndex(t,$.MENU_SELECTED_LIST_ITEM)),this.adapter.addClassToElementAtIndex(e,$.MENU_SELECTED_LIST_ITEM),this.adapter.addAttributeToElementAtIndex(e,G.ARIA_CHECKED_ATTR,"true"),this.selectedIndex=e},c.prototype.setEnabled=function(e,t){this.validatedIndex(e),t?(this.adapter.removeClassFromElementAtIndex(e,O.LIST_ITEM_DISABLED_CLASS),this.adapter.addAttributeToElementAtIndex(e,G.ARIA_DISABLED_ATTR,"false")):(this.adapter.addClassToElementAtIndex(e,O.LIST_ITEM_DISABLED_CLASS),this.adapter.addAttributeToElementAtIndex(e,G.ARIA_DISABLED_ATTR,"true"))},c.prototype.validatedIndex=function(e){var t=this.adapter.getMenuItemCount();if(!(e>=0&&e `}createAdapter(){return{addClassToElementAtIndex:(e,t)=>{const i=this.listElement;if(!i)return;const c=i.items[e];c&&("mdc-menu-item--selected"===t?this.forceGroupSelection&&!c.selected&&i.toggle(e,!0):c.classList.add(t))},removeClassFromElementAtIndex:(e,t)=>{const i=this.listElement;if(!i)return;const c=i.items[e];c&&("mdc-menu-item--selected"===t?c.selected&&i.toggle(e,!1):c.classList.remove(t))},addAttributeToElementAtIndex:(e,t,i)=>{const c=this.listElement;if(!c)return;const o=c.items[e];o&&o.setAttribute(t,i)},removeAttributeFromElementAtIndex:(e,t)=>{const i=this.listElement;if(!i)return;const c=i.items[e];c&&c.removeAttribute(t)},getAttributeFromElementAtIndex:(e,t)=>{const i=this.listElement;if(!i)return null;const c=i.items[e];return c?c.getAttribute(t):null},elementContainsClass:(e,t)=>e.classList.contains(t),closeSurface:()=>{this.open=!1},getElementIndex:e=>{const t=this.listElement;return t?t.items.indexOf(e):-1},notifySelected:()=>{},getMenuItemCount:()=>{const e=this.listElement;return e?e.items.length:0},focusItemAtIndex:e=>{const t=this.listElement;if(!t)return;const i=t.items[e];i&&i.focus()},focusListRoot:()=>{this.listElement&&this.listElement.focus()},getSelectedSiblingOfItemAtIndex:e=>{const t=this.listElement;if(!t)return-1;const i=t.items[e];if(!i||!i.group)return-1;for(let c=0;c{const t=this.listElement;if(!t)return!1;const i=t.items[e];return!!i&&i.hasAttribute("group")}}}onKeydown(e){this.mdcFoundation&&this.mdcFoundation.handleKeydown(e)}onAction(e){const t=this.listElement;if(this.mdcFoundation&&t){const i=e.detail.index,c=t.items[i];c&&this.mdcFoundation.handleItemAction(c)}}onOpened(){this.open=!0,this.mdcFoundation&&this.mdcFoundation.handleMenuSurfaceOpened()}onClosed(){this.open=!1}async getUpdateComplete(){await this._listUpdateComplete;return await super.getUpdateComplete()}async firstUpdated(){super.firstUpdated();const e=this.listElement;e&&(this._listUpdateComplete=e.updateComplete,await this._listUpdateComplete)}select(e){const t=this.listElement;t&&t.select(e)}close(){this.open=!1}show(){this.open=!0}getFocusedItemIndex(){const e=this.listElement;return e?e.getFocusedItemIndex():-1}focusItemAtIndex(e){const t=this.listElement;t&&t.focusItemAtIndex(e)}layout(e=!0){const t=this.listElement;t&&t.layout(e)}}o([r(".mdc-menu")],W.prototype,"mdcRoot",void 0),o([r("slot")],W.prototype,"slotElement",void 0),o([n({type:Object})],W.prototype,"anchor",void 0),o([n({type:Boolean,reflect:!0})],W.prototype,"open",void 0),o([n({type:Boolean})],W.prototype,"quick",void 0),o([n({type:Boolean})],W.prototype,"wrapFocus",void 0),o([n({type:String})],W.prototype,"innerRole",void 0),o([n({type:String})],W.prototype,"innerAriaLabel",void 0),o([n({type:String})],W.prototype,"corner",void 0),o([n({type:Number})],W.prototype,"x",void 0),o([n({type:Number})],W.prototype,"y",void 0),o([n({type:Boolean})],W.prototype,"absolute",void 0),o([n({type:Boolean})],W.prototype,"multi",void 0),o([n({type:Boolean})],W.prototype,"activatable",void 0),o([n({type:Boolean})],W.prototype,"fixed",void 0),o([n({type:Boolean})],W.prototype,"forceGroupSelection",void 0),o([n({type:Boolean})],W.prototype,"fullwidth",void 0),o([n({type:String})],W.prototype,"menuCorner",void 0),o([n({type:Boolean})],W.prototype,"stayOpenOnBodyClick",void 0),o([n({type:String}),C((function(e){this.mdcFoundation&&this.mdcFoundation.setDefaultFocusState(P[e])}))],W.prototype,"defaultFocus",void 0);const q=f`mwc-list ::slotted([mwc-list-item]:not([twoline])),mwc-list ::slotted([noninteractive]:not([twoline])){height:var(--mdc-menu-item-height, 48px)}`;let K=class extends W{};var V,X;K.styles=[q],K=o([b("mwc-menu")],K);const Q=null!==(X=null===(V=window.ShadyDOM)||void 0===V?void 0:V.inUse)&&void 0!==X&&X;class Y extends d{constructor(){super(...arguments),this.disabled=!1,this.containingForm=null,this.formDataListener=e=>{this.disabled||this.setFormData(e.formData)}}findFormElement(){if(!this.shadowRoot||Q)return null;const e=this.getRootNode().querySelectorAll("form");for(const t of Array.from(e))if(t.contains(this))return t;return null}connectedCallback(){var e;super.connectedCallback(),this.containingForm=this.findFormElement(),null===(e=this.containingForm)||void 0===e||e.addEventListener("formdata",this.formDataListener)}disconnectedCallback(){var e;super.disconnectedCallback(),null===(e=this.containingForm)||void 0===e||e.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",(e=>{this.dispatchEvent(new Event("change",e))}))}}Y.shadowRootOptions={mode:"open",delegatesFocus:!0},o([n({type:Boolean})],Y.prototype,"disabled",void 0);var J='/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://github.com/material-components/material-components-web/blob/master/LICENSE\n */\n.mdc-touch-target-wrapper{display:inline}.mdc-deprecated-chip-trailing-action__touch{position:absolute;top:50%;height:48px;left:50%;width:48px;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}.mdc-deprecated-chip-trailing-action{border:none;display:inline-flex;position:relative;align-items:center;justify-content:center;box-sizing:border-box;padding:0;outline:none;cursor:pointer;-webkit-appearance:none;background:none}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__icon{height:18px;width:18px;font-size:18px}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__touch{width:26px}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__icon{fill:currentColor;color:inherit}@-webkit-keyframes mdc-ripple-fg-radius-in{from{-webkit-animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{-webkit-transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-radius-in{from{-webkit-animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);-webkit-transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{-webkit-transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@-webkit-keyframes mdc-ripple-fg-opacity-in{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-in{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@-webkit-keyframes mdc-ripple-fg-opacity-out{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}@keyframes mdc-ripple-fg-opacity-out{from{-webkit-animation-timing-function:linear;animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-deprecated-chip-trailing-action{--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-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::before,.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded .mdc-deprecated-chip-trailing-action__ripple::before{-webkit-transform:scale(var(--mdc-ripple-fg-scale, 1));transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded .mdc-deprecated-chip-trailing-action__ripple::after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded--unbounded .mdc-deprecated-chip-trailing-action__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded--foreground-activation .mdc-deprecated-chip-trailing-action__ripple::after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded--foreground-deactivation .mdc-deprecated-chip-trailing-action__ripple::after{-webkit-animation:mdc-ripple-fg-opacity-out 150ms;animation:mdc-ripple-fg-opacity-out 150ms;-webkit-transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::before,.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::after{top:calc(50% - 50%);left:calc(50% - 50%);width:100%;height:100%}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded .mdc-deprecated-chip-trailing-action__ripple::before,.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded .mdc-deprecated-chip-trailing-action__ripple::after{top:var(--mdc-ripple-top, calc(50% - 50%));left:var(--mdc-ripple-left, calc(50% - 50%));width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded .mdc-deprecated-chip-trailing-action__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::before,.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-deprecated-chip-trailing-action:hover .mdc-deprecated-chip-trailing-action__ripple::before,.mdc-deprecated-chip-trailing-action.mdc-ripple-surface--hover .mdc-deprecated-chip-trailing-action__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded--background-focused .mdc-deprecated-chip-trailing-action__ripple::before,.mdc-deprecated-chip-trailing-action:not(.mdc-ripple-upgraded):focus .mdc-deprecated-chip-trailing-action__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-deprecated-chip-trailing-action:not(.mdc-ripple-upgraded) .mdc-deprecated-chip-trailing-action__ripple::after{transition:opacity 150ms linear}.mdc-deprecated-chip-trailing-action:not(.mdc-ripple-upgraded):active .mdc-deprecated-chip-trailing-action__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-deprecated-chip-trailing-action.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-deprecated-chip-trailing-action .mdc-deprecated-chip-trailing-action__ripple{position:absolute;box-sizing:content-box;width:100%;height:100%;overflow:hidden}.mdc-chip__icon--leading{color:rgba(0,0,0,.54)}.mdc-deprecated-chip-trailing-action{color:#000}.mdc-chip__icon--trailing{color:rgba(0,0,0,.54)}.mdc-chip__icon--trailing:hover{color:rgba(0,0,0,.62)}.mdc-chip__icon--trailing:focus{color:rgba(0,0,0,.87)}.mdc-chip__icon.mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden){width:20px;height:20px;font-size:20px}.mdc-deprecated-chip-trailing-action__icon{height:18px;width:18px;font-size:18px}.mdc-chip__icon.mdc-chip__icon--trailing{width:18px;height:18px;font-size:18px}.mdc-deprecated-chip-trailing-action{margin-left:4px;margin-right:-4px}[dir=rtl] .mdc-deprecated-chip-trailing-action,.mdc-deprecated-chip-trailing-action[dir=rtl]{margin-left:-4px;margin-right:4px}.mdc-chip__icon--trailing{margin-left:4px;margin-right:-4px}[dir=rtl] .mdc-chip__icon--trailing,.mdc-chip__icon--trailing[dir=rtl]{margin-left:-4px;margin-right:4px}.mdc-elevation-overlay{position:absolute;border-radius:inherit;pointer-events:none;opacity:0;opacity:var(--mdc-elevation-overlay-opacity, 0);transition:opacity 280ms cubic-bezier(0.4, 0, 0.2, 1);background-color:#fff;background-color:var(--mdc-elevation-overlay-color, #fff)}.mdc-chip{border-radius:16px;background-color:#e0e0e0;color:rgba(0, 0, 0, 0.87);-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-body2-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.875rem;font-size:var(--mdc-typography-body2-font-size, 0.875rem);line-height:1.25rem;line-height:var(--mdc-typography-body2-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-body2-font-weight, 400);letter-spacing:0.0178571429em;letter-spacing:var(--mdc-typography-body2-letter-spacing, 0.0178571429em);text-decoration:inherit;-webkit-text-decoration:var(--mdc-typography-body2-text-decoration, inherit);text-decoration:var(--mdc-typography-body2-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-body2-text-transform, inherit);height:32px;position:relative;display:inline-flex;align-items:center;box-sizing:border-box;padding:0 12px;border-width:0;outline:none;cursor:pointer;-webkit-appearance:none}.mdc-chip .mdc-chip__ripple{border-radius:16px}.mdc-chip:hover{color:rgba(0, 0, 0, 0.87)}.mdc-chip.mdc-chip--selected .mdc-chip__checkmark,.mdc-chip .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden){margin-left:-4px;margin-right:4px}[dir=rtl] .mdc-chip.mdc-chip--selected .mdc-chip__checkmark,[dir=rtl] .mdc-chip .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden),.mdc-chip.mdc-chip--selected .mdc-chip__checkmark[dir=rtl],.mdc-chip .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden)[dir=rtl]{margin-left:4px;margin-right:-4px}.mdc-chip .mdc-elevation-overlay{width:100%;height:100%;top:0;left:0}.mdc-chip::-moz-focus-inner{padding:0;border:0}.mdc-chip:hover{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-chip .mdc-chip__touch{position:absolute;top:50%;height:48px;left:0;right:0;-webkit-transform:translateY(-50%);transform:translateY(-50%)}.mdc-chip--exit{transition:opacity 75ms cubic-bezier(0.4, 0, 0.2, 1),width 150ms cubic-bezier(0, 0, 0.2, 1),padding 100ms linear,margin 100ms linear;opacity:0}.mdc-chip__overflow{text-overflow:ellipsis;overflow:hidden}.mdc-chip__text{white-space:nowrap}.mdc-chip__icon{border-radius:50%;outline:none;vertical-align:middle}.mdc-chip__checkmark{height:20px}.mdc-chip__checkmark-path{transition:stroke-dashoffset 150ms 50ms cubic-bezier(0.4, 0, 0.6, 1);stroke-width:2px;stroke-dashoffset:29.7833385;stroke-dasharray:29.7833385}.mdc-chip__primary-action:focus{outline:none}.mdc-chip--selected .mdc-chip__checkmark-path{stroke-dashoffset:0}.mdc-chip__icon--leading,.mdc-chip__icon--trailing{position:relative}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected .mdc-chip__icon--leading{color:rgba(98,0,238,.54)}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected:hover{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}.mdc-chip-set--choice .mdc-chip .mdc-chip__checkmark-path{stroke:#6200ee;stroke:var(--mdc-theme-primary, #6200ee)}.mdc-chip-set--choice .mdc-chip--selected{background-color:#fff;background-color:var(--mdc-theme-surface, #fff)}.mdc-chip__checkmark-svg{width:0;height:20px;transition:width 150ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-chip--selected .mdc-chip__checkmark-svg{width:20px}.mdc-chip-set--filter .mdc-chip__icon--leading{transition:opacity 75ms linear;transition-delay:-50ms;opacity:1}.mdc-chip-set--filter .mdc-chip__icon--leading+.mdc-chip__checkmark{transition:opacity 75ms linear;transition-delay:80ms;opacity:0}.mdc-chip-set--filter .mdc-chip__icon--leading+.mdc-chip__checkmark .mdc-chip__checkmark-svg{transition:width 0ms}.mdc-chip-set--filter .mdc-chip--selected .mdc-chip__icon--leading{opacity:0}.mdc-chip-set--filter .mdc-chip--selected .mdc-chip__icon--leading+.mdc-chip__checkmark{width:0;opacity:1}.mdc-chip-set--filter .mdc-chip__icon--leading-hidden.mdc-chip__icon--leading{width:0;opacity:0}.mdc-chip-set--filter .mdc-chip__icon--leading-hidden.mdc-chip__icon--leading+.mdc-chip__checkmark{width:20px}.mdc-chip{--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-chip .mdc-chip__ripple::before,.mdc-chip .mdc-chip__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-chip .mdc-chip__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-chip .mdc-chip__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-chip.mdc-ripple-upgraded .mdc-chip__ripple::before{-webkit-transform:scale(var(--mdc-ripple-fg-scale, 1));transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-chip.mdc-ripple-upgraded .mdc-chip__ripple::after{top:0;left:0;-webkit-transform:scale(0);transform:scale(0);-webkit-transform-origin:center center;transform-origin:center center}.mdc-chip.mdc-ripple-upgraded--unbounded .mdc-chip__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-chip.mdc-ripple-upgraded--foreground-activation .mdc-chip__ripple::after{-webkit-animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards;animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-chip.mdc-ripple-upgraded--foreground-deactivation .mdc-chip__ripple::after{-webkit-animation:mdc-ripple-fg-opacity-out 150ms;animation:mdc-ripple-fg-opacity-out 150ms;-webkit-transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1));transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-chip .mdc-chip__ripple::before,.mdc-chip .mdc-chip__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-chip.mdc-ripple-upgraded .mdc-chip__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-chip .mdc-chip__ripple::before,.mdc-chip .mdc-chip__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-chip:hover .mdc-chip__ripple::before,.mdc-chip.mdc-ripple-surface--hover .mdc-chip__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-chip.mdc-ripple-upgraded--background-focused .mdc-chip__ripple::before,.mdc-chip.mdc-ripple-upgraded:focus-within .mdc-chip__ripple::before,.mdc-chip:not(.mdc-ripple-upgraded):focus .mdc-chip__ripple::before,.mdc-chip:not(.mdc-ripple-upgraded):focus-within .mdc-chip__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-chip:not(.mdc-ripple-upgraded) .mdc-chip__ripple::after{transition:opacity 150ms linear}.mdc-chip:not(.mdc-ripple-upgraded):active .mdc-chip__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-chip.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-chip .mdc-chip__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;overflow:hidden}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected .mdc-chip__ripple::before{opacity:0.08;opacity:var(--mdc-ripple-selected-opacity, 0.08)}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected .mdc-chip__ripple::before,.mdc-chip-set--choice .mdc-chip.mdc-chip--selected .mdc-chip__ripple::after{background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected:hover .mdc-chip__ripple::before,.mdc-chip-set--choice .mdc-chip.mdc-chip--selected.mdc-ripple-surface--hover .mdc-chip__ripple::before{opacity:0.12;opacity:var(--mdc-ripple-hover-opacity, 0.12)}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected.mdc-ripple-upgraded--background-focused .mdc-chip__ripple::before,.mdc-chip-set--choice .mdc-chip.mdc-chip--selected.mdc-ripple-upgraded:focus-within .mdc-chip__ripple::before,.mdc-chip-set--choice .mdc-chip.mdc-chip--selected:not(.mdc-ripple-upgraded):focus .mdc-chip__ripple::before,.mdc-chip-set--choice .mdc-chip.mdc-chip--selected:not(.mdc-ripple-upgraded):focus-within .mdc-chip__ripple::before{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-focus-opacity, 0.2)}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected:not(.mdc-ripple-upgraded) .mdc-chip__ripple::after{transition:opacity 150ms linear}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected:not(.mdc-ripple-upgraded):active .mdc-chip__ripple::after{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-chip-set--choice .mdc-chip.mdc-chip--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.2)}@-webkit-keyframes mdc-chip-entry{from{-webkit-transform:scale(0.8);transform:scale(0.8);opacity:.4}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes mdc-chip-entry{from{-webkit-transform:scale(0.8);transform:scale(0.8);opacity:.4}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.mdc-chip-set{padding:4px;display:flex;flex-wrap:wrap;box-sizing:border-box}.mdc-chip-set .mdc-chip{margin:4px}.mdc-chip-set .mdc-chip--touch{margin-top:8px;margin-bottom:8px}.mdc-chip-set--input .mdc-chip{-webkit-animation:mdc-chip-entry 100ms cubic-bezier(0, 0, 0.2, 1);animation:mdc-chip-entry 100ms cubic-bezier(0, 0, 0.2, 1)}\n\n/*# sourceMappingURL=mdc.chips.min.css.map*/';g([b("ha-chip")],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[{kind:"field",decorators:[n({type:Boolean})],key:"hasIcon",value:()=>!1},{kind:"field",decorators:[n({type:Boolean})],key:"hasTrailingIcon",value:()=>!1},{kind:"field",decorators:[n({type:Boolean})],key:"noText",value:()=>!1},{kind:"method",key:"render",value:function(){return s`
${this.hasIcon?s`
`:null}
${this.hasTrailingIcon?s`
`:null}
`}},{kind:"get",static:!0,key:"styles",value:function(){return f` ${x(J)} .mdc-chip { background-color: var( --ha-chip-background-color, rgba(var(--rgb-primary-text-color), 0.15) ); color: var(--ha-chip-text-color, var(--primary-text-color)); } .mdc-chip.no-text { padding: 0 10px; } .mdc-chip:hover { color: var(--ha-chip-text-color, var(--primary-text-color)); } .mdc-chip__icon--leading, .mdc-chip__icon--trailing { --mdc-icon-size: 18px; line-height: 14px; color: var(--ha-chip-icon-color, var(--ha-chip-text-color)); } .mdc-chip.mdc-chip--selected .mdc-chip__checkmark, .mdc-chip .mdc-chip__icon--leading:not(.mdc-chip__icon--leading-hidden) { margin-right: -4px; margin-inline-start: -4px; margin-inline-end: 4px; direction: var(--direction); } span[role="gridcell"] { line-height: 14px; } `}}]}}),k);class Z extends Y{constructor(){super(...arguments),this.checked=!1,this.indeterminate=!1,this.disabled=!1,this.name="",this.value="on",this.reducedTouchTarget=!1,this.animationClass="",this.shouldRenderRipple=!1,this.focused=!1,this.mdcFoundationClass=void 0,this.mdcFoundation=void 0,this.rippleElement=null,this.rippleHandlers=new T((()=>(this.shouldRenderRipple=!0,this.ripple.then((e=>this.rippleElement=e)),this.ripple)))}createAdapter(){return{}}update(e){const t=e.get("indeterminate"),i=e.get("checked"),c=e.get("disabled");if(void 0!==t||void 0!==i||void 0!==c){const e=this.calculateAnimationStateName(!!i,!!t,!!c),o=this.calculateAnimationStateName(this.checked,this.indeterminate,this.disabled);this.animationClass=`${e}-${o}`}super.update(e)}calculateAnimationStateName(e,t,i){return i?"disabled":t?"indeterminate":e?"checked":"unchecked"}renderRipple(){return this.shouldRenderRipple?this.renderRippleTemplate():""}renderRippleTemplate(){return s``}render(){const e=this.indeterminate||this.checked,t={"mdc-checkbox--disabled":this.disabled,"mdc-checkbox--selected":e,"mdc-checkbox--touch":!this.reducedTouchTarget,"mdc-ripple-upgraded--background-focused":this.focused,"mdc-checkbox--anim-checked-indeterminate":"checked-indeterminate"==this.animationClass,"mdc-checkbox--anim-checked-unchecked":"checked-unchecked"==this.animationClass,"mdc-checkbox--anim-indeterminate-checked":"indeterminate-checked"==this.animationClass,"mdc-checkbox--anim-indeterminate-unchecked":"indeterminate-unchecked"==this.animationClass,"mdc-checkbox--anim-unchecked-checked":"unchecked-checked"==this.animationClass,"mdc-checkbox--anim-unchecked-indeterminate":"unchecked-indeterminate"==this.animationClass},i=this.indeterminate?"mixed":void 0;return s`
${this.renderRipple()}
`}setFormData(e){this.name&&this.checked&&e.append(this.name,this.value)}handleFocus(){this.focused=!0,this.handleRippleFocus()}handleBlur(){this.focused=!1,this.handleRippleBlur()}handleRippleMouseDown(e){const t=()=>{window.removeEventListener("mouseup",t),this.handleRippleDeactivate()};window.addEventListener("mouseup",t),this.rippleHandlers.startPress(e)}handleRippleTouchStart(e){this.rippleHandlers.startPress(e)}handleRippleDeactivate(){this.rippleHandlers.endPress()}handleRippleMouseEnter(){this.rippleHandlers.startHover()}handleRippleMouseLeave(){this.rippleHandlers.endHover()}handleRippleFocus(){this.rippleHandlers.startFocus()}handleRippleBlur(){this.rippleHandlers.endFocus()}handleChange(){this.checked=this.formElement.checked,this.indeterminate=this.formElement.indeterminate}resetAnimationClass(){this.animationClass=""}get isRippleActive(){var e;return(null===(e=this.rippleElement)||void 0===e?void 0:e.isActive)||!1}}o([r(".mdc-checkbox")],Z.prototype,"mdcRoot",void 0),o([r("input")],Z.prototype,"formElement",void 0),o([n({type:Boolean,reflect:!0})],Z.prototype,"checked",void 0),o([n({type:Boolean})],Z.prototype,"indeterminate",void 0),o([n({type:Boolean,reflect:!0})],Z.prototype,"disabled",void 0),o([n({type:String,reflect:!0})],Z.prototype,"name",void 0),o([n({type:String})],Z.prototype,"value",void 0),o([_,n({type:String,attribute:"aria-label"})],Z.prototype,"ariaLabel",void 0),o([_,n({type:String,attribute:"aria-labelledby"})],Z.prototype,"ariaLabelledBy",void 0),o([_,n({type:String,attribute:"aria-describedby"})],Z.prototype,"ariaDescribedBy",void 0),o([n({type:Boolean})],Z.prototype,"reducedTouchTarget",void 0),o([a()],Z.prototype,"animationClass",void 0),o([a()],Z.prototype,"shouldRenderRipple",void 0),o([a()],Z.prototype,"focused",void 0),o([y("mwc-ripple")],Z.prototype,"ripple",void 0),o([v({passive:!0})],Z.prototype,"handleRippleTouchStart",null);const ee=f`.mdc-checkbox{padding:calc((40px - 18px) / 2);padding:calc((var(--mdc-checkbox-ripple-size, 40px) - 18px) / 2);margin:calc((40px - 40px) / 2);margin:calc((var(--mdc-checkbox-touch-target-size, 40px) - 40px) / 2)}.mdc-checkbox .mdc-checkbox__ripple::before,.mdc-checkbox .mdc-checkbox__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, #000)}.mdc-checkbox:hover .mdc-checkbox__ripple::before,.mdc-checkbox.mdc-ripple-surface--hover .mdc-checkbox__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-checkbox.mdc-ripple-upgraded--background-focused .mdc-checkbox__ripple::before,.mdc-checkbox:not(.mdc-ripple-upgraded):focus .mdc-checkbox__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-checkbox:not(.mdc-ripple-upgraded) .mdc-checkbox__ripple::after{transition:opacity 150ms linear}.mdc-checkbox:not(.mdc-ripple-upgraded):active .mdc-checkbox__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-checkbox.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-checkbox.mdc-checkbox--selected .mdc-checkbox__ripple::before,.mdc-checkbox.mdc-checkbox--selected .mdc-checkbox__ripple::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-checkbox.mdc-checkbox--selected:hover .mdc-checkbox__ripple::before,.mdc-checkbox.mdc-checkbox--selected.mdc-ripple-surface--hover .mdc-checkbox__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-checkbox.mdc-checkbox--selected.mdc-ripple-upgraded--background-focused .mdc-checkbox__ripple::before,.mdc-checkbox.mdc-checkbox--selected:not(.mdc-ripple-upgraded):focus .mdc-checkbox__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-checkbox.mdc-checkbox--selected:not(.mdc-ripple-upgraded) .mdc-checkbox__ripple::after{transition:opacity 150ms linear}.mdc-checkbox.mdc-checkbox--selected:not(.mdc-ripple-upgraded):active .mdc-checkbox__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-checkbox.mdc-checkbox--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-checkbox.mdc-ripple-upgraded--background-focused.mdc-checkbox--selected .mdc-checkbox__ripple::before,.mdc-checkbox.mdc-ripple-upgraded--background-focused.mdc-checkbox--selected .mdc-checkbox__ripple::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-checkbox .mdc-checkbox__background{top:calc((40px - 18px) / 2);top:calc((var(--mdc-checkbox-ripple-size, 40px) - 18px) / 2);left:calc((40px - 18px) / 2);left:calc((var(--mdc-checkbox-ripple-size, 40px) - 18px) / 2)}.mdc-checkbox .mdc-checkbox__native-control{top:calc((40px - 40px) / 2);top:calc((40px - var(--mdc-checkbox-touch-target-size, 40px)) / 2);right:calc((40px - 40px) / 2);right:calc((40px - var(--mdc-checkbox-touch-target-size, 40px)) / 2);left:calc((40px - 40px) / 2);left:calc((40px - var(--mdc-checkbox-touch-target-size, 40px)) / 2);width:40px;width:var(--mdc-checkbox-touch-target-size, 40px);height:40px;height:var(--mdc-checkbox-touch-target-size, 40px)}.mdc-checkbox .mdc-checkbox__native-control:enabled:not(:checked):not(:indeterminate):not([data-indeterminate=true])~.mdc-checkbox__background{border-color:rgba(0, 0, 0, 0.54);border-color:var(--mdc-checkbox-unchecked-color, rgba(0, 0, 0, 0.54));background-color:transparent}.mdc-checkbox .mdc-checkbox__native-control:enabled:checked~.mdc-checkbox__background,.mdc-checkbox .mdc-checkbox__native-control:enabled:indeterminate~.mdc-checkbox__background,.mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true]:enabled~.mdc-checkbox__background{border-color:#018786;border-color:var(--mdc-checkbox-checked-color, var(--mdc-theme-secondary, #018786));background-color:#018786;background-color:var(--mdc-checkbox-checked-color, var(--mdc-theme-secondary, #018786))}@keyframes mdc-checkbox-fade-in-background-8A000000FF01878600000000FF018786{0%{border-color:rgba(0, 0, 0, 0.54);border-color:var(--mdc-checkbox-unchecked-color, rgba(0, 0, 0, 0.54));background-color:transparent}50%{border-color:#018786;border-color:var(--mdc-checkbox-checked-color, var(--mdc-theme-secondary, #018786));background-color:#018786;background-color:var(--mdc-checkbox-checked-color, var(--mdc-theme-secondary, #018786))}}@keyframes mdc-checkbox-fade-out-background-8A000000FF01878600000000FF018786{0%,80%{border-color:#018786;border-color:var(--mdc-checkbox-checked-color, var(--mdc-theme-secondary, #018786));background-color:#018786;background-color:var(--mdc-checkbox-checked-color, var(--mdc-theme-secondary, #018786))}100%{border-color:rgba(0, 0, 0, 0.54);border-color:var(--mdc-checkbox-unchecked-color, rgba(0, 0, 0, 0.54));background-color:transparent}}.mdc-checkbox.mdc-checkbox--anim-unchecked-checked .mdc-checkbox__native-control:enabled~.mdc-checkbox__background,.mdc-checkbox.mdc-checkbox--anim-unchecked-indeterminate .mdc-checkbox__native-control:enabled~.mdc-checkbox__background{animation-name:mdc-checkbox-fade-in-background-8A000000FF01878600000000FF018786}.mdc-checkbox.mdc-checkbox--anim-checked-unchecked .mdc-checkbox__native-control:enabled~.mdc-checkbox__background,.mdc-checkbox.mdc-checkbox--anim-indeterminate-unchecked .mdc-checkbox__native-control:enabled~.mdc-checkbox__background{animation-name:mdc-checkbox-fade-out-background-8A000000FF01878600000000FF018786}.mdc-checkbox .mdc-checkbox__native-control[disabled]:not(:checked):not(:indeterminate):not([data-indeterminate=true])~.mdc-checkbox__background{border-color:rgba(0, 0, 0, 0.38);border-color:var(--mdc-checkbox-disabled-color, rgba(0, 0, 0, 0.38));background-color:transparent}.mdc-checkbox .mdc-checkbox__native-control[disabled]:checked~.mdc-checkbox__background,.mdc-checkbox .mdc-checkbox__native-control[disabled]:indeterminate~.mdc-checkbox__background,.mdc-checkbox .mdc-checkbox__native-control[data-indeterminate=true][disabled]~.mdc-checkbox__background{border-color:transparent;background-color:rgba(0, 0, 0, 0.38);background-color:var(--mdc-checkbox-disabled-color, rgba(0, 0, 0, 0.38))}.mdc-checkbox .mdc-checkbox__native-control:enabled~.mdc-checkbox__background .mdc-checkbox__checkmark{color:#fff;color:var(--mdc-checkbox-ink-color, #fff)}.mdc-checkbox .mdc-checkbox__native-control:enabled~.mdc-checkbox__background .mdc-checkbox__mixedmark{border-color:#fff;border-color:var(--mdc-checkbox-ink-color, #fff)}.mdc-checkbox .mdc-checkbox__native-control:disabled~.mdc-checkbox__background .mdc-checkbox__checkmark{color:#fff;color:var(--mdc-checkbox-ink-color, #fff)}.mdc-checkbox .mdc-checkbox__native-control:disabled~.mdc-checkbox__background .mdc-checkbox__mixedmark{border-color:#fff;border-color:var(--mdc-checkbox-ink-color, #fff)}.mdc-touch-target-wrapper{display:inline}@keyframes mdc-checkbox-unchecked-checked-checkmark-path{0%,50%{stroke-dashoffset:29.7833385}50%{animation-timing-function:cubic-bezier(0, 0, 0.2, 1)}100%{stroke-dashoffset:0}}@keyframes mdc-checkbox-unchecked-indeterminate-mixedmark{0%,68.2%{transform:scaleX(0)}68.2%{animation-timing-function:cubic-bezier(0, 0, 0, 1)}100%{transform:scaleX(1)}}@keyframes mdc-checkbox-checked-unchecked-checkmark-path{from{animation-timing-function:cubic-bezier(0.4, 0, 1, 1);opacity:1;stroke-dashoffset:0}to{opacity:0;stroke-dashoffset:-29.7833385}}@keyframes mdc-checkbox-checked-indeterminate-checkmark{from{animation-timing-function:cubic-bezier(0, 0, 0.2, 1);transform:rotate(0deg);opacity:1}to{transform:rotate(45deg);opacity:0}}@keyframes mdc-checkbox-indeterminate-checked-checkmark{from{animation-timing-function:cubic-bezier(0.14, 0, 0, 1);transform:rotate(45deg);opacity:0}to{transform:rotate(360deg);opacity:1}}@keyframes mdc-checkbox-checked-indeterminate-mixedmark{from{animation-timing-function:mdc-animation-deceleration-curve-timing-function;transform:rotate(-45deg);opacity:0}to{transform:rotate(0deg);opacity:1}}@keyframes mdc-checkbox-indeterminate-checked-mixedmark{from{animation-timing-function:cubic-bezier(0.14, 0, 0, 1);transform:rotate(0deg);opacity:1}to{transform:rotate(315deg);opacity:0}}@keyframes mdc-checkbox-indeterminate-unchecked-mixedmark{0%{animation-timing-function:linear;transform:scaleX(1);opacity:1}32.8%,100%{transform:scaleX(0);opacity:0}}.mdc-checkbox{display:inline-block;position:relative;flex:0 0 18px;box-sizing:content-box;width:18px;height:18px;line-height:0;white-space:nowrap;cursor:pointer;vertical-align:bottom}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-checkbox__native-control[disabled]:not(:checked):not(:indeterminate):not([data-indeterminate=true])~.mdc-checkbox__background{border-color:GrayText;border-color:var(--mdc-checkbox-disabled-color, GrayText);background-color:transparent}.mdc-checkbox__native-control[disabled]:checked~.mdc-checkbox__background,.mdc-checkbox__native-control[disabled]:indeterminate~.mdc-checkbox__background,.mdc-checkbox__native-control[data-indeterminate=true][disabled]~.mdc-checkbox__background{border-color:GrayText;background-color:transparent;background-color:var(--mdc-checkbox-disabled-color, transparent)}.mdc-checkbox__native-control:disabled~.mdc-checkbox__background .mdc-checkbox__checkmark{color:GrayText;color:var(--mdc-checkbox-ink-color, GrayText)}.mdc-checkbox__native-control:disabled~.mdc-checkbox__background .mdc-checkbox__mixedmark{border-color:GrayText;border-color:var(--mdc-checkbox-ink-color, GrayText)}.mdc-checkbox__mixedmark{margin:0 1px}}.mdc-checkbox--disabled{cursor:default;pointer-events:none}.mdc-checkbox__background{display:inline-flex;position:absolute;align-items:center;justify-content:center;box-sizing:border-box;width:18px;height:18px;border:2px solid currentColor;border-radius:2px;background-color:transparent;pointer-events:none;will-change:background-color,border-color;transition:background-color 90ms 0ms cubic-bezier(0.4, 0, 0.6, 1),border-color 90ms 0ms cubic-bezier(0.4, 0, 0.6, 1)}.mdc-checkbox__checkmark{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;opacity:0;transition:opacity 180ms 0ms cubic-bezier(0.4, 0, 0.6, 1)}.mdc-checkbox--upgraded .mdc-checkbox__checkmark{opacity:1}.mdc-checkbox__checkmark-path{transition:stroke-dashoffset 180ms 0ms cubic-bezier(0.4, 0, 0.6, 1);stroke:currentColor;stroke-width:3.12px;stroke-dashoffset:29.7833385;stroke-dasharray:29.7833385}.mdc-checkbox__mixedmark{width:100%;height:0;transform:scaleX(0) rotate(0deg);border-width:1px;border-style:solid;opacity:0;transition:opacity 90ms 0ms cubic-bezier(0.4, 0, 0.6, 1),transform 90ms 0ms cubic-bezier(0.4, 0, 0.6, 1)}.mdc-checkbox--anim-unchecked-checked .mdc-checkbox__background,.mdc-checkbox--anim-unchecked-indeterminate .mdc-checkbox__background,.mdc-checkbox--anim-checked-unchecked .mdc-checkbox__background,.mdc-checkbox--anim-indeterminate-unchecked .mdc-checkbox__background{animation-duration:180ms;animation-timing-function:linear}.mdc-checkbox--anim-unchecked-checked .mdc-checkbox__checkmark-path{animation:mdc-checkbox-unchecked-checked-checkmark-path 180ms linear 0s;transition:none}.mdc-checkbox--anim-unchecked-indeterminate .mdc-checkbox__mixedmark{animation:mdc-checkbox-unchecked-indeterminate-mixedmark 90ms linear 0s;transition:none}.mdc-checkbox--anim-checked-unchecked .mdc-checkbox__checkmark-path{animation:mdc-checkbox-checked-unchecked-checkmark-path 90ms linear 0s;transition:none}.mdc-checkbox--anim-checked-indeterminate .mdc-checkbox__checkmark{animation:mdc-checkbox-checked-indeterminate-checkmark 90ms linear 0s;transition:none}.mdc-checkbox--anim-checked-indeterminate .mdc-checkbox__mixedmark{animation:mdc-checkbox-checked-indeterminate-mixedmark 90ms linear 0s;transition:none}.mdc-checkbox--anim-indeterminate-checked .mdc-checkbox__checkmark{animation:mdc-checkbox-indeterminate-checked-checkmark 500ms linear 0s;transition:none}.mdc-checkbox--anim-indeterminate-checked .mdc-checkbox__mixedmark{animation:mdc-checkbox-indeterminate-checked-mixedmark 500ms linear 0s;transition:none}.mdc-checkbox--anim-indeterminate-unchecked .mdc-checkbox__mixedmark{animation:mdc-checkbox-indeterminate-unchecked-mixedmark 300ms linear 0s;transition:none}.mdc-checkbox__native-control:checked~.mdc-checkbox__background,.mdc-checkbox__native-control:indeterminate~.mdc-checkbox__background,.mdc-checkbox__native-control[data-indeterminate=true]~.mdc-checkbox__background{transition:border-color 90ms 0ms cubic-bezier(0, 0, 0.2, 1),background-color 90ms 0ms cubic-bezier(0, 0, 0.2, 1)}.mdc-checkbox__native-control:checked~.mdc-checkbox__background .mdc-checkbox__checkmark-path,.mdc-checkbox__native-control:indeterminate~.mdc-checkbox__background .mdc-checkbox__checkmark-path,.mdc-checkbox__native-control[data-indeterminate=true]~.mdc-checkbox__background .mdc-checkbox__checkmark-path{stroke-dashoffset:0}.mdc-checkbox__native-control{position:absolute;margin:0;padding:0;opacity:0;cursor:inherit}.mdc-checkbox__native-control:disabled{cursor:default;pointer-events:none}.mdc-checkbox--touch{margin:calc((48px - 40px) / 2);margin:calc((var(--mdc-checkbox-state-layer-size, 48px) - var(--mdc-checkbox-state-layer-size, 40px)) / 2)}.mdc-checkbox--touch .mdc-checkbox__native-control{top:calc((40px - 48px) / 2);top:calc((var(--mdc-checkbox-state-layer-size, 40px) - var(--mdc-checkbox-state-layer-size, 48px)) / 2);right:calc((40px - 48px) / 2);right:calc((var(--mdc-checkbox-state-layer-size, 40px) - var(--mdc-checkbox-state-layer-size, 48px)) / 2);left:calc((40px - 48px) / 2);left:calc((var(--mdc-checkbox-state-layer-size, 40px) - var(--mdc-checkbox-state-layer-size, 48px)) / 2);width:48px;width:var(--mdc-checkbox-state-layer-size, 48px);height:48px;height:var(--mdc-checkbox-state-layer-size, 48px)}.mdc-checkbox__native-control:checked~.mdc-checkbox__background .mdc-checkbox__checkmark{transition:opacity 180ms 0ms cubic-bezier(0, 0, 0.2, 1),transform 180ms 0ms cubic-bezier(0, 0, 0.2, 1);opacity:1}.mdc-checkbox__native-control:checked~.mdc-checkbox__background .mdc-checkbox__mixedmark{transform:scaleX(1) rotate(-45deg)}.mdc-checkbox__native-control:indeterminate~.mdc-checkbox__background .mdc-checkbox__checkmark,.mdc-checkbox__native-control[data-indeterminate=true]~.mdc-checkbox__background .mdc-checkbox__checkmark{transform:rotate(45deg);opacity:0;transition:opacity 90ms 0ms cubic-bezier(0.4, 0, 0.6, 1),transform 90ms 0ms cubic-bezier(0.4, 0, 0.6, 1)}.mdc-checkbox__native-control:indeterminate~.mdc-checkbox__background .mdc-checkbox__mixedmark,.mdc-checkbox__native-control[data-indeterminate=true]~.mdc-checkbox__background .mdc-checkbox__mixedmark{transform:scaleX(1) rotate(0deg);opacity:1}.mdc-checkbox.mdc-checkbox--upgraded .mdc-checkbox__background,.mdc-checkbox.mdc-checkbox--upgraded .mdc-checkbox__checkmark,.mdc-checkbox.mdc-checkbox--upgraded .mdc-checkbox__checkmark-path,.mdc-checkbox.mdc-checkbox--upgraded .mdc-checkbox__mixedmark{transition:none}:host{outline:none;display:inline-flex;-webkit-tap-highlight-color:transparent}:host([checked]),:host([indeterminate]){--mdc-ripple-color:var(--mdc-theme-secondary, #018786)}.mdc-checkbox .mdc-checkbox__background::before{content:none}`;g([b("ha-checkbox")],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[{kind:"field",static:!0,key:"styles",value:()=>[ee,f` :host { --mdc-theme-secondary: var(--primary-color); } `]}]}}),Z);var te={ROOT:"mdc-form-field"},ie={LABEL_SELECTOR:".mdc-form-field > label"},ce=function(i){function c(e){var o=i.call(this,t(t({},c.defaultAdapter),e))||this;return o.click=function(){o.handleClick()},o}return e(c,i),Object.defineProperty(c,"cssClasses",{get:function(){return te},enumerable:!1,configurable:!0}),Object.defineProperty(c,"strings",{get:function(){return ie},enumerable:!1,configurable:!0}),Object.defineProperty(c,"defaultAdapter",{get:function(){return{activateInputRipple:function(){},deactivateInputRipple:function(){},deregisterInteractionHandler:function(){},registerInteractionHandler:function(){}}},enumerable:!1,configurable:!0}),c.prototype.init=function(){this.adapter.registerInteractionHandler("click",this.click)},c.prototype.destroy=function(){this.adapter.deregisterInteractionHandler("click",this.click)},c.prototype.handleClick=function(){var e=this;this.adapter.activateInputRipple(),requestAnimationFrame((function(){e.adapter.deactivateInputRipple()}))},c}(c);class oe extends d{constructor(){super(...arguments),this.alignEnd=!1,this.spaceBetween=!1,this.nowrap=!1,this.label="",this.mdcFoundationClass=ce}createAdapter(){return{registerInteractionHandler:(e,t)=>{this.labelEl.addEventListener(e,t)},deregisterInteractionHandler:(e,t)=>{this.labelEl.removeEventListener(e,t)},activateInputRipple:async()=>{const e=this.input;if(e instanceof Y){const t=await e.ripple;t&&t.startPress()}},deactivateInputRipple:async()=>{const e=this.input;if(e instanceof Y){const t=await e.ripple;t&&t.endPress()}}}}get input(){var e,t;return null!==(t=null===(e=this.slottedInputs)||void 0===e?void 0:e[0])&&void 0!==t?t:null}render(){const e={"mdc-form-field--align-end":this.alignEnd,"mdc-form-field--space-between":this.spaceBetween,"mdc-form-field--nowrap":this.nowrap};return s`
`}click(){this._labelClick()}_labelClick(){const e=this.input;e&&(e.focus(),e.click())}}o([n({type:Boolean})],oe.prototype,"alignEnd",void 0),o([n({type:Boolean})],oe.prototype,"spaceBetween",void 0),o([n({type:Boolean})],oe.prototype,"nowrap",void 0),o([n({type:String}),C((async function(e){var t;null===(t=this.input)||void 0===t||t.setAttribute("aria-label",e)}))],oe.prototype,"label",void 0),o([r(".mdc-form-field")],oe.prototype,"mdcRoot",void 0),o([I("",!0,"*")],oe.prototype,"slottedInputs",void 0),o([r("label")],oe.prototype,"labelEl",void 0);const re=f`.mdc-form-field{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-body2-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.875rem;font-size:var(--mdc-typography-body2-font-size, 0.875rem);line-height:1.25rem;line-height:var(--mdc-typography-body2-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-body2-font-weight, 400);letter-spacing:0.0178571429em;letter-spacing:var(--mdc-typography-body2-letter-spacing, 0.0178571429em);text-decoration:inherit;text-decoration:var(--mdc-typography-body2-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-body2-text-transform, inherit);color:rgba(0, 0, 0, 0.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87));display:inline-flex;align-items:center;vertical-align:middle}.mdc-form-field>label{margin-left:0;margin-right:auto;padding-left:4px;padding-right:0;order:0}[dir=rtl] .mdc-form-field>label,.mdc-form-field>label[dir=rtl]{margin-left:auto;margin-right:0}[dir=rtl] .mdc-form-field>label,.mdc-form-field>label[dir=rtl]{padding-left:0;padding-right:4px}.mdc-form-field--nowrap>label{text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.mdc-form-field--align-end>label{margin-left:auto;margin-right:0;padding-left:0;padding-right:4px;order:-1}[dir=rtl] .mdc-form-field--align-end>label,.mdc-form-field--align-end>label[dir=rtl]{margin-left:0;margin-right:auto}[dir=rtl] .mdc-form-field--align-end>label,.mdc-form-field--align-end>label[dir=rtl]{padding-left:4px;padding-right:0}.mdc-form-field--space-between{justify-content:space-between}.mdc-form-field--space-between>label{margin:0}[dir=rtl] .mdc-form-field--space-between>label,.mdc-form-field--space-between>label[dir=rtl]{margin:0}:host{display:inline-flex}.mdc-form-field{width:100%}::slotted(*){-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-body2-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.875rem;font-size:var(--mdc-typography-body2-font-size, 0.875rem);line-height:1.25rem;line-height:var(--mdc-typography-body2-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-body2-font-weight, 400);letter-spacing:0.0178571429em;letter-spacing:var(--mdc-typography-body2-letter-spacing, 0.0178571429em);text-decoration:inherit;text-decoration:var(--mdc-typography-body2-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-body2-text-transform, inherit);color:rgba(0, 0, 0, 0.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87))}::slotted(mwc-switch){margin-right:10px}[dir=rtl] ::slotted(mwc-switch),::slotted(mwc-switch[dir=rtl]){margin-left:10px}`;g([b("ha-formfield")],(function(e,t){return{F:class extends t{constructor(...t){super(...t),e(this)}},d:[{kind:"method",key:"_labelClick",value:function(){const e=this.input;if(e)switch(e.focus(),e.tagName){case"HA-CHECKBOX":case"HA-RADIO":e.checked=!e.checked,E(e,"change");break;default:e.click()}}},{kind:"field",static:!0,key:"styles",value:()=>[re,f` :host(:not([alignEnd])) ::slotted(ha-switch) { margin-right: 10px; margin-inline-end: 10px; margin-inline-start: inline; } .mdc-form-field > label { direction: var(--direction); margin-inline-start: 0; margin-inline-end: auto; padding-inline-start: 4px; padding-inline-end: 0; } `]}]}}),oe);export{R as C,Y as F,oe as a,ee as b,Z as c,J as d,re as s}; ================================================ FILE: custom_components/hacs/hacs_frontend/c.063631e8.js ================================================ import{A as e,aY as t,r as i,aZ as a,aa as n,a7 as o,a_ as s,a$ as r,a9 as l,S as d,T as c,x as u,Q as h,b0 as p,a as v,h as m,e as f,i as g,L as _,N as y,$ as k,z as b,ac as x,ad as $,n as w,b1 as C,aQ as A,b2 as I,b3 as E,b4 as z,b5 as S,b6 as L,b7 as T,b8 as O,b9 as P,ba as M,bb as F,bc as D,aB as B,bd as N,be as V,bf as j,bg as q,bh as R,bi as U,bj as H,bk as G,bl as W,bm as K,bn as Y,aS as Z,bo as Q,bp as X,bq as J,br as ee,bs as te,ag as ie,bt as ae,bu as ne,bv as oe,bw as se,bx as re,by as le,bz as de,bA as ce,bB as ue,bC as he,bD as pe,bE as ve,bF as me,bG as fe,bH as ge,bI as _e,bJ as ye,bK as ke,bL as be,bM as xe,bN as $e,bO as we,bP as Ce,bQ as Ae,bR as Ie,E as Ee,bS as ze,bT as Se,bU as Le,bV as Te,bW as Oe,bX as Pe,bY as Me,bZ as Fe,b_ as De,b$ as Be,c0 as Ne,c1 as Ve,c2 as je,c3 as qe,c4 as Re,c5 as Ue,c6 as He,c7 as Ge,c8 as We,c9 as Ke,ca as Ye,cb as Ze,cc as Qe,cd as Xe,ce as Je,cf as et,cg as tt,ch as it,ci as at,cj as nt,ck as ot,cl as st,cm as rt,cn as lt,co as dt,cp as ct,cq as ut,cr as ht,cs as pt,ct as vt,cu as mt,cv as ft,cw as gt,cx as _t,cy as yt,cz as kt,cA as bt,cB as xt,cC as $t,cD as wt,az as Ct,cE as At,cF as It,cG as Et,cH as zt,cI as St,cJ as Lt,cK as Tt,cL as Ot,cM as Pt,cN as Mt,cO as Ft,cP as Dt,cQ as Bt,cR as Nt,cS as Vt,cT as jt,cU as qt,cV as Rt,cW as Ut,cX as Ht,cY as Gt,cZ as Wt,c_ as Kt,c$ as Yt,d0 as Zt,d1 as Qt,d2 as Xt,d3 as Jt,d4 as ei,d5 as ti,d6 as ii,d7 as ai,d8 as ni,d9 as oi,da as si,db as ri,dc as li,dd as di,de as ci,df as ui,dg as hi,dh as pi,di as vi,dj as mi,dk as fi,dl as gi,dm as _i,dn as yi,dp as ki,dq as bi,dr as xi,ds as $i,dt as wi,du as Ci,dv as Ai,dw as Ii,dx as Ei,dy as zi,dz as Si,dA as Li,dB as Ti,dC as Oi,dD as Pi,dE as Mi,dF as Fi,dG as Di,dH as Bi,dI as Ni,t as Vi,I as ji,j as qi,m as Ri,Z as Ui,aD as Hi,dJ as Gi,dK as Wi,dL as Ki,aM as Yi,dM as Zi,_ as Qi,o as Xi,dN as Ji,dO as ea,dP as ta,dQ as ia,dR as aa,dS as na,dT as oa,dU as sa,dV as ra,dW as la,dX as da,dY as ca,dZ as ua,d_ as ha,d$ as pa,aK as va,e0 as ma,e1 as fa,V as ga,e2 as _a,e3 as ya,e4 as ka,e5 as ba,e6 as xa,e7 as $a,e8 as wa,e9 as Ca,J as Aa,af as Ia}from"./main-7bc9a818.js";import{d as Ea,a as za}from"./c.67735e63.js";import"./c.cf66b923.js";import"./c.5d9598b2.js";import"./c.fb76e5d5.js";import"./c.9475214f.js";import{s as Sa,a as La,b as Ta}from"./c.38b86040.js";import{T as Oa,a as Pa,s as Ma}from"./c.2aa297ae.js";import{b as Fa,e as Da}from"./c.d9dcade0.js";import{d as Ba}from"./c.01f18260.js";import{i as Na}from"./c.21c042d4.js";import{c as Va,u as ja}from"./c.743a15a1.js";import"./c.5ec2d281.js";import{g as qa}from"./c.6711bf6c.js";import"./c.e0e56ec4.js";import{a as Ra}from"./c.487362b0.js";import"./c.9a1f96ed.js";let Ua=!1,Ha=[],Ga=[];function Wa(){Ua=!0,requestAnimationFrame((function(){Ua=!1,function(e){for(;e.length;)Ka(e.shift())}(Ha),setTimeout((function(){!function(e){for(let t=0,i=e.length;t{throw e}))}}function Ya(e){if(!e||"object"!=typeof e)return e;if("[object Date]"==Object.prototype.toString.call(e))return new Date(e.getTime());if(Array.isArray(e))return e.map(Ya);var t={};return Object.keys(e).forEach((function(i){t[i]=Ya(e[i])})),t}const Za=(e,t)=>et?1:0,Qa=(e,t)=>Za(e.toLowerCase(),t.toLowerCase());class Xa extends TypeError{constructor(e,t){let i;const{message:a,...n}=e,{path:o}=e;super(0===o.length?a:"At path: "+o.join(".")+" -- "+a),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 a;return null!=(a=i)?a:i=[e,...t()]}}}function Ja(e){return"object"==typeof e&&null!=e}function en(e){return"string"==typeof e?JSON.stringify(e):""+e}function tn(e,t,i,a){if(!0===e)return;!1===e?e={}:"string"==typeof e&&(e={message:e});const{path:n,branch:o}=t,{type:s}=i,{refinement:r,message:l="Expected a value of type `"+s+"`"+(r?" with refinement `"+r+"`":"")+", but received: `"+en(a)+"`"}=e;return{value:a,type:s,refinement:r,key:n[n.length-1],path:n,branch:o,...e,message:l}}function*an(e,t,i,a){(function(e){return Ja(e)&&"function"==typeof e[Symbol.iterator]})(e)||(e=[e]);for(const n of e){const e=tn(n,t,i,a);e&&(yield e)}}function*nn(e,t,i){void 0===i&&(i={});const{path:a=[],branch:n=[e],coerce:o=!1,mask:s=!1}=i,r={path:a,branch:n};if(o&&(e=t.coercer(e,r),s&&"type"!==t.type&&Ja(t.schema)&&Ja(e)&&!Array.isArray(e)))for(const i in e)void 0===t.schema[i]&&delete e[i];let l=!0;for(const i of t.validator(e,r))l=!1,yield[i,void 0];for(let[i,d,c]of t.entries(e,r)){const t=nn(d,c,{path:void 0===i?a:[...a,i],branch:void 0===i?n:[...n,d],coerce:o,mask:s});for(const a of t)a[0]?(l=!1,yield[a[0],void 0]):o&&(d=a[1],void 0===i?e=d:e instanceof Map?e.set(i,d):e instanceof Set?e.add(d):Ja(e)&&(e[i]=d))}if(l)for(const i of t.refiner(e,r))l=!1,yield[i,void 0];l&&(yield[void 0,e])}class on{constructor(e){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:t,schema:i,validator:a,refiner:n,coercer:o=(e=>e),entries:s=function*(){}}=e;this.type=t,this.schema=i,this.entries=s,this.coercer=o,this.validator=a?(e,t)=>an(a(e,t),t,this,e):()=>[],this.refiner=n?(e,t)=>an(n(e,t),t,this,e):()=>[]}assert(e){return sn(e,this)}create(e){return function(e,t){const i=ln(e,t,{coerce:!0});if(i[0])throw i[0];return i[1]}(e,this)}is(e){return rn(e,this)}mask(e){return function(e,t){const i=ln(e,t,{coerce:!0,mask:!0});if(i[0])throw i[0];return i[1]}(e,this)}validate(e,t){return void 0===t&&(t={}),ln(e,this,t)}}function sn(e,t){const i=ln(e,t);if(i[0])throw i[0]}function rn(e,t){return!ln(e,t)[0]}function ln(e,t,i){void 0===i&&(i={});const a=nn(e,t,i),n=function(e){const{done:t,value:i}=e.next();return t?void 0:i}(a);if(n[0]){const e=new Xa(n[0],(function*(){for(const e of a)e[0]&&(yield e[0])}));return[e,void 0]}return[void 0,n[1]]}function dn(){for(var e=arguments.length,t=new Array(e),i=0;ie.schema)),o=Object.assign({},...n);return a?yn(o):fn(o)}function cn(e,t){return new on({type:e,schema:null,validator:t})}function un(){return cn("any",(()=>!0))}function hn(e){return new on({type:"array",schema:e,*entries(t){if(e&&Array.isArray(t))for(const[i,a]of t.entries())yield[i,a,e]},coercer:e=>Array.isArray(e)?e.slice():e,validator:e=>Array.isArray(e)||"Expected an array value, but received: "+en(e)})}function pn(){return cn("boolean",(e=>"boolean"==typeof e))}function vn(e){const t=en(e),i=typeof e;return new on({type:"literal",schema:"string"===i||"number"===i||"boolean"===i?e:null,validator:i=>i===e||"Expected the literal `"+t+"`, but received: "+en(i)})}function mn(){return cn("number",(e=>"number"==typeof e&&!isNaN(e)||"Expected a number, but received: "+en(e)))}function fn(e){const t=e?Object.keys(e):[],i=cn("never",(()=>!1));return new on({type:"object",schema:e||null,*entries(a){if(e&&Ja(a)){const n=new Set(Object.keys(a));for(const i of t)n.delete(i),yield[i,a[i],e[i]];for(const e of n)yield[e,a[e],i]}},validator:e=>Ja(e)||"Expected an object, but received: "+en(e),coercer:e=>Ja(e)?{...e}:e})}function gn(e){return new on({...e,validator:(t,i)=>void 0===t||e.validator(t,i),refiner:(t,i)=>void 0===t||e.refiner(t,i)})}function _n(){return cn("string",(e=>"string"==typeof e||"Expected a string, but received: "+en(e)))}function yn(e){const t=Object.keys(e);return new on({type:"type",schema:e,*entries(i){if(Ja(i))for(const a of t)yield[a,i[a],e[a]]},validator:e=>Ja(e)||"Expected an object, but received: "+en(e)})}function kn(e){const t=e.map((e=>e.type)).join(" | ");return new on({type:"union",schema:null,coercer(t,i){const a=e.find((e=>{const[i]=e.validate(t,{coerce:!0});return!i}))||cn("unknown",(()=>!0));return a.coercer(t,i)},validator(i,a){const n=[];for(const t of e){const[...e]=nn(i,t,a),[o]=e;if(!o[0])return[];for(const[t]of e)t&&n.push(t)}return["Expected the value to satisfy a union of `"+t+"`, but received: "+en(i),...n]}})}const bn=(e,t)=>{if(!(t instanceof Xa))return{warnings:[t.message],errors:void 0};const i=[],a=[];for(const n of t.failures())if(void 0===n.value)i.push(e.localize("ui.errors.config.key_missing","key",n.path.join(".")));else if("never"===n.type)a.push(e.localize("ui.errors.config.key_not_expected","key",n.path.join(".")));else{if("union"===n.type)continue;"enums"===n.type?a.push(e.localize("ui.errors.config.key_wrong_type","key",n.path.join("."),"type_correct",n.message.replace("Expected ","").split(", ")[0],"type_wrong",JSON.stringify(n.value))):a.push(e.localize("ui.errors.config.key_wrong_type","key",n.path.join("."),"type_correct",n.refinement||n.type,"type_wrong",JSON.stringify(n.value)))}return{warnings:a,errors:i}},xn=(e,t)=>e.callWS({type:"validate_config",...t}),$n=e=>e.substr(e.indexOf(".")+1),wn=fn({alias:gn(_n()),enabled:gn(pn())}),Cn=fn({entity_id:gn(kn([_n(),hn(_n())])),device_id:gn(kn([_n(),hn(_n())])),area_id:gn(kn([_n(),hn(_n())]))});dn(wn,fn({service:gn(_n()),service_template:gn(_n()),entity_id:gn(_n()),target:gn(Cn),data:gn(fn())}));const An=dn(wn,fn({service:vn("media_player.play_media"),target:gn(fn({entity_id:gn(_n())})),entity_id:gn(_n()),data:fn({media_content_id:_n(),media_content_type:_n()}),metadata:fn()})),In=dn(wn,fn({service:vn("scene.turn_on"),target:gn(fn({entity_id:gn(_n())})),entity_id:gn(_n()),metadata:fn()})),En=(t,i)=>e(t,"hass-notification",i),zn=e=>e.substr(0,e.indexOf(".")),Sn=e=>{return t=e.entity_id,void 0===(i=e.attributes).friendly_name?$n(t).replace(/_/g," "):i.friendly_name||"";var t,i};class Ln extends HTMLElement{static get version(){return"23.1.3"}}customElements.define("vaadin-material-styles",Ln);const Tn=e=>class extends e{static get properties(){return{theme:{type:String,reflectToAttribute:!0,observer:"__deprecatedThemePropertyChanged"},_theme:{type:String,readOnly:!0}}}__deprecatedThemePropertyChanged(e){this._set_theme(e)}},On=[];function Pn(e,i,a={}){var n;e&&(n=e,Nn(customElements.get(n))&&console.warn(`The custom element definition for "${e}"\n was finalized before a style module was registered.\n Make sure to add component specific style modules before\n importing the corresponding custom element.`)),i=function(e=[]){return[e].flat(1/0).filter((e=>e instanceof t||(console.warn("An item in styles is not of type CSSResult. Use `unsafeCSS` or `css`."),!1)))}(i),window.Vaadin&&window.Vaadin.styleModules?window.Vaadin.styleModules.registerStyles(e,i,a):On.push({themeFor:e,styles:i,include:a.include,moduleId:a.moduleId})}function Mn(){return window.Vaadin&&window.Vaadin.styleModules?window.Vaadin.styleModules.getAllThemes():On}function Fn(e=""){let t=0;return 0===e.indexOf("lumo-")||0===e.indexOf("material-")?t=1:0===e.indexOf("vaadin-")&&(t=2),t}function Dn(e){const t=[];return e.include&&[].concat(e.include).forEach((e=>{const i=Mn().find((t=>t.moduleId===e));i?t.push(...Dn(i),...i.styles):console.warn(`Included moduleId ${e} not found in style registry`)}),e.styles),t}function Bn(e){const t=`${e}-default-theme`,i=Mn().filter((i=>i.moduleId!==t&&function(e,t){return(e||"").split(" ").some((e=>new RegExp(`^${e.split("*").join(".*")}$`).test(t)))}(i.themeFor,e))).map((e=>({...e,styles:[...Dn(e),...e.styles],includePriority:Fn(e.moduleId)}))).sort(((e,t)=>t.includePriority-e.includePriority));return i.length>0?i:Mn().filter((e=>e.moduleId===t))}function Nn(e){return e&&Object.prototype.hasOwnProperty.call(e,"__themes")}const Vn=e=>class extends(Tn(e)){static finalize(){if(super.finalize(),this.elementStyles)return;const e=this.prototype._template;e&&!Nn(this)&&function(e,t){const i=document.createElement("style");i.innerHTML=e.map((e=>e.cssText)).join("\n"),t.content.appendChild(i)}(this.getStylesForThis(),e)}static finalizeStyles(e){const t=this.getStylesForThis();return e?[...super.finalizeStyles(e),...t]:t}static getStylesForThis(){const e=Object.getPrototypeOf(this.prototype),t=(e?e.constructor.__themes:[])||[];this.__themes=[...t,...Bn(this.is)];const i=this.__themes.flatMap((e=>e.styles));return i.filter(((e,t)=>t===i.lastIndexOf(e)))}};Pn("",i` :host { /* Text colors */ --material-body-text-color: var(--light-theme-text-color, rgba(0, 0, 0, 0.87)); --material-secondary-text-color: var(--light-theme-secondary-color, rgba(0, 0, 0, 0.54)); --material-disabled-text-color: var(--light-theme-disabled-color, rgba(0, 0, 0, 0.38)); /* Primary colors */ --material-primary-color: var(--primary-color, #6200ee); --material-primary-contrast-color: var(--dark-theme-base-color, #fff); --material-primary-text-color: var(--material-primary-color); /* Error colors */ --material-error-color: var(--error-color, #b00020); --material-error-text-color: var(--material-error-color); /* Background colors */ --material-background-color: var(--light-theme-background-color, #fff); --material-secondary-background-color: var(--light-theme-secondary-background-color, #f5f5f5); --material-disabled-color: rgba(0, 0, 0, 0.26); /* Divider colors */ --material-divider-color: rgba(0, 0, 0, 0.12); /* Undocumented internal properties (prefixed with three dashes) */ /* Text field tweaks */ --_material-text-field-input-line-background-color: initial; --_material-text-field-input-line-opacity: initial; --_material-text-field-input-line-hover-opacity: initial; --_material-text-field-focused-label-opacity: initial; /* Button tweaks */ --_material-button-raised-background-color: initial; --_material-button-outline-color: initial; /* Grid tweaks */ --_material-grid-row-hover-background-color: initial; /* Split layout tweaks */ --_material-split-layout-splitter-background-color: initial; background-color: var(--material-background-color); color: var(--material-body-text-color); } [theme~='dark'] { /* Text colors */ --material-body-text-color: var(--dark-theme-text-color, rgba(255, 255, 255, 1)); --material-secondary-text-color: var(--dark-theme-secondary-color, rgba(255, 255, 255, 0.7)); --material-disabled-text-color: var(--dark-theme-disabled-color, rgba(255, 255, 255, 0.5)); /* Primary colors */ --material-primary-color: var(--light-primary-color, #7e3ff2); --material-primary-text-color: #b794f6; /* Error colors */ --material-error-color: var(--error-color, #de2839); --material-error-text-color: var(--material-error-color); /* Background colors */ --material-background-color: var(--dark-theme-background-color, #303030); --material-secondary-background-color: var(--dark-theme-secondary-background-color, #3b3b3b); --material-disabled-color: rgba(255, 255, 255, 0.3); /* Divider colors */ --material-divider-color: rgba(255, 255, 255, 0.12); /* Undocumented internal properties (prefixed with three dashes) */ /* Text field tweaks */ --_material-text-field-input-line-background-color: #fff; --_material-text-field-input-line-opacity: 0.7; --_material-text-field-input-line-hover-opacity: 1; --_material-text-field-focused-label-opacity: 1; /* Button tweaks */ --_material-button-raised-background-color: rgba(255, 255, 255, 0.08); --_material-button-outline-color: rgba(255, 255, 255, 0.2); /* Grid tweaks */ --_material-grid-row-hover-background-color: rgba(255, 255, 255, 0.08); --_material-grid-row-selected-overlay-opacity: 0.16; /* Split layout tweaks */ --_material-split-layout-splitter-background-color: rgba(255, 255, 255, 0.8); background-color: var(--material-background-color); color: var(--material-body-text-color); } a { color: inherit; } `,{moduleId:"material-color-light"});Pn("",i` :host { /* Text colors */ --material-body-text-color: var(--dark-theme-text-color, rgba(255, 255, 255, 1)); --material-secondary-text-color: var(--dark-theme-secondary-color, rgba(255, 255, 255, 0.7)); --material-disabled-text-color: var(--dark-theme-disabled-color, rgba(255, 255, 255, 0.5)); /* Primary colors */ --material-primary-color: var(--light-primary-color, #7e3ff2); --material-primary-text-color: #b794f6; /* Error colors */ --material-error-color: var(--error-color, #de2839); --material-error-text-color: var(--material-error-color); /* Background colors */ --material-background-color: var(--dark-theme-background-color, #303030); --material-secondary-background-color: var(--dark-theme-secondary-background-color, #3b3b3b); --material-disabled-color: rgba(255, 255, 255, 0.3); /* Divider colors */ --material-divider-color: rgba(255, 255, 255, 0.12); /* Undocumented internal properties (prefixed with three dashes) */ /* Text field tweaks */ --_material-text-field-input-line-background-color: #fff; --_material-text-field-input-line-opacity: 0.7; --_material-text-field-input-line-hover-opacity: 1; --_material-text-field-focused-label-opacity: 1; /* Button tweaks */ --_material-button-raised-background-color: rgba(255, 255, 255, 0.08); --_material-button-outline-color: rgba(255, 255, 255, 0.2); /* Grid tweaks */ --_material-grid-row-hover-background-color: rgba(255, 255, 255, 0.08); --_material-grid-row-selected-overlay-opacity: 0.16; /* Split layout tweaks */ --_material-split-layout-splitter-background-color: rgba(255, 255, 255, 0.8); background-color: var(--material-background-color); color: var(--material-body-text-color); } `,{moduleId:"material-color-dark"});const jn=i` :host { /* Text colors */ --material-body-text-color: var(--light-theme-text-color, rgba(0, 0, 0, 0.87)); --material-secondary-text-color: var(--light-theme-secondary-color, rgba(0, 0, 0, 0.54)); --material-disabled-text-color: var(--light-theme-disabled-color, rgba(0, 0, 0, 0.38)); /* Primary colors */ --material-primary-color: var(--primary-color, #6200ee); --material-primary-contrast-color: var(--dark-theme-base-color, #fff); --material-primary-text-color: var(--material-primary-color); /* Error colors */ --material-error-color: var(--error-color, #b00020); --material-error-text-color: var(--material-error-color); /* Background colors */ --material-background-color: var(--light-theme-background-color, #fff); --material-secondary-background-color: var(--light-theme-secondary-background-color, #f5f5f5); --material-disabled-color: rgba(0, 0, 0, 0.26); /* Divider colors */ --material-divider-color: rgba(0, 0, 0, 0.12); } `,qn=document.createElement("template");qn.innerHTML=``,document.head.appendChild(qn.content);const Rn=i` :host { /* Font family */ --material-font-family: 'Roboto', sans-serif; /* Font sizes */ --material-h1-font-size: 6rem; --material-h2-font-size: 3.75rem; --material-h3-font-size: 3rem; --material-h4-font-size: 2.125rem; --material-h5-font-size: 1.5rem; --material-h6-font-size: 1.25rem; --material-body-font-size: 1rem; --material-small-font-size: 0.875rem; --material-button-font-size: 0.875rem; --material-caption-font-size: 0.75rem; /* Icon size */ --material-icon-font-size: 20px; } `;Pn("",i` body, :host { font-family: var(--material-font-family); font-size: var(--material-body-font-size); line-height: 1.4; -webkit-text-size-adjust: 100%; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } h1, h2, h3, h4, h5, h6 { color: inherit; line-height: 1.1; margin-top: 1.5em; } h1 { font-size: var(--material-h3-font-size); font-weight: 300; letter-spacing: -0.015em; margin-bottom: 1em; text-indent: -0.07em; } h2 { font-size: var(--material-h4-font-size); font-weight: 300; letter-spacing: -0.01em; margin-bottom: 0.75em; text-indent: -0.07em; } h3 { font-size: var(--material-h5-font-size); font-weight: 400; margin-bottom: 0.75em; text-indent: -0.05em; } h4 { font-size: var(--material-h6-font-size); font-weight: 400; letter-spacing: 0.01em; margin-bottom: 0.75em; text-indent: -0.05em; } h5 { font-size: var(--material-body-font-size); font-weight: 500; margin-bottom: 0.5em; text-indent: -0.025em; } h6 { font-size: var(--material-small-font-size); font-weight: 500; letter-spacing: 0.01em; margin-bottom: 0.25em; text-indent: -0.025em; } a, b, strong { font-weight: 500; } `,{moduleId:"material-typography"});const Un=document.createElement("template");if(Un.innerHTML=``,document.head.appendChild(Un.content),!window.polymerSkipLoadingFontRoboto){const e="https://fonts.googleapis.com/css?family=Roboto+Mono:400,700|Roboto:400,300,300italic,400italic,500,500italic,700,700italic",t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.crossOrigin="anonymous",t.href=e,document.head.appendChild(t)}const Hn=i` /* prettier-ignore */ :host { /* from http://codepen.io/shyndman/pen/c5394ddf2e8b2a5c9185904b57421cdb */ --material-shadow-elevation-2dp: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12), 0 3px 1px -2px rgba(0, 0, 0, 0.2); --material-shadow-elevation-3dp: 0 3px 4px 0 rgba(0, 0, 0, 0.14), 0 1px 8px 0 rgba(0, 0, 0, 0.12), 0 3px 3px -2px rgba(0, 0, 0, 0.4); --material-shadow-elevation-4dp: 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12), 0 2px 4px -1px rgba(0, 0, 0, 0.4); --material-shadow-elevation-6dp: 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12), 0 3px 5px -1px rgba(0, 0, 0, 0.4); --material-shadow-elevation-8dp: 0 8px 10px 1px rgba(0, 0, 0, 0.14), 0 3px 14px 2px rgba(0, 0, 0, 0.12), 0 5px 5px -3px rgba(0, 0, 0, 0.4); --material-shadow-elevation-12dp: 0 12px 16px 1px rgba(0, 0, 0, 0.14), 0 4px 22px 3px rgba(0, 0, 0, 0.12), 0 6px 7px -4px rgba(0, 0, 0, 0.4); --material-shadow-elevation-16dp: 0 16px 24px 2px rgba(0, 0, 0, 0.14), 0 6px 30px 5px rgba(0, 0, 0, 0.12), 0 8px 10px -5px rgba(0, 0, 0, 0.4); --material-shadow-elevation-24dp: 0 24px 38px 3px rgba(0, 0, 0, 0.14), 0 9px 46px 8px rgba(0, 0, 0, 0.12), 0 11px 15px -7px rgba(0, 0, 0, 0.4); } `,Gn=document.createElement("template");Gn.innerHTML=``,document.head.appendChild(Gn.content);const Wn=i` :host { top: 16px; right: 16px; /* TODO (@jouni): remove unnecessary multiplication after https://github.com/vaadin/vaadin-overlay/issues/90 is fixed */ bottom: calc(1px * var(--vaadin-overlay-viewport-bottom) + 16px); left: 16px; } [part='overlay'] { background-color: var(--material-background-color); border-radius: 4px; box-shadow: var(--material-shadow-elevation-4dp); color: var(--material-body-text-color); font-family: var(--material-font-family); font-size: var(--material-body-font-size); font-weight: 400; } [part='content'] { padding: 8px 0; } [part='backdrop'] { opacity: 0.2; animation: 0.2s vaadin-overlay-backdrop-enter; will-change: opacity; } @keyframes vaadin-overlay-backdrop-enter { 0% { opacity: 0; } } `;Pn("",Wn,{moduleId:"material-overlay"}),Pn("vaadin-overlay",Wn,{moduleId:"material-vaadin-overlay"});const Kn=e=>e.test(navigator.userAgent),Yn=e=>e.test(navigator.platform);Kn(/Android/),Kn(/Chrome/)&&/Google Inc/.test(navigator.vendor),Kn(/Firefox/);const Zn=Yn(/^iPad/)||Yn(/^Mac/)&&navigator.maxTouchPoints>1,Qn=Yn(/^iPhone/)||Zn,Xn=Kn(/^((?!chrome|android).)*safari/i),Jn=(()=>{try{return document.createEvent("TouchEvent"),!0}catch(e){return!1}})(),eo=a((e=>class extends e{constructor(){super(),this.__controllers=new Set}connectedCallback(){super.connectedCallback(),this.__controllers.forEach((e=>{e.hostConnected&&e.hostConnected()}))}disconnectedCallback(){super.disconnectedCallback(),this.__controllers.forEach((e=>{e.hostDisconnected&&e.hostDisconnected()}))}addController(e){this.__controllers.add(e),void 0!==this.$&&this.isConnected&&e.hostConnected&&e.hostConnected()}removeController(e){this.__controllers.delete(e)}}));class to{static detectScrollType(){const e=document.createElement("div");e.textContent="ABCD",e.dir="rtl",e.style.fontSize="14px",e.style.width="4px",e.style.height="1px",e.style.position="absolute",e.style.top="-1000px",e.style.overflow="scroll",document.body.appendChild(e);let t="reverse";return e.scrollLeft>0?t="default":(e.scrollLeft=2,e.scrollLeft<2&&(t="negative")),document.body.removeChild(e),t}static getNormalizedScrollLeft(e,t,i){const{scrollLeft:a}=i;if("rtl"!==t||!e)return a;switch(e){case"negative":return i.scrollWidth-i.clientWidth+a;case"reverse":return i.scrollWidth-i.clientWidth-a;default:return a}}static setNormalizedScrollLeft(e,t,i,a){if("rtl"===t&&e)switch(e){case"negative":i.scrollLeft=i.clientWidth-i.scrollWidth+a;break;case"reverse":i.scrollLeft=i.scrollWidth-i.clientWidth-a;break;default:i.scrollLeft=a}else i.scrollLeft=a}}const io=[];let ao;function no(e,t,i=e.getAttribute("dir")){t?e.setAttribute("dir",t):null!=i&&e.removeAttribute("dir")}function oo(){return document.documentElement.getAttribute("dir")}new MutationObserver((function(){const e=oo();io.forEach((t=>{no(t,e)}))})).observe(document.documentElement,{attributes:!0,attributeFilter:["dir"]});const so=e=>class extends e{static get properties(){return{dir:{type:String,value:"",reflectToAttribute:!0,converter:{fromAttribute:e=>e||"",toAttribute:e=>""===e?null:e}}}}static finalize(){super.finalize(),ao||(ao=to.detectScrollType())}connectedCallback(){super.connectedCallback(),this.hasAttribute("dir")||(this.__subscribe(),no(this,oo(),null))}attributeChangedCallback(e,t,i){if(super.attributeChangedCallback(e,t,i),"dir"!==e)return;const a=oo(),n=i===a&&-1===io.indexOf(this),o=!i&&t&&-1===io.indexOf(this),s=i!==a&&t===a;n||o?(this.__subscribe(),no(this,a,i)):s&&this.__subscribe(!1)}disconnectedCallback(){super.disconnectedCallback(),this.__subscribe(!1),this.removeAttribute("dir")}_valueToNodeAttribute(e,t,i){("dir"!==i||""!==t||e.hasAttribute("dir"))&&super._valueToNodeAttribute(e,t,i)}_attributeToProperty(e,t,i){"dir"!==e||t?super._attributeToProperty(e,t,i):this.dir=""}__subscribe(e=!0){e?io.includes(this)||io.push(this):io.includes(this)&&io.splice(io.indexOf(this),1)}__getNormalizedScrollLeft(e){return to.getNormalizedScrollLeft(ao,this.getAttribute("dir")||"ltr",e)}__setNormalizedScrollLeft(e,t){return to.setNormalizedScrollLeft(ao,this.getAttribute("dir")||"ltr",e,t)}};function ro(e,t){const i=Math.max(e.tabIndex,0),a=Math.max(t.tabIndex,0);return 0===i||0===a?a>i:i>a}function lo(e){const t=e.length;if(t<2)return e;const i=Math.ceil(t/2);return function(e,t){const i=[];for(;e.length>0&&t.length>0;)ro(e[0],t[0])?i.push(t.shift()):i.push(e.shift());return i.concat(e,t)}(lo(e.slice(0,i)),lo(e.slice(i)))}function co(e,t){if(e.nodeType!==Node.ELEMENT_NODE||function(e){const t=e.style;if("hidden"===t.visibility||"none"===t.display)return!0;const i=window.getComputedStyle(e);return"hidden"===i.visibility||"none"===i.display}(e))return!1;const i=e,a=function(e){if(!function(e){return!e.matches('[tabindex="-1"]')&&(e.matches("input, select, textarea, button, object")?e.matches(":not([disabled])"):e.matches("a[href], area[href], iframe, [tabindex], [contentEditable]"))}(e))return-1;const t=e.getAttribute("tabindex")||0;return Number(t)}(i);let n=a>0;a>=0&&t.push(i);let o=[];return o="slot"===i.localName?i.assignedNodes({flatten:!0}):(i.shadowRoot||i).children,[...o].forEach((e=>{n=co(e,t)||n})),n}function uo(e){return e.getRootNode().activeElement===e}const ho=[];class po{constructor(e){this.host=e,this.__trapNode=null,this.__onKeyDown=this.__onKeyDown.bind(this)}hostConnected(){document.addEventListener("keydown",this.__onKeyDown)}hostDisconnected(){document.removeEventListener("keydown",this.__onKeyDown)}trapFocus(e){if(this.__trapNode=e,0===this.__focusableElements.length)throw this.__trapNode=null,new Error("The trap node should have at least one focusable descendant or be focusable itself.");ho.push(this),-1===this.__focusedElementIndex&&this.__focusableElements[0].focus()}releaseFocus(){this.__trapNode=null,ho.pop()}__onKeyDown(e){if(this.__trapNode&&this===Array.from(ho).pop()&&"Tab"===e.key){e.preventDefault();const t=e.shiftKey;this.__focusNextElement(t)}}__focusNextElement(e=!1){const t=this.__focusableElements,i=e?-1:1,a=this.__focusedElementIndex;t[(t.length+a+i)%t.length].focus()}get __focusableElements(){return function(e){const t=[];return co(e,t)?lo(t):t}(this.__trapNode)}get __focusedElementIndex(){const e=this.__focusableElements;return e.indexOf(e.filter(uo).pop())}}class vo extends(Vn(so(eo(n)))){static get template(){return o`
`}static get is(){return"vaadin-overlay"}static get properties(){return{opened:{type:Boolean,notify:!0,observer:"_openedChanged",reflectToAttribute:!0},owner:Element,renderer:Function,template:{type:Object,notify:!0},content:{type:Object,notify:!0},withBackdrop:{type:Boolean,value:!1,reflectToAttribute:!0},model:Object,modeless:{type:Boolean,value:!1,reflectToAttribute:!0,observer:"_modelessChanged"},hidden:{type:Boolean,reflectToAttribute:!0,observer:"_hiddenChanged"},focusTrap:{type:Boolean,value:!1},restoreFocusOnClose:{type:Boolean,value:!1},restoreFocusNode:{type:HTMLElement},_mouseDownInside:{type:Boolean},_mouseUpInside:{type:Boolean},_instance:{type:Object},_originalContentPart:Object,_contentNodes:Array,_oldOwner:Element,_oldModel:Object,_oldTemplate:Object,_oldRenderer:Object,_oldOpened:Boolean}}static get observers(){return["_templateOrRendererChanged(template, renderer, owner, model, opened)"]}constructor(){super(),this._boundMouseDownListener=this._mouseDownListener.bind(this),this._boundMouseUpListener=this._mouseUpListener.bind(this),this._boundOutsideClickListener=this._outsideClickListener.bind(this),this._boundKeydownListener=this._keydownListener.bind(this),this._observer=new s(this,(e=>{this._setTemplateFromNodes(e.addedNodes)})),this._boundIronOverlayCanceledListener=this._ironOverlayCanceled.bind(this),Qn&&(this._boundIosResizeListener=()=>this._detectIosNavbar()),this.__focusTrapController=new po(this)}ready(){super.ready(),this._observer.flush(),this.addEventListener("click",(()=>{})),this.$.backdrop.addEventListener("click",(()=>{})),this.addController(this.__focusTrapController)}_detectIosNavbar(){if(!this.opened)return;const e=window.innerHeight,t=window.innerWidth>e,i=document.documentElement.clientHeight;t&&i>e?this.style.setProperty("--vaadin-overlay-viewport-bottom",i-e+"px"):this.style.setProperty("--vaadin-overlay-viewport-bottom","0")}_setTemplateFromNodes(e){this.template=e.filter((e=>e.localName&&"template"===e.localName))[0]||this.template}close(e){const t=new CustomEvent("vaadin-overlay-close",{bubbles:!0,cancelable:!0,detail:{sourceEvent:e}});this.dispatchEvent(t),t.defaultPrevented||(this.opened=!1)}connectedCallback(){super.connectedCallback(),this._boundIosResizeListener&&(this._detectIosNavbar(),window.addEventListener("resize",this._boundIosResizeListener))}disconnectedCallback(){super.disconnectedCallback(),this._boundIosResizeListener&&window.removeEventListener("resize",this._boundIosResizeListener)}requestContentUpdate(){this.renderer&&this.renderer.call(this.owner,this.content,this.owner,this.model)}_ironOverlayCanceled(e){e.preventDefault()}_mouseDownListener(e){this._mouseDownInside=e.composedPath().indexOf(this.$.overlay)>=0}_mouseUpListener(e){this._mouseUpInside=e.composedPath().indexOf(this.$.overlay)>=0}_outsideClickListener(e){if(e.composedPath().includes(this.$.overlay)||this._mouseDownInside||this._mouseUpInside)return this._mouseDownInside=!1,void(this._mouseUpInside=!1);if(!this._last)return;const t=new CustomEvent("vaadin-overlay-outside-click",{bubbles:!0,cancelable:!0,detail:{sourceEvent:e}});this.dispatchEvent(t),this.opened&&!t.defaultPrevented&&this.close(e)}_keydownListener(e){if(this._last&&(!this.modeless||e.composedPath().includes(this.$.overlay))&&"Escape"===e.key){const t=new CustomEvent("vaadin-overlay-escape-press",{bubbles:!0,cancelable:!0,detail:{sourceEvent:e}});this.dispatchEvent(t),this.opened&&!t.defaultPrevented&&this.close(e)}}_ensureTemplatized(){this._setTemplateFromNodes(Array.from(this.children))}_openedChanged(e,t){var i,a,n;this._instance||this._ensureTemplatized(),e?(this.__restoreFocusNode=this._getActiveElement(),this._animatedOpening(),i=this,a=()=>{this.focusTrap&&this.__focusTrapController.trapFocus(this.$.overlay);const e=new CustomEvent("vaadin-overlay-open",{bubbles:!0});this.dispatchEvent(e)},Ua||Wa(),Ga.push([i,a,n]),document.addEventListener("keydown",this._boundKeydownListener),this.modeless||this._addGlobalListeners()):t&&(this.focusTrap&&this.__focusTrapController.releaseFocus(),this._animatedClosing(),document.removeEventListener("keydown",this._boundKeydownListener),this.modeless||this._removeGlobalListeners())}_hiddenChanged(e){e&&this.hasAttribute("closing")&&this._flushAnimation("closing")}_shouldAnimate(){const e=getComputedStyle(this).getPropertyValue("animation-name");return!("none"===getComputedStyle(this).getPropertyValue("display"))&&e&&"none"!==e}_enqueueAnimation(e,t){const i=`__${e}Handler`,a=e=>{e&&e.target!==this||(t(),this.removeEventListener("animationend",a),delete this[i])};this[i]=a,this.addEventListener("animationend",a)}_flushAnimation(e){const t=`__${e}Handler`;"function"==typeof this[t]&&this[t]()}_animatedOpening(){this.parentNode===document.body&&this.hasAttribute("closing")&&this._flushAnimation("closing"),this._attachOverlay(),this.modeless||this._enterModalState(),this.setAttribute("opening",""),this._shouldAnimate()?this._enqueueAnimation("opening",(()=>{this._finishOpening()})):this._finishOpening()}_attachOverlay(){this._placeholder=document.createComment("vaadin-overlay-placeholder"),this.parentNode.insertBefore(this._placeholder,this),document.body.appendChild(this),this.bringToFront()}_finishOpening(){document.addEventListener("iron-overlay-canceled",this._boundIronOverlayCanceledListener),this.removeAttribute("opening")}_finishClosing(){document.removeEventListener("iron-overlay-canceled",this._boundIronOverlayCanceledListener),this._detachOverlay(),this.$.overlay.style.removeProperty("pointer-events"),this.removeAttribute("closing")}_animatedClosing(){if(this.hasAttribute("opening")&&this._flushAnimation("opening"),this._placeholder){this._exitModalState();const e=this.restoreFocusNode||this.__restoreFocusNode;if(this.restoreFocusOnClose&&e){const t=this._getActiveElement();(t===document.body||this._deepContains(t))&&setTimeout((()=>e.focus())),this.__restoreFocusNode=null}this.setAttribute("closing",""),this.dispatchEvent(new CustomEvent("vaadin-overlay-closing")),this._shouldAnimate()?this._enqueueAnimation("closing",(()=>{this._finishClosing()})):this._finishClosing()}}_detachOverlay(){this._placeholder.parentNode.insertBefore(this,this._placeholder),this._placeholder.parentNode.removeChild(this._placeholder)}static get __attachedInstances(){return Array.from(document.body.children).filter((e=>e instanceof vo&&!e.hasAttribute("closing"))).sort(((e,t)=>e.__zIndex-t.__zIndex||0))}get _last(){return this===vo.__attachedInstances.pop()}_modelessChanged(e){e?(this._removeGlobalListeners(),this._exitModalState()):this.opened&&(this._addGlobalListeners(),this._enterModalState())}_addGlobalListeners(){document.addEventListener("mousedown",this._boundMouseDownListener),document.addEventListener("mouseup",this._boundMouseUpListener),document.documentElement.addEventListener("click",this._boundOutsideClickListener,!0)}_enterModalState(){"none"!==document.body.style.pointerEvents&&(this._previousDocumentPointerEvents=document.body.style.pointerEvents,document.body.style.pointerEvents="none"),vo.__attachedInstances.forEach((e=>{e!==this&&(e.shadowRoot.querySelector('[part="overlay"]').style.pointerEvents="none")}))}_removeGlobalListeners(){document.removeEventListener("mousedown",this._boundMouseDownListener),document.removeEventListener("mouseup",this._boundMouseUpListener),document.documentElement.removeEventListener("click",this._boundOutsideClickListener,!0)}_exitModalState(){void 0!==this._previousDocumentPointerEvents&&(document.body.style.pointerEvents=this._previousDocumentPointerEvents,delete this._previousDocumentPointerEvents);const e=vo.__attachedInstances;let t;for(;(t=e.pop())&&(t===this||(t.shadowRoot.querySelector('[part="overlay"]').style.removeProperty("pointer-events"),t.modeless)););}_removeOldContent(){this.content&&this._contentNodes&&(this._observer.disconnect(),this._contentNodes.forEach((e=>{e.parentNode===this.content&&this.content.removeChild(e)})),this._originalContentPart&&(this.$.content.parentNode.replaceChild(this._originalContentPart,this.$.content),this.$.content=this._originalContentPart,this._originalContentPart=void 0),this._observer.connect(),this._contentNodes=void 0,this.content=void 0)}_stampOverlayTemplate(e){this._removeOldContent(),e._Templatizer||(e._Templatizer=r(e,this,{forwardHostProp(e,t){this._instance&&this._instance.forwardHostProp(e,t)}})),this._instance=new e._Templatizer({}),this._contentNodes=Array.from(this._instance.root.childNodes);const t=e._templateRoot||(e._templateRoot=e.getRootNode());if(t!==document){this.$.content.shadowRoot||this.$.content.attachShadow({mode:"open"});let e=Array.from(t.querySelectorAll("style")).reduce(((e,t)=>e+t.textContent),"");if(e=e.replace(/:host/g,":host-nomatch"),e){const t=document.createElement("style");t.textContent=e,this.$.content.shadowRoot.appendChild(t),this._contentNodes.unshift(t)}this.$.content.shadowRoot.appendChild(this._instance.root),this.content=this.$.content.shadowRoot}else this.appendChild(this._instance.root),this.content=this}_removeNewRendererOrTemplate(e,t,i,a){e!==t?this.template=void 0:i!==a&&(this.renderer=void 0)}_templateOrRendererChanged(e,t,i,a,n){if(e&&t)throw this._removeNewRendererOrTemplate(e,this._oldTemplate,t,this._oldRenderer),new Error("You should only use either a renderer or a template for overlay content");const o=this._oldOwner!==i||this._oldModel!==a;this._oldModel=a,this._oldOwner=i;const s=this._oldTemplate!==e;this._oldTemplate=e;const r=this._oldRenderer!==t;this._oldRenderer=t;const l=this._oldOpened!==n;this._oldOpened=n,r&&(this.content=this,this.content.innerHTML="",delete this.content._$litPart$),e&&s?this._stampOverlayTemplate(e):t&&(r||l||o)&&n&&this.requestContentUpdate()}_getActiveElement(){let e=document.activeElement||document.body;for(;e.shadowRoot&&e.shadowRoot.activeElement;)e=e.shadowRoot.activeElement;return e}_deepContains(e){if(this.contains(e))return!0;let t=e;const i=e.ownerDocument;for(;t&&t!==i&&t!==this;)t=t.parentNode||t.host;return t===this}bringToFront(){let e="";const t=vo.__attachedInstances.filter((e=>e!==this)).pop();if(t){e=t.__zIndex+1}this.style.zIndex=e,this.__zIndex=e||parseFloat(getComputedStyle(this).zIndex)}}customElements.define(vo.is,vo);const mo=Wn;Pn("",mo,{moduleId:"material-menu-overlay"});Pn("vaadin-combo-box-overlay",[mo,i` :host { --_vaadin-combo-box-items-container-border-width: 8px 0; --_vaadin-combo-box-items-container-border-style: solid; --_vaadin-combo-box-items-container-border-color: transparent; } [part='overlay'] { position: relative; overflow: visible; border-top-left-radius: 0; border-top-right-radius: 0; } [part='content'] { padding: 0; } :host([loading]) [part='loader'] { height: 2px; position: absolute; z-index: 1; top: -2px; left: 0; right: 0; background: var(--material-background-color) linear-gradient( 90deg, transparent 0%, transparent 20%, var(--material-primary-color) 20%, var(--material-primary-color) 40%, transparent 40%, transparent 60%, var(--material-primary-color) 60%, var(--material-primary-color) 80%, transparent 80%, transparent 100% ) 0 0 / 400% 100% repeat-x; opacity: 0; animation: 3s linear infinite material-combo-box-loader-progress, 0.3s 0.1s both material-combo-box-loader-fade-in; } [part='loader']::before { content: ''; display: block; height: 100%; opacity: 0.16; background: var(--material-primary-color); } @keyframes material-combo-box-loader-fade-in { 0% { opacity: 0; } 100% { opacity: 1; } } @keyframes material-combo-box-loader-progress { 0% { background-position: 0 0; background-size: 300% 100%; } 33% { background-position: -100% 0; background-size: 400% 100%; } 67% { background-position: -200% 0; background-size: 250% 100%; } 100% { background-position: -300% 0; background-size: 300% 100%; } } /* RTL specific styles */ @keyframes material-combo-box-loader-progress-rtl { 0% { background-position: 100% 0; background-size: 300% 100%; } 33% { background-position: 200% 0; background-size: 400% 100%; } 67% { background-position: 300% 0; background-size: 250% 100%; } 100% { background-position: 400% 0; background-size: 300% 100%; } } :host([loading][dir='rtl']) [part='loader'] { animation: 3s linear infinite material-combo-box-loader-progress-rtl, 0.3s 0.1s both material-combo-box-loader-fade-in; } `],{moduleId:"material-combo-box-overlay"});const fo=document.createElement("template");fo.innerHTML='\n \n',document.head.appendChild(fo.content);const go=i` :host { display: flex; align-items: center; box-sizing: border-box; min-height: 36px; padding: 8px 32px 8px 10px; overflow: hidden; font-family: var(--material-font-family); font-size: var(--material-small-font-size); line-height: 24px; } /* It's the list-box's responsibility to add the focus style */ :host([focused]) { outline: none; } /* Checkmark */ [part='checkmark']::before { display: var(--_material-item-selected-icon-display, none); content: ''; font-family: material-icons; font-size: 24px; line-height: 1; font-weight: 400; width: 24px; text-align: center; margin-right: 10px; color: var(--material-secondary-text-color); flex: none; } :host([selected]) [part='checkmark']::before { content: var(--material-icons-check); } @media (any-hover: hover) { :host(:hover:not([disabled])) { background-color: var(--material-secondary-background-color); } :host([focused]:not([disabled])) { background-color: var(--material-divider-color); } } /* Disabled */ :host([disabled]) { color: var(--material-disabled-text-color); cursor: default; pointer-events: none; } /* RTL specific styles */ :host([dir='rtl']) { padding: 8px 10px 8px 32px; } :host([dir='rtl']) [part='checkmark']::before { margin-right: 0; margin-left: 10px; } `;Pn("vaadin-item",go,{moduleId:"material-item"});Pn("vaadin-combo-box-item",[go,i` :host { cursor: pointer; -webkit-tap-highlight-color: transparent; padding: 4px 10px; --_material-item-selected-icon-display: block; } `],{moduleId:"material-combo-box-item"});class _o extends(Vn(so(n))){static get template(){return o`
`}static get is(){return"vaadin-combo-box-item"}static get properties(){return{index:Number,item:Object,label:String,selected:{type:Boolean,value:!1,reflectToAttribute:!0},focused:{type:Boolean,value:!1,reflectToAttribute:!0},renderer:Function,_oldRenderer:Function}}static get observers(){return["__rendererOrItemChanged(renderer, index, item.*, selected, focused)","__updateLabel(label, renderer)"]}connectedCallback(){super.connectedCallback(),this._comboBox=this.parentNode.comboBox;const e=this._comboBox.getAttribute("dir");e&&this.setAttribute("dir",e)}requestContentUpdate(){if(!this.renderer)return;const e={index:this.index,item:this.item,focused:this.focused,selected:this.selected};this.renderer(this,this._comboBox,e)}__rendererOrItemChanged(e,t,i){void 0!==i&&void 0!==t&&(this._oldRenderer!==e&&(this.innerHTML="",delete this._$litPart$),e&&(this._oldRenderer=e,this.requestContentUpdate()))}__updateLabel(e,t){t||(this.textContent=e)}}customElements.define(_o.is,_o);const yo={start:"top",end:"bottom"},ko={start:"left",end:"right"},bo=e=>class extends e{static get properties(){return{positionTarget:{type:Object,value:null},horizontalAlign:{type:String,value:"start"},verticalAlign:{type:String,value:"top"},noHorizontalOverlap:{type:Boolean,value:!1},noVerticalOverlap:{type:Boolean,value:!1}}}static get observers(){return["__positionSettingsChanged(horizontalAlign, verticalAlign, noHorizontalOverlap, noVerticalOverlap)","__overlayOpenedChanged(opened, positionTarget)"]}constructor(){super(),this._updatePosition=this._updatePosition.bind(this)}connectedCallback(){super.connectedCallback(),this.opened&&this.__addUpdatePositionEventListeners()}disconnectedCallback(){super.disconnectedCallback(),this.__removeUpdatePositionEventListeners()}__addUpdatePositionEventListeners(){window.addEventListener("resize",this._updatePosition),this.__positionTargetAncestorRootNodes=function(e){const t=[];for(;e;){if(e.nodeType===Node.DOCUMENT_NODE){t.push(e);break}e.nodeType!==Node.DOCUMENT_FRAGMENT_NODE?e=e.assignedSlot?e.assignedSlot:e.parentNode:(t.push(e),e=e.host)}return t}(this.positionTarget),this.__positionTargetAncestorRootNodes.forEach((e=>{e.addEventListener("scroll",this._updatePosition,!0)}))}__removeUpdatePositionEventListeners(){window.removeEventListener("resize",this._updatePosition),this.__positionTargetAncestorRootNodes&&(this.__positionTargetAncestorRootNodes.forEach((e=>{e.removeEventListener("scroll",this._updatePosition,!0)})),this.__positionTargetAncestorRootNodes=null)}__overlayOpenedChanged(e,t){if(this.__removeUpdatePositionEventListeners(),e&&t&&this.__addUpdatePositionEventListeners(),e){const e=getComputedStyle(this);this.__margins||(this.__margins={},["top","bottom","left","right"].forEach((t=>{this.__margins[t]=parseInt(e[t],10)}))),this.setAttribute("dir",e.direction),this._updatePosition(),requestAnimationFrame((()=>this._updatePosition()))}}get __isRTL(){return"rtl"===this.getAttribute("dir")}__positionSettingsChanged(){this._updatePosition()}_updatePosition(){if(!this.positionTarget||!this.opened)return;const e=this.positionTarget.getBoundingClientRect(),t=this.__shouldAlignStartVertically(e);this.style.justifyContent=t?"flex-start":"flex-end";const i=this.__shouldAlignStartHorizontally(e,this.__isRTL),a=!this.__isRTL&&i||this.__isRTL&&!i;this.style.alignItems=a?"flex-start":"flex-end";const n=this.getBoundingClientRect(),o=this.__calculatePositionInOneDimension(e,n,this.noVerticalOverlap,yo,this,t),s=this.__calculatePositionInOneDimension(e,n,this.noHorizontalOverlap,ko,this,i);Object.assign(this.style,o,s),this.toggleAttribute("bottom-aligned",!t),this.toggleAttribute("top-aligned",t),this.toggleAttribute("end-aligned",!a),this.toggleAttribute("start-aligned",a)}__shouldAlignStartHorizontally(e,t){const i=Math.max(this.__oldContentWidth||0,this.$.overlay.offsetWidth);this.__oldContentWidth=this.$.overlay.offsetWidth;const a=Math.min(window.innerWidth,document.documentElement.clientWidth),n=!t&&"start"===this.horizontalAlign||t&&"end"===this.horizontalAlign;return this.__shouldAlignStart(e,i,a,this.__margins,n,this.noHorizontalOverlap,ko)}__shouldAlignStartVertically(e){const t=Math.max(this.__oldContentHeight||0,this.$.overlay.offsetHeight);this.__oldContentHeight=this.$.overlay.offsetHeight;const i=Math.min(window.innerHeight,document.documentElement.clientHeight),a="top"===this.verticalAlign;return this.__shouldAlignStart(e,t,i,this.__margins,a,this.noVerticalOverlap,yo)}__shouldAlignStart(e,t,i,a,n,o,s){const r=i-e[o?s.end:s.start]-a[s.end],l=e[o?s.start:s.end]-a[s.start],d=n?r:l;return n===(d>(n?l:r)||d>t)}__calculatePositionInOneDimension(e,t,i,a,n,o){const s=o?a.start:a.end,r=o?a.end:a.start;return{[s]:`${parseFloat(n.style[s]||getComputedStyle(n)[s])+(t[o?a.start:a.end]-e[i===o?a.end:a.start])*(o?-1:1)}px`,[r]:""}}};let xo;Pn("vaadin-combo-box-overlay",i` #overlay { width: var(--vaadin-combo-box-overlay-width, var(--_vaadin-combo-box-overlay-default-width, auto)); } [part='content'] { display: flex; flex-direction: column; height: 100%; } `,{moduleId:"vaadin-combo-box-overlay-styles"});class $o extends(bo(vo)){static get is(){return"vaadin-combo-box-overlay"}static get template(){return xo||(xo=super.template.cloneNode(!0),xo.content.querySelector('[part~="overlay"]').removeAttribute("tabindex")),xo}static get observers(){return["_setOverlayWidth(positionTarget, opened)"]}connectedCallback(){super.connectedCallback();const e=this.__dataHost,t=e&&e.getRootNode().host;this._comboBox=t;const i=t&&t.getAttribute("dir");i&&this.setAttribute("dir",i)}ready(){super.ready();const e=document.createElement("div");e.setAttribute("part","loader");const t=this.shadowRoot.querySelector('[part~="content"]');t.parentNode.insertBefore(e,t)}_outsideClickListener(e){const t=e.composedPath();t.includes(this.positionTarget)||t.includes(this)||this.close()}_setOverlayWidth(e,t){if(e&&t){const t=this.localName;this.style.setProperty(`--_${t}-default-width`,`${e.clientWidth}px`);const i=getComputedStyle(this._comboBox).getPropertyValue(`--${t}-width`);""===i?this.style.removeProperty(`--${t}-width`):this.style.setProperty(`--${t}-width`,i),this._updatePosition()}}}customElements.define($o.is,$o);let wo=0,Co=0;const Ao=[];let Io=0,Eo=!1;const zo=document.createTextNode("");new window.MutationObserver((function(){Eo=!1;const e=Ao.length;for(let t=0;t{throw e}))}}Ao.splice(0,e),Co+=e})).observe(zo,{characterData:!0});const So={after:e=>({run:t=>window.setTimeout(t,e),cancel(e){window.clearTimeout(e)}}),run:(e,t)=>window.setTimeout(e,t),cancel(e){window.clearTimeout(e)}},Lo={run:e=>window.requestAnimationFrame(e),cancel(e){window.cancelAnimationFrame(e)}},To={run:e=>window.requestIdleCallback?window.requestIdleCallback(e):window.setTimeout(e,16),cancel(e){window.cancelIdleCallback?window.cancelIdleCallback(e):window.clearTimeout(e)}},Oo={run(e){Eo||(Eo=!0,zo.textContent=Io,Io+=1),Ao.push(e);const t=wo;return wo+=1,t},cancel(e){const t=e-Co;if(t>=0){if(!Ao[t])throw new Error(`invalid async handle: ${e}`);Ao[t]=null}}};class Po{static debounce(e,t,i){return e instanceof Po?e._cancelAsync():e=new Po,e.setConfig(t,i),e}constructor(){this._asyncModule=null,this._callback=null,this._timer=null}setConfig(e,t){this._asyncModule=e,this._callback=t,this._timer=this._asyncModule.run((()=>{this._timer=null,Mo.delete(this),this._callback()}))}cancel(){this.isActive()&&(this._cancelAsync(),Mo.delete(this))}_cancelAsync(){this.isActive()&&(this._asyncModule.cancel(this._timer),this._timer=null)}flush(){this.isActive()&&(this.cancel(),this._callback())}isActive(){return null!=this._timer}}let Mo=new Set;function Fo(){const e=Boolean(Mo.size);return Mo.forEach((e=>{try{e.flush()}catch(e){setTimeout((()=>{throw e}))}})),e}const Do=()=>{let e;do{e=Fo()}while(e)},Bo=navigator.userAgent.match(/iP(?:hone|ad;(?: U;)? CPU) OS (\d+)/),No=Bo&&Bo[1]>=8,Vo={_ratio:.5,_scrollerPaddingTop:0,_scrollPosition:0,_physicalSize:0,_physicalAverage:0,_physicalAverageCount:0,_physicalTop:0,_virtualCount:0,_estScrollHeight:0,_scrollHeight:0,_viewportHeight:0,_viewportWidth:0,_physicalItems:null,_physicalSizes:null,_firstVisibleIndexVal:null,_lastVisibleIndexVal:null,_maxPages:2,_templateCost:0,get _physicalBottom(){return this._physicalTop+this._physicalSize},get _scrollBottom(){return this._scrollPosition+this._viewportHeight},get _virtualEnd(){return this._virtualStart+this._physicalCount-1},get _hiddenContentSize(){return this._physicalSize-this._viewportHeight},get _maxScrollTop(){return this._estScrollHeight-this._viewportHeight+this._scrollOffset},get _maxVirtualStart(){const e=this._virtualCount;return Math.max(0,e-this._physicalCount)},get _virtualStart(){return this._virtualStartVal||0},set _virtualStart(e){e=this._clamp(e,0,this._maxVirtualStart),this._virtualStartVal=e},get _physicalStart(){return this._physicalStartVal||0},set _physicalStart(e){(e%=this._physicalCount)<0&&(e=this._physicalCount+e),this._physicalStartVal=e},get _physicalEnd(){return(this._physicalStart+this._physicalCount-1)%this._physicalCount},get _physicalCount(){return this._physicalCountVal||0},set _physicalCount(e){this._physicalCountVal=e},get _optPhysicalSize(){return 0===this._viewportHeight?1/0:this._viewportHeight*this._maxPages},get _isVisible(){return Boolean(this.offsetWidth||this.offsetHeight)},get firstVisibleIndex(){let e=this._firstVisibleIndexVal;if(null==e){let t=this._physicalTop+this._scrollOffset;e=this._iterateItems(((e,i)=>{if(t+=this._getPhysicalSizeIncrement(e),t>this._scrollPosition)return i}))||0,this._firstVisibleIndexVal=e}return e},get lastVisibleIndex(){let e=this._lastVisibleIndexVal;if(null==e){let t=this._physicalTop+this._scrollOffset;this._iterateItems(((i,a)=>{t=0;if(this._scrollPosition=e,this._firstVisibleIndexVal=null,this._lastVisibleIndexVal=null,Math.abs(t)>this._physicalSize&&this._physicalSize>0){t-=this._scrollOffset;const e=Math.round(t/this._physicalAverage);this._virtualStart+=e,this._physicalStart+=e,this._physicalTop=Math.min(Math.floor(this._virtualStart)*this._physicalAverage,this._scrollPosition),this._update()}else if(this._physicalCount>0){const e=this._getReusables(i);i?(this._physicalTop=e.physicalTop,this._virtualStart+=e.indexes.length,this._physicalStart+=e.indexes.length):(this._virtualStart-=e.indexes.length,this._physicalStart-=e.indexes.length),this._update(e.indexes,i?null:e.indexes),this._debounce("_increasePoolIfNeeded",this._increasePoolIfNeeded.bind(this,0),Oo)}},_getReusables(e){let t,i,a;const n=[],o=this._hiddenContentSize*this._ratio,s=this._virtualStart,r=this._virtualEnd,l=this._physicalCount;let d=this._physicalTop+this._scrollOffset;const c=this._physicalBottom+this._scrollOffset,u=this._scrollPosition,h=this._scrollBottom;for(e?(t=this._physicalStart,i=u-d):(t=this._physicalEnd,i=c-h);a=this._getPhysicalSizeIncrement(t),i-=a,!(n.length>=l||i<=o);)if(e){if(r+n.length+1>=this._virtualCount)break;if(d+a>=u-this._scrollOffset)break;n.push(t),d+=a,t=(t+1)%l}else{if(s-n.length<=0)break;if(d+this._physicalSize-a<=h)break;n.push(t),d-=a,t=0===t?l-1:t-1}return{indexes:n,physicalTop:d-this._scrollOffset}},_update(e,t){if(!(e&&0===e.length||0===this._physicalCount)){if(this._assignModels(e),this._updateMetrics(e),t)for(;t.length;){const e=t.pop();this._physicalTop-=this._getPhysicalSizeIncrement(e)}this._positionItems(),this._updateScrollerSize()}},_isClientFull(){return 0!==this._scrollBottom&&this._physicalBottom-1>=this._scrollBottom&&this._physicalTop<=this._scrollPosition},_increasePoolIfNeeded(e){const t=this._clamp(this._physicalCount+e,3,this._virtualCount-this._virtualStart)-this._physicalCount;let i=Math.round(.5*this._physicalCount);if(!(t<0)){if(t>0){const e=window.performance.now();[].push.apply(this._physicalItems,this._createPool(t));for(let e=0;ethis._physicalEnd&&this._isIndexRendered(this._focusedVirtualIndex)&&this._getPhysicalIndex(this._focusedVirtualIndex)=this._virtualCount-1||0===i||(this._isClientFull()?this._physicalSize0&&(this.updateViewportBoundaries(),this._increasePoolIfNeeded(3))},_itemsChanged(e){"items"===e.path&&(this._virtualStart=0,this._physicalTop=0,this._virtualCount=this.items?this.items.length:0,this._physicalIndexForKey={},this._firstVisibleIndexVal=null,this._lastVisibleIndexVal=null,this._physicalCount=this._physicalCount||0,this._physicalItems=this._physicalItems||[],this._physicalSizes=this._physicalSizes||[],this._physicalStart=0,this._scrollTop>this._scrollOffset&&this._resetScrollPosition(0),this._debounce("_render",this._render,Lo))},_iterateItems(e,t){let i,a,n,o;if(2===arguments.length&&t){for(o=0;o=this._physicalStart?this._virtualStart+(e-this._physicalStart):this._virtualStart+(this._physicalCount-this._physicalStart)+e},_updateMetrics(e){Do();let t=0,i=0;const a=this._physicalAverageCount,n=this._physicalAverage;this._iterateItems(((e,a)=>{i+=this._physicalSizes[e],this._physicalSizes[e]=this._physicalItems[e].offsetHeight,t+=this._physicalSizes[e],this._physicalAverageCount+=this._physicalSizes[e]?1:0}),e),this._physicalSize=this._physicalSize+t-i,this._physicalAverageCount!==a&&(this._physicalAverage=Math.round((n*a+t)/this._physicalAverageCount))},_positionItems(){this._adjustScrollPosition();let e=this._physicalTop;this._iterateItems((t=>{this.translate3d(0,`${e}px`,0,this._physicalItems[t]),e+=this._physicalSizes[t]}))},_getPhysicalSizeIncrement(e){return this._physicalSizes[e]},_adjustScrollPosition(){const e=0===this._virtualStart?this._physicalTop:Math.min(this._scrollPosition+this._physicalTop,0);if(0!==e){this._physicalTop-=e;const t=this._scrollPosition;!No&&t>0&&this._resetScrollPosition(t-e)}},_resetScrollPosition(e){this.scrollTarget&&e>=0&&(this._scrollTop=e,this._scrollPosition=this._scrollTop)},_updateScrollerSize(e){this._estScrollHeight=this._physicalBottom+Math.max(this._virtualCount-this._physicalCount-this._virtualStart,0)*this._physicalAverage,((e=(e=e||0===this._scrollHeight)||this._scrollPosition>=this._estScrollHeight-this._physicalSize)||Math.abs(this._estScrollHeight-this._scrollHeight)>=this._viewportHeight)&&(this.$.items.style.height=`${this._estScrollHeight}px`,this._scrollHeight=this._estScrollHeight)},scrollToIndex(e){if("number"!=typeof e||e<0||e>this.items.length-1)return;if(Do(),0===this._physicalCount)return;e=this._clamp(e,0,this._virtualCount-1),(!this._isIndexRendered(e)||e>=this._maxVirtualStart)&&(this._virtualStart=e-1),this._assignModels(),this._updateMetrics(),this._physicalTop=this._virtualStart*this._physicalAverage;let t=this._physicalStart,i=this._virtualStart,a=0;const n=this._hiddenContentSize;for(;i{this._firstVisibleIndexVal=null,this._lastVisibleIndexVal=null,this._isVisible?(this.updateViewportBoundaries(),this.toggleScrollListener(!0),this._resetAverage(),this._render()):this.toggleScrollListener(!1)}),Lo)},_isIndexRendered(e){return e>=this._virtualStart&&e<=this._virtualEnd},_getPhysicalIndex(e){return(this._physicalStart+(e-this._virtualStart))%this._physicalCount},_clamp:(e,t,i)=>Math.min(i,Math.max(t,e)),_debounce(e,t,i){var a;this._debouncers=this._debouncers||{},this._debouncers[e]=Po.debounce(this._debouncers[e],i,t.bind(this)),a=this._debouncers[e],Mo.add(a)}};class jo{constructor({createElements:e,updateElement:t,scrollTarget:i,scrollContainer:a,elementsContainer:n,reorderElements:o}){this.isAttached=!0,this._vidxOffset=0,this.createElements=e,this.updateElement=t,this.scrollTarget=i,this.scrollContainer=a,this.elementsContainer=n||a,this.reorderElements=o,this._maxPages=1.3,this.__placeholderHeight=200,this.__elementHeightQueue=Array(10),this.timeouts={SCROLL_REORDER:500,IGNORE_WHEEL:500},this.__resizeObserver=new ResizeObserver((()=>this._resizeHandler())),"visible"===getComputedStyle(this.scrollTarget).overflow&&(this.scrollTarget.style.overflow="auto"),"static"===getComputedStyle(this.scrollContainer).position&&(this.scrollContainer.style.position="relative"),this.__resizeObserver.observe(this.scrollTarget),this.scrollTarget.addEventListener("scroll",(()=>this._scrollHandler())),this._scrollLineHeight=this._getScrollLineHeight(),this.scrollTarget.addEventListener("wheel",(e=>this.__onWheel(e))),this.reorderElements&&(this.scrollTarget.addEventListener("mousedown",(()=>this.__mouseDown=!0)),this.scrollTarget.addEventListener("mouseup",(()=>{this.__mouseDown=!1,this.__pendingReorder&&this.__reorderElements()})))}get scrollOffset(){return 0}get adjustedFirstVisibleIndex(){return this.firstVisibleIndex+this._vidxOffset}get adjustedLastVisibleIndex(){return this.lastVisibleIndex+this._vidxOffset}scrollToIndex(e){if("number"!=typeof e||isNaN(e)||0===this.size||!this.scrollTarget.offsetHeight)return;e=this._clamp(e,0,this.size-1);const t=this.__getVisibleElements().length;let i=Math.floor(e/this.size*this._virtualCount);this._virtualCount-i{i.__virtualIndex>=e&&i.__virtualIndex<=t&&this.__updateElement(i,i.__virtualIndex,!0)}))}__updateElement(e,t,i){e.style.paddingTop&&(e.style.paddingTop=""),this.__preventElementUpdates||e.__lastUpdatedIndex===t&&!i||(this.updateElement(e,t),e.__lastUpdatedIndex=t);const a=e.offsetHeight;if(0===a)e.style.paddingTop=`${this.__placeholderHeight}px`;else{this.__elementHeightQueue.push(a),this.__elementHeightQueue.shift();const e=this.__elementHeightQueue.filter((e=>void 0!==e));this.__placeholderHeight=Math.round(e.reduce(((e,t)=>e+t),0)/e.length)}}__getIndexScrollOffset(e){const t=this.__getVisibleElements().find((t=>t.__virtualIndex===e));return t?this.scrollTarget.getBoundingClientRect().top-t.getBoundingClientRect().top:void 0}get size(){return this.__size}set size(e){if(e===this.size)return;let t,i;if(this.__preventElementUpdates=!0,e>0&&(t=this.adjustedFirstVisibleIndex,i=this.__getIndexScrollOffset(t)),this.__size=e,Do(),this._itemsChanged({path:"items"}),Do(),e>0){t=Math.min(t,e-1),this.scrollToIndex(t);const a=this.__getIndexScrollOffset(t);void 0!==i&&void 0!==a&&(this._scrollTop+=i-a)}this.elementsContainer.children.length||requestAnimationFrame((()=>this._resizeHandler())),this.__preventElementUpdates=!1,this._resizeHandler(),Do()}get _scrollTop(){return this.scrollTarget.scrollTop}set _scrollTop(e){this.scrollTarget.scrollTop=e}get items(){return{length:Math.min(this.size,1e5)}}get offsetHeight(){return this.scrollTarget.offsetHeight}get $(){return{items:this.scrollContainer}}updateViewportBoundaries(){const e=window.getComputedStyle(this.scrollTarget);this._scrollerPaddingTop=this.scrollTarget===this?0:parseInt(e["padding-top"],10),this._isRTL=Boolean("rtl"===e.direction),this._viewportWidth=this.elementsContainer.offsetWidth,this._viewportHeight=this.scrollTarget.offsetHeight,this._scrollPageHeight=this._viewportHeight-this._scrollLineHeight,this.grid&&this._updateGridMetrics()}setAttribute(){}_createPool(e){const t=this.createElements(e),i=document.createDocumentFragment();return t.forEach((e=>{e.style.position="absolute",i.appendChild(e),this.__resizeObserver.observe(e)})),this.elementsContainer.appendChild(i),t}_assignModels(e){this._iterateItems(((e,t)=>{const i=this._physicalItems[e];i.hidden=t>=this.size,i.hidden?delete i.__lastUpdatedIndex:(i.__virtualIndex=t+(this._vidxOffset||0),this.__updateElement(i,i.__virtualIndex))}),e)}_isClientFull(){return setTimeout((()=>this.__clientFull=!0)),this.__clientFull||super._isClientFull()}translate3d(e,t,i,a){a.style.transform=`translateY(${t})`}toggleScrollListener(){}_scrollHandler(){this._adjustVirtualIndexOffset(this._scrollTop-(this.__previousScrollTop||0));const e=this.scrollTarget.scrollTop-this._scrollPosition;if(super._scrollHandler(),0!==this._physicalCount){const t=e>=0,i=this._getReusables(!t);i.indexes.length&&(this._physicalTop=i.physicalTop,t?(this._virtualStart-=i.indexes.length,this._physicalStart-=i.indexes.length):(this._virtualStart+=i.indexes.length,this._physicalStart+=i.indexes.length),this._resizeHandler())}this.reorderElements&&(this.__scrollReorderDebouncer=Po.debounce(this.__scrollReorderDebouncer,So.after(this.timeouts.SCROLL_REORDER),(()=>this.__reorderElements()))),this.__previousScrollTop=this._scrollTop}__onWheel(e){if(e.ctrlKey||this._hasScrolledAncestor(e.target,e.deltaX,e.deltaY))return;let t=e.deltaY;if(e.deltaMode===WheelEvent.DOM_DELTA_LINE?t*=this._scrollLineHeight:e.deltaMode===WheelEvent.DOM_DELTA_PAGE&&(t*=this._scrollPageHeight),this._deltaYAcc=this._deltaYAcc||0,this._wheelAnimationFrame)return this._deltaYAcc+=t,void e.preventDefault();t+=this._deltaYAcc,this._deltaYAcc=0,this._wheelAnimationFrame=!0,this.__debouncerWheelAnimationFrame=Po.debounce(this.__debouncerWheelAnimationFrame,Lo,(()=>this._wheelAnimationFrame=!1));const i=Math.abs(e.deltaX)+Math.abs(t);this._canScroll(this.scrollTarget,e.deltaX,t)?(e.preventDefault(),this.scrollTarget.scrollTop+=t,this.scrollTarget.scrollLeft+=e.deltaX,this._hasResidualMomentum=!0,this._ignoreNewWheel=!0,this._debouncerIgnoreNewWheel=Po.debounce(this._debouncerIgnoreNewWheel,So.after(this.timeouts.IGNORE_WHEEL),(()=>this._ignoreNewWheel=!1))):this._hasResidualMomentum&&i<=this._previousMomentum||this._ignoreNewWheel?e.preventDefault():i>this._previousMomentum&&(this._hasResidualMomentum=!1),this._previousMomentum=i}_hasScrolledAncestor(e,t,i){return e!==this.scrollTarget&&e!==this.scrollTarget.getRootNode().host&&(!(!this._canScroll(e,t,i)||-1===["auto","scroll"].indexOf(getComputedStyle(e).overflow))||(e!==this&&e.parentElement?this._hasScrolledAncestor(e.parentElement,t,i):void 0))}_canScroll(e,t,i){return i>0&&e.scrollTop0||t>0&&e.scrollLeft0}_getScrollLineHeight(){const e=document.createElement("div");e.style.fontSize="initial",e.style.display="none",document.body.appendChild(e);const t=window.getComputedStyle(e).fontSize;return document.body.removeChild(e),t?window.parseInt(t):void 0}__getVisibleElements(){return Array.from(this.elementsContainer.children).filter((e=>!e.hidden))}__reorderElements(){if(this.__mouseDown)return void(this.__pendingReorder=!0);this.__pendingReorder=!1;const e=this._virtualStart+(this._vidxOffset||0),t=this.__getVisibleElements(),i=t.find((e=>e.contains(this.elementsContainer.getRootNode().activeElement)||e.contains(this.scrollTarget.getRootNode().activeElement)))||t[0];if(!i)return;const a=i.__virtualIndex-e,n=t.indexOf(i)-a;if(n>0)for(let e=0;ethis.scrollTarget.style.transform=e))}}_adjustVirtualIndexOffset(e){if(this._virtualCount>=this.size)this._vidxOffset=0;else if(this.__skipNextVirtualIndexAdjust)this.__skipNextVirtualIndexAdjust=!1;else if(Math.abs(e)>1e4){const e=this._scrollTop/(this.scrollTarget.scrollHeight-this.scrollTarget.offsetHeight),t=e*this.size;this._vidxOffset=Math.round(t-e*this._virtualCount)}else{const e=this._vidxOffset,t=1e3,i=100;0===this._scrollTop?(this._vidxOffset=0,e!==this._vidxOffset&&super.scrollToIndex(0)):this.firstVisibleIndex0&&(this._vidxOffset-=Math.min(this._vidxOffset,i),super.scrollToIndex(this.firstVisibleIndex+(e-this._vidxOffset)));const a=this.size-this._virtualCount;this._scrollTop>=this._maxScrollTop&&this._maxScrollTop>0?(this._vidxOffset=a,e!==this._vidxOffset&&super.scrollToIndex(this._virtualCount-1)):this.firstVisibleIndex>this._virtualCount-t&&this._vidxOffset :host { display: block; min-height: 1px; overflow: auto; /* Fixes item background from getting on top of scrollbars on Safari */ transform: translate3d(0, 0, 0); /* Enable momentum scrolling on iOS */ -webkit-overflow-scrolling: touch; /* Fixes scrollbar disappearing when 'Show scroll bars: Always' enabled in Safari */ box-shadow: 0 0 0 white; } #selector { border-width: var(--_vaadin-combo-box-items-container-border-width); border-style: var(--_vaadin-combo-box-items-container-border-style); border-color: var(--_vaadin-combo-box-items-container-border-color); }
`}static get properties(){return{items:{type:Array,observer:"__itemsChanged"},focusedIndex:{type:Number,observer:"__focusedIndexChanged"},loading:{type:Boolean,observer:"__loadingChanged"},opened:{type:Boolean,observer:"__openedChanged"},selectedItem:{type:Object},itemIdPath:{type:String},comboBox:{type:Object},getItemLabel:{type:Object},renderer:{type:Object,observer:"__rendererChanged"},theme:{type:String}}}constructor(){super(),this.__boundOnItemClick=this.__onItemClick.bind(this)}__openedChanged(e){e&&this.requestContentUpdate()}ready(){super.ready(),this.__hostTagName=this.constructor.is.replace("-scroller",""),this.setAttribute("role","listbox"),this.addEventListener("click",(e=>e.stopPropagation())),this.__patchWheelOverScrolling(),this.__virtualizer=new qo({createElements:this.__createElements.bind(this),updateElement:this.__updateElement.bind(this),elementsContainer:this,scrollTarget:this,scrollContainer:this.$.selector})}requestContentUpdate(){this.__virtualizer&&this.__virtualizer.update()}scrollIntoView(e){if(!(this.opened&&e>=0))return;const t=this._visibleItemsCount();let i=e;e>this.__virtualizer.lastVisibleIndex-1?(this.__virtualizer.scrollToIndex(e),i=e-t+1):e>this.__virtualizer.firstVisibleIndex&&(i=this.__virtualizer.firstVisibleIndex),this.__virtualizer.scrollToIndex(Math.max(0,i));const a=[...this.children].find((e=>!e.hidden&&e.index===this.__virtualizer.lastVisibleIndex));if(!a||e!==a.index)return;const n=a.getBoundingClientRect(),o=this.getBoundingClientRect(),s=n.bottom-o.bottom+this._viewportTotalPaddingBottom;s>0&&(this.scrollTop+=s)}__getAriaRole(e){return void 0!==e&&"option"}__getAriaSelected(e,t){return this.__isItemFocused(e,t).toString()}__isItemFocused(e,t){return e===t}__isItemSelected(e,t,i){return!(e instanceof Ro)&&(i&&void 0!==e&&void 0!==t?this.get(i,e)===this.get(i,t):e===t)}__itemsChanged(e){this.__virtualizer&&e&&(this.__virtualizer.size=e.length,this.__virtualizer.flush(),this.setAttribute("aria-setsize",e.length),this.requestContentUpdate())}__loadingChanged(e){this.__virtualizer&&!e&&setTimeout((()=>this.requestContentUpdate()))}__focusedIndexChanged(e,t){this.__virtualizer&&(e!==t&&this.requestContentUpdate(),e>=0&&!this.loading&&this.scrollIntoView(e))}__rendererChanged(e,t){(e||t)&&this.requestContentUpdate()}__createElements(e){return[...Array(e)].map((()=>{const e=document.createElement(`${this.__hostTagName}-item`);return e.addEventListener("click",this.__boundOnItemClick),e.tabIndex="-1",e.style.width="100%",e}))}__updateElement(e,t){const i=this.items[t],a=this.focusedIndex;e.setProperties({item:i,index:this.__requestItemByIndex(i,t),label:this.getItemLabel(i),selected:this.__isItemSelected(i,this.selectedItem,this.itemIdPath),renderer:this.renderer,focused:this.__isItemFocused(a,t)}),e.id=`${this.__hostTagName}-item-${t}`,e.setAttribute("role",this.__getAriaRole(t)),e.setAttribute("aria-selected",this.__getAriaSelected(a,t)),e.setAttribute("aria-posinset",t+1),this.theme?e.setAttribute("theme",this.theme):e.removeAttribute("theme")}__onItemClick(e){this.dispatchEvent(new CustomEvent("selection-changed",{detail:{item:e.currentTarget.item}}))}__patchWheelOverScrolling(){this.$.selector.addEventListener("wheel",(e=>{const t=0===this.scrollTop,i=this.scrollHeight-this.scrollTop-this.clientHeight<=1;(t&&e.deltaY<0||i&&e.deltaY>0)&&e.preventDefault()}))}get _viewportTotalPaddingBottom(){if(void 0===this._cachedViewportTotalPaddingBottom){const e=window.getComputedStyle(this.$.selector);this._cachedViewportTotalPaddingBottom=[e.paddingBottom,e.borderBottomWidth].map((e=>parseInt(e,10))).reduce(((e,t)=>e+t))}return this._cachedViewportTotalPaddingBottom}__requestItemByIndex(e,t){return e instanceof Ro&&void 0!==t&&this.dispatchEvent(new CustomEvent("index-requested",{detail:{index:t,currentScrollerPos:this._oldScrollerPosition}})),t}_visibleItemsCount(){this.__virtualizer.scrollToIndex(this.__virtualizer.firstVisibleIndex);return this.__virtualizer.size>0?this.__virtualizer.lastVisibleIndex-this.__virtualizer.firstVisibleIndex+1:0}}customElements.define(Uo.is,Uo);class Ho extends n{static get is(){return"vaadin-combo-box-dropdown"}static get template(){return o` `}static get properties(){return{opened:Boolean,positionTarget:{type:Object},renderer:Function,loading:{type:Boolean,value:!1,reflectToAttribute:!0},theme:String,_selectedItem:{type:Object},_items:{type:Array},_focusedIndex:{type:Number,value:-1},focusedItem:{type:String,computed:"_getFocusedItem(_focusedIndex)"},_itemLabelPath:{type:String,value:"label"},_itemValuePath:{type:String,value:"value"},_scroller:Object,_itemIdPath:String,_overlayOpened:{type:Boolean,observer:"_openedChanged"}}}static get observers(){return["_openedOrItemsChanged(opened, _items, loading)","__updateScroller(_scroller, _items, opened, loading, _selectedItem, _itemIdPath, _focusedIndex, renderer, theme)"]}constructor(){super();const e=Ho._uniqueId=1+Ho._uniqueId||0;this.scrollerId=`${this.localName}-scroller-${e}`}ready(){super.ready(),this.__hostTagName=this.constructor.is.replace("-dropdown","");const e=this.$.overlay,t=`${this.__hostTagName}-scroller`;e.renderer=e=>{if(!e.firstChild){const i=document.createElement(t);e.appendChild(i)}},e.requestContentUpdate(),this._scroller=e.content.querySelector(t),this._scroller.id=this.scrollerId,this._scroller.getItemLabel=this.getItemLabel.bind(this),this._scroller.comboBox=this.getRootNode().host,this._scroller.addEventListener("selection-changed",(e=>this._forwardScrollerEvent(e))),this._scroller.addEventListener("index-requested",(e=>this._forwardScrollerEvent(e))),e.addEventListener("touchend",(e=>this._fireTouchAction(e))),e.addEventListener("touchmove",(e=>this._fireTouchAction(e))),e.addEventListener("mousedown",(e=>e.preventDefault())),e.addEventListener("vaadin-overlay-outside-click",(e=>{e.preventDefault()}))}disconnectedCallback(){super.disconnectedCallback(),this._overlayOpened=!1}_fireTouchAction(e){this.dispatchEvent(new CustomEvent("vaadin-overlay-touch-action",{detail:{sourceEvent:e}}))}_forwardScrollerEvent(e){this.dispatchEvent(new CustomEvent(e.type,{detail:e.detail}))}_openedChanged(e,t){e?(this._scroller.style.maxHeight=getComputedStyle(this).getPropertyValue(`--${this.__hostTagName}-overlay-max-height`)||"65vh",this.dispatchEvent(new CustomEvent("vaadin-combo-box-dropdown-opened",{bubbles:!0,composed:!0}))):t&&!this.__emptyItems&&this.dispatchEvent(new CustomEvent("vaadin-combo-box-dropdown-closed",{bubbles:!0,composed:!0}))}_openedOrItemsChanged(e,t,i){const a=t&&t.length;a||(this.__emptyItems=!0),this._overlayOpened=!(!e||!i&&!a),this.__emptyItems=!1}_getFocusedItem(e){if(e>=0)return this._items[e]}indexOfLabel(e){if(this._items&&e)for(let t=0;tclass extends e{static get properties(){return{pageSize:{type:Number,value:50,observer:"_pageSizeChanged"},size:{type:Number,observer:"_sizeChanged"},dataProvider:{type:Object,observer:"_dataProviderChanged"},_pendingRequests:{value:()=>({})},__placeHolder:{value:new Ro},__previousDataProviderFilter:{type:String}}}static get observers(){return["_dataProviderFilterChanged(filter)","_warnDataProviderValue(dataProvider, value)","_ensureFirstPage(opened)"]}ready(){super.ready(),this.$.dropdown.addEventListener("index-requested",(e=>{const t=e.detail.index,i=e.detail.currentScrollerPos,a=Math.floor(1.5*this.pageSize);if(!this._shouldSkipIndex(t,a,i)&&void 0!==t){const e=this._getPageForIndex(t);this._shouldLoadPage(e)&&this._loadPage(e)}}))}_dataProviderFilterChanged(e){void 0!==this.__previousDataProviderFilter||""!==e?this.__previousDataProviderFilter!==e&&(this.__previousDataProviderFilter=e,this._pendingRequests={},this.loading=this._shouldFetchData(),this.size=void 0,this.clearCache()):this.__previousDataProviderFilter=e}_shouldFetchData(){return!!this.dataProvider&&(this.opened||this.filter&&this.filter.length)}_ensureFirstPage(e){e&&this._shouldLoadPage(0)&&this._loadPage(0)}_shouldSkipIndex(e,t,i){return 0!==i&&e>=i-t&&e<=i+t}_shouldLoadPage(e){if(!this.filteredItems||this._forceNextRequest)return this._forceNextRequest=!1,!0;const t=this.filteredItems[e*this.pageSize];return void 0!==t?t instanceof Ro:void 0===this.size}_loadPage(e){if(!this._pendingRequests[e]&&this.dataProvider){this.loading=!0;const t={page:e,pageSize:this.pageSize,filter:this.filter},i=(a,n)=>{if(this._pendingRequests[e]===i){const i=this.filteredItems?[...this.filteredItems]:[];i.splice(t.page*t.pageSize,a.length,...a),this.filteredItems=i,this._isValidValue(this.value)&&this._getItemValue(this.selectedItem)!==this.value&&this._selectItemForValue(this.value),this.opened||this.hasAttribute("focused")||this._commitValue(),this.size=n,delete this._pendingRequests[e],0===Object.keys(this._pendingRequests).length&&(this.loading=!1)}};this._pendingRequests[e]||(this._pendingRequests[e]=i,this.dataProvider(t,i))}}_getPageForIndex(e){return Math.floor(e/this.pageSize)}clearCache(){if(!this.dataProvider)return;this._pendingRequests={};const e=[];for(let t=0;t<(this.size||0);t++)e.push(this.__placeHolder);this.filteredItems=e,this._shouldFetchData()?(this._forceNextRequest=!1,this._loadPage(0)):this._forceNextRequest=!0}_sizeChanged(e=0){const t=(this.filteredItems||[]).slice(0,e);for(let i=0;i 0");this.clearCache()}_dataProviderChanged(e,t){this._ensureItemsOrDataProvider((()=>{this.dataProvider=t})),this.clearCache()}_ensureItemsOrDataProvider(e){if(void 0!==this.items&&void 0!==this.dataProvider)throw e(),new Error("Using `items` and `dataProvider` together is not supported");this.dataProvider&&!this.filteredItems&&(this.filteredItems=[])}_warnDataProviderValue(e,t){if(e&&""!==t&&(void 0===this.selectedItem||null===this.selectedItem)){const e=this._indexOfValue(t,this.filteredItems);(e<0||!this._getItemLabel(this.filteredItems[e]))&&console.warn("Warning: unable to determine the label for the provided `value`. Nothing to display in the text field. This usually happens when setting an initial `value` before any items are returned from the `dataProvider` callback. Consider setting `selectedItem` instead of `value`")}}_flushPendingRequests(e){if(this._pendingRequests){const t=Math.ceil(e/this.pageSize),i=Object.keys(this._pendingRequests);for(let a=0;a=t&&this._pendingRequests[n]([],e)}}}},Wo=a((e=>class extends e{static get properties(){return{disabled:{type:Boolean,value:!1,observer:"_disabledChanged",reflectToAttribute:!0}}}_disabledChanged(e){this._setAriaDisabled(e)}_setAriaDisabled(e){e?this.setAttribute("aria-disabled","true"):this.removeAttribute("aria-disabled")}click(){this.disabled||super.click()}})),Ko=a((e=>class extends e{ready(){super.ready(),this.addEventListener("keydown",(e=>{this._onKeyDown(e)})),this.addEventListener("keyup",(e=>{this._onKeyUp(e)}))}_onKeyDown(e){switch(e.key){case"Enter":this._onEnter(e);break;case"Escape":this._onEscape(e)}}_onKeyUp(e){}_onEnter(e){}_onEscape(e){}}));const Yo=a((e=>class extends e{static get properties(){return{inputElement:{type:Object,readOnly:!0,observer:"_inputElementChanged"},type:{type:String,readOnly:!0},value:{type:String,value:"",observer:"_valueChanged",notify:!0}}}constructor(){super(),this._boundOnInput=this._onInput.bind(this),this._boundOnChange=this._onChange.bind(this)}clear(){this.value=""}_addInputListeners(e){e.addEventListener("input",this._boundOnInput),e.addEventListener("change",this._boundOnChange)}_removeInputListeners(e){e.removeEventListener("input",this._boundOnInput),e.removeEventListener("change",this._boundOnChange)}_forwardInputValue(e){this.inputElement&&(this.inputElement.value=null!=e?e:"")}_inputElementChanged(e,t){e?this._addInputListeners(e):t&&this._removeInputListeners(t)}_onInput(e){this.__userInput=e.isTrusted,this.value=e.target.value,this.__userInput=!1}_onChange(e){}_toggleHasValue(e){this.toggleAttribute("has-value",e)}_valueChanged(e,t){this._toggleHasValue(""!==e&&null!=e),""===e&&void 0===t||this.__userInput||this._forwardInputValue(e)}}));class Zo{constructor(e){this.host=e,e.addEventListener("opened-changed",(()=>{e.opened||this.__setVirtualKeyboardEnabled(!1)})),e.addEventListener("blur",(()=>this.__setVirtualKeyboardEnabled(!0))),e.addEventListener("touchstart",(()=>this.__setVirtualKeyboardEnabled(!0)))}__setVirtualKeyboardEnabled(e){this.host.inputElement&&(this.host.inputElement.inputMode=e?"":"none")}}const Qo=e=>class extends(eo(Ko(Yo(Wo(e))))){static get properties(){return{opened:{type:Boolean,notify:!0,value:!1,reflectToAttribute:!0,observer:"_openedChanged"},autoOpenDisabled:{type:Boolean},readonly:{type:Boolean,value:!1,reflectToAttribute:!0},renderer:Function,items:{type:Array,observer:"_itemsChanged"},allowCustomValue:{type:Boolean,value:!1},filteredItems:{type:Array},_lastCommittedValue:String,loading:{type:Boolean,value:!1,reflectToAttribute:!0,observer:"_loadingChanged"},_focusedIndex:{type:Number,observer:"_focusedIndexChanged",value:-1},filter:{type:String,value:"",notify:!0},selectedItem:{type:Object,notify:!0},itemLabelPath:{type:String,value:"label",observer:"_itemLabelPathChanged"},itemValuePath:{type:String,value:"value"},itemIdPath:String,_toggleElement:{type:Object,observer:"_toggleElementChanged"},_closeOnBlurIsPrevented:Boolean,__restoreFocusOnClose:Boolean}}static get observers(){return["_filterChanged(filter, itemValuePath, itemLabelPath)","_filteredItemsChanged(filteredItems)","_selectedItemChanged(selectedItem, itemValuePath, itemLabelPath)"]}constructor(){super(),this._boundOnFocusout=this._onFocusout.bind(this),this._boundOverlaySelectedItemChanged=this._overlaySelectedItemChanged.bind(this),this._boundOnClearButtonMouseDown=this.__onClearButtonMouseDown.bind(this),this._boundClose=this.close.bind(this),this._boundOnOpened=this._onOpened.bind(this),this._boundOnClick=this._onClick.bind(this),this._boundOnOverlayTouchAction=this._onOverlayTouchAction.bind(this),this._boundOnTouchend=this._onTouchend.bind(this)}get _inputElementValue(){return this.inputElement?this.inputElement[this._propertyForValue]:void 0}set _inputElementValue(e){this.inputElement&&(this.inputElement[this._propertyForValue]=e)}_inputElementChanged(e){super._inputElementChanged(e),e&&(e.autocomplete="off",e.autocapitalize="off",e.setAttribute("role","combobox"),e.setAttribute("aria-autocomplete","list"),e.setAttribute("aria-expanded",!!this.opened),e.setAttribute("spellcheck","false"),e.setAttribute("autocorrect","off"),this._revertInputValueToValue(),this.clearElement&&this.clearElement.addEventListener("mousedown",this._boundOnClearButtonMouseDown))}ready(){super.ready(),this.addEventListener("focusout",this._boundOnFocusout),this._lastCommittedValue=this.value,this.$.dropdown.addEventListener("selection-changed",this._boundOverlaySelectedItemChanged),this.addEventListener("vaadin-combo-box-dropdown-closed",this._boundClose),this.addEventListener("vaadin-combo-box-dropdown-opened",this._boundOnOpened),this.addEventListener("click",this._boundOnClick),this.$.dropdown.addEventListener("vaadin-overlay-touch-action",this._boundOnOverlayTouchAction),this.addEventListener("touchend",this._boundOnTouchend);const e=()=>{requestAnimationFrame((()=>{this.$.dropdown.$.overlay.bringToFront()}))};var t;this.addEventListener("mousedown",e),this.addEventListener("touchstart",e),t=this,window.Vaadin&&window.Vaadin.templateRendererCallback?window.Vaadin.templateRendererCallback(t):t.querySelector("template")&&console.warn(`WARNING: