diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 80e5098f..58a1825d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -2,26 +2,72 @@ name: Publish on: release: - types: - - published + types: [published] + push: + branches: [test-build] + workflow_dispatch: + inputs: + channel: + description: Card release channel to test (does not publish a release) + type: choice + options: [dev, stable] + default: dev + +permissions: + contents: read jobs: - release_zip_file: - name: Publish Wiser zip file asset - runs-on: ubuntu-latest + package: + name: Build Wiser integration + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-python@v6 + with: + python-version: '3.13' + - name: Install test dependencies + run: python -m pip install -r tests/requirements.txt + - name: Test packaging and sidebar integration + run: | + python -m unittest discover -s tests -p test_card_releases.py + python -m unittest discover -s tests -p test_build.py + python -m unittest discover -s tests -p test_card_updates.py + python -m unittest discover -s tests -p test_ui_options.py + python -m unittest discover -s tests -p test_sidebar_icons.py + python -m unittest discover -s tests -p 'test_*sidebar.py' + python -m unittest discover -s tests -p 'test_*version.py' + - name: Build integration with published card releases + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARD_CHANNEL: ${{ github.event_name == 'release' && (github.event.release.prerelease && 'dev' || 'stable') || inputs.channel || 'dev' }} + PANEL_CONFIG_REPOSITORY: ${{ vars.WISER_PANEL_CONFIG_REPOSITORY || 'andyblac/WiserFrontendPanelConfig' }} + run: >- + python scripts/build.py --release + --channel "$CARD_CHANNEL" + --panel-config-repository "$PANEL_CONFIG_REPOSITORY" + - name: Upload release package for inspection + uses: actions/upload-artifact@v7 + with: + name: wiser-package + path: | + dist/wiser.zip + dist/card-releases.json + dist/panel-config-release.json + if-no-files-found: error + + publish: + name: Attach package to published release + if: github.event_name == 'release' + needs: package + runs-on: ubuntu-24.04 permissions: contents: write steps: - - name: 📥 Checkout the repository - uses: actions/checkout@v3.0.2 - # Pack the Wiser dir as a zip and upload to the release - - name: 📦 ZIP Wiser Dir - if: ${{ github.event_name == 'release' }} - run: | - cd ${{ github.workspace }}/custom_components/wiser - zip wiser.zip -r ./ - - name: 📤 Upload zip to release - uses: softprops/action-gh-release@v0.1.14 - if: ${{ github.event_name == 'release' }} + - uses: actions/download-artifact@v8 + with: + name: wiser-package + path: dist + - uses: softprops/action-gh-release@v3 with: - files: ${{ github.workspace }}/custom_components/wiser/wiser.zip + files: dist/wiser.zip + fail_on_unmatched_files: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..8b4ffe7d --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,263 @@ +# Changelog + +All notable changes to the Wiser Home Assistant integration, newest release first. + +For releases before v3.3.5, see the [Full Change Log](https://github.com/asantaga/wiserHomeAssistantPlatform/wiki/Full-Change-Log) on our wiki. + +## v4.0.0 + +### Breaking changes + +- The device registry is restructured. The separate virtual controller device is merged into the physical hub device, and Wiser room devices move to stable identifiers. In both cases the old device is removed and its entities are reassigned to the surviving device. Entity IDs and history are preserved, but anything that targets a Wiser *device* rather than an entity - device-based automations and scripts, or dashboard cards pointing at a device - needs repointing - PR [#693](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/693), issue [#733](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/733), PR [#734](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/734). +- Entity unique IDs migrate to deterministic UUIDv5. The migration runs automatically on upgrade and renames registry entries in place, including light capability entities, so entity IDs, history, and customisations are preserved. Every target is checked before any change is applied, so it cannot partially apply, but it aborts if it finds a unique ID collision - take a backup before upgrading - PR [#693](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/693), PR [#723](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/723). +- Downgrading is not supported. Because unique IDs and device identifiers change, returning to a 3.x release means restoring the backup taken before upgrading, not just reinstalling the older version. +- Equipment power and energy sensors are now optional and off by default, including for existing installations. To keep them, enable **Equipment sensors** in the integration options after upgrading - PR [#726](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/726). +- Room climate entities only report the `demand_type` attribute when OpenTherm is enabled. On systems without OpenTherm it is no longer present, so update any templates or automations that use it. `percentage_demand` is still reported on all systems - PR [#724](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/724). +- The Heating, Heating channel and Hot Water circuit state sensors now report the standard Home Assistant states `on` and `off` instead of `On` and `Off`, and show as unknown when the hub reports neither. The displayed state is unchanged, but templates and automations that compare against `On` or `Off` must use the lowercase values - issue [#711](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/711), PR [#721](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/721). + +### Added + +- Added Schneider Mureva EV Link charger entities - issue [#689](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/689), PR [#749](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/749). +- Added smoke alarm operating states and a report count sensor - PR [#744](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/744). +- Added scheduled setpoint sensors, showing the current schedule temperature for Wiser rooms without climate control. Rooms are chosen under **Additional sensors** in the automation options - issue [#738](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/738), PR [#746](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/746). +- Added optional Wiser-only OpenTherm domestic-hot-water control with a Home Assistant water-heater entity, configurable temperature and presets, an optional external current-temperature sensor, icon mode controls, and an integration-owned weekly schedule managed from the Wiser Schedules panel. Non-timed presets can be used in schedule periods; temporary boost presets remain manual actions - PR [#745](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/745). +- Added configurable OpenTherm monitoring and controls, including relative modulation, a Delta-T sensor, daily flame runtime statistics counted from local midnight, boiler capacity and estimated boiler output sensors, expanded telemetry, and categorized diagnostics. OpenTherm options and entities only appear on systems with an OpenTherm boiler - PR [#697](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/697), PR [#701](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/701), PR [#715](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/715), issue [#735](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/735), PR [#729](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/729). +- Added an `is_calling_for_heat` attribute to room climate entities on OpenTherm systems - PR [#724](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/724). +- Added PowerTag Control equipment sensors - PR [#726](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/726). +- Added a single **Wiser** sidebar panel with a custom sidebar icon and tabs for Schedules, Zigbee, Controls and Hub. It is shown by default and can be hidden with **Show Wiser in sidebar** in the integration's UI options - PR [#708](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/708), PR [#719](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/719). +- Added the **Wiser Controls card**, a dashboard card for heating, stored and OpenTherm hot water, shutters, lights, dimmers and smart plugs, with room controls, schedule information and bulk actions. It is also available as the Controls tab of the Wiser panel - PR [#719](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/719). +- Added the **Wiser Hub panel**, a Wiser panel tab giving an overview, live controls, diagnostics and recent history charts for each hub - PR [#719](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/719). +- Added an integration-managed frontend registry (`cards.json`), so the Schedule, Zigbee and Controls cards and the Wiser panel tabs install and update independently through Home Assistant update entities, with offline caching and a shared panel-settings API - PR [#709](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/709), PR [#719](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/719). +- Added a Hot Water on/off switch entity, attached to the hub device - issue [#626](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/626), PR [#682](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/682), PR [#704](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/704). +- Added a UFH controller measured-temperature sensor - issue [#628](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/628). +- Added optimistic light state for instant UI feedback on toggles, kept stable across rapid repeated toggles - PR [#684](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/684). +- Added British English translations and corrected the German, French, and English integration translations - PR [#708](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/708). +- Added Danish, Spanish, Finnish, Hungarian, Norwegian, Dutch, Portuguese, and Swedish translations - PR [#747](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/747). + +### Changed + +- Entity naming now follows Home Assistant conventions, using `has_entity_name` so Home Assistant composes each displayed name from the device name plus the entity name. Existing entity IDs are not renamed. A new **Legacy naming** option controls whether Wiser room names are included in device names; it is switched on automatically for existing installations, so your device names stay as they are unless you turn it off. New installations on Home Assistant 2026.8 and later default it to off. Light and dual-relay channels are named from the physical device, and single-channel lights do not repeat a channel name - PR [#693](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/693), PR [#700](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/700), issue [#666](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/666). +- Wiser devices are now placed in Home Assistant areas matching their Wiser room, creating the area if it does not exist. This applies only to devices that have no area yet, so any area you assigned manually is left untouched - PR [#693](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/693), PR [#700](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/700). +- Card and panel bundles are no longer stored in the source repository. Release packages include them, and each card or panel updates from its own repository through a Home Assistant update entity - PR [#716](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/716). +- Card versions are now detected from the installed card bundles instead of being pinned in the integration, so card updates no longer need an integration release - PR [#708](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/708). +- OpenTherm hot-water temperature changes made in Auto mode now act as temporary overrides and are replaced by the next scheduled period - PR [#745](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/745). + +### Fixed + +- Fixed Wiser room renames and removals creating duplicate or orphaned devices and entities. Physical devices and logical rooms now use stable identifiers, existing registry records are migrated while retaining entity IDs and history, integration-managed area assignments follow room renames, user-selected areas remain unchanged, and deleted rooms are removed after confirmation - issue [#732](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/732). +- Fixed passive mode for underfloor heating rooms - issue [#641](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/641), PR [#742](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/742). +- Improved light and smart plug responsiveness: commands are no longer blocked by a hub refresh, stale confirmations are retried, obsolete ones are cancelled, and smart plug state updates immediately after a command - issue [#666](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/666), PR [#725](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/725). +- Fixed Hub V1 installations creating unsupported Hub V2 system sensors - PR [#731](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/731). +- Fixed system binary sensors not updating - issue [#736](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/736), PR [#737](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/737). +- Reintroduced the previous Power and Energy entities, corrected equipment data naming on v2 hubs, and added an active state for the window/door sensor - issue [#677](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/677), PR [#690](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/690). +- Fixed multi-gang dimmer lights breaking select, binary_sensor, switch, and sensor entities - issue [#681](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/681), PR [#683](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/683). +- Preserved light entities across the multi-gang unique_id migration - issue [#681](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/681), PR [#683](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/683). +- Fixed the device signal sensor to emit once per physical device instead of once per light channel on multi-gang dimmers - issue [#681](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/681), PR [#683](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/683). +- Fixed the room **Window Detection Active** binary sensor being presented as a physical window sensor. It carried a `window` device class, so Home Assistant displayed it as Open/Closed as though it reported a real window, when it actually reports whether the room's window-detection feature is switched on. It now shows a plain on/off state. The underlying state values are unchanged, so automations that test for `on`/`off` keep working - only the displayed wording and icon change - PR [#703](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/703). +- Fixed away mode and frost protection reporting distinct target temperatures, and the frost protection target now honours the configured value - PR [#693](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/693). +- Fixed room stat and iTRV battery values not always being reported correctly - issue [#662](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/662), PR [#680](https://github.com/asantaga/wiserHomeAssistantPlatform/pull/680). + +## v3.4.20 + +- Added equipment data to smart plugs, heating actuators, and PowerTag E devices. +- Added LED-indicator support for on/off and dimmable lights. +- Added seasonal-comfort controls, including the target lift for shutters. +- Added an option to hide the hot-water schedule in the Wiser Schedule Card. +- Fixed the controller signal sensor to use the HeatHub name - issue [#639](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/639). +- Fixed schedule deletion and schedule-name inputs on current Home Assistant releases - issue [#461](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/461). + +## v3.4.19 + +- Fixed: Breaking change in HA 2026.2 causes integration not to load - issue [#643](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/643) +- Fixed: V2 hubs with firmware lower than 4.42.23 cannot connect on https + +## v3.4.18 + +- Fixed no long term stats for temp/humidity sensors issue [#598](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/598) +- Fixed deprecated ZeroconfServiceInfo - issue [#612](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/612) +- Fixed error 500 when selecting integration config - issue [#613](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/613) +- Fixed unable to create new schedule card - issue [#616](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/616) +- Fixed schedule card badges are misaligned - issue [#615](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/615) +- Changed: Communication to v2 hub now uses https over internal network +- Bumped schedule card to v1.5.0 +- Bumped api to v1.7.0 + +## v3.4.17 + +- Fixed hot water climate does not respect away mode - issue [#579](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/579) +- Fixed deprecation error sets option flow config_entry explicitly - issue [#595](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/595) +- Fixed schedule card unable to edit - issue [#607](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/607) + +## v3.4.16 + +- Fix incorrect identifer on hub device +- Fix deprecation warning for access to lovelace resource parameters +- Fix error on unloading services for hubs without hot water control +- Fix deprecation warning for ZeroconfServiceInfo +- Bump api to v1.6.6 to support SSL for future firmware + +## v3.4.15 + +- Added experimental hw climate mode to operate differently. See wiki for details +- Changed min/max hw climate temp range from 40-80C to 10-80C - issue [#545](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/545) +- Fixed issue whereby hw climate errors if temp sensor not available after HA start - issue [#541](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/541) +- Fixed issue whereby hw climate config requires some entry in the temp sensor select option, even if not enabled - issue [#544](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/544) +- Fixed issue whereby hw min does not change when setting both via service call if temps are 1C or less different - issue [#547](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/547) + +## v3.4.14 + +- Fixed issue causing integration not to load in some circumstances due to failed config entry migration - issue #539 +- Added binary sensor active state sensor + +## v3.4.13 + +- Added support for PowerTag C - issue #528 +- BREAKING CHANGE - refactored HW climate automation - issues #481, #490. See wiki for updated instructions +- Added illuminance, humidity and temp sensors to devices with threshold sensors - issue #531 +- Added support for 2 gang light switch - issue #529 +- Added interacts with room climate switch to supported devices +- Fixed support for Binary sensors with threshold sensors - issue #530 +- Fixed incompatibility with Python3.13 and HA2024.12 - issue #535 +- Fixed events not correctly firing for climate changes - issue #526 +- Fixed error when saving schedule with an off slot - issue #536 +- Changed all hot water related sensors to now belong to a hot water device +- Bumped aiowiserheatapi to v1.6.3 + +## v3.4.12 + +- Fixed issue assigning schedules with non ascii characters in name - issue #509 +- Fixed error when using HotWater climate automation - issue #517 +- Fixed wiser http path not registering - issue #521 +- Fixed issue causing integration to fail loading with BoilerInterface - issue #523 +- Added support for ButtonPanel (Wiser Odace) - issue #524 +- Bump api to v1.5.19 to resolve issues #509, #523, #524 + +## v3.4.11 + +- Bump api to v1.5.18 to reduce Payload not completed errors +- Fix typo in dimable light color mode - issue #518 + +## v3.4.10 + +- Fixed error loading sensors - issue #513 + +## v3.4.9 (Pulled) + +- Fixed smoke alarm naming issue - issue #496 +- Set humidity to Unavailable if no value - issue #503 +- Add support for BoilerInterface - issue #499 +- Add support for WindowDoorSensor for v2 hub +- Add support for CFMT device for v2 hub - issue #507 + +## v3.4.8 + +- Fix deprecation warning no waiting on setups - [#485](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/485) +- Fix color mode issue - [#479](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/479) +- Added smoke alarm sensors - [#457](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/457) +- Fixed missing save layout button in zigbee card - [#488](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/488) + +## v3.4.7 + +- Bump api to v1.5.14 to improve handling of hub connection errors +- Fix - improve handling of hub update failures - [#434](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/434) +- Fix - set entity values to unknown if not provided in the hub update - [#471](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/471) +- Fix - removed use of async_add_job - [#463](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/463) +- Fix - add color modes to lights - [#458](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/458) +- Fix - use default boost temp with presets - [#467](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/467) +- Add ability to unassign a schedule via the assign schedule service - [#470](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/470) + +## v3.4.6 + +- Bump api to v1.5.13 to improve retry handling to include hub conneciton error +- Prevent entities going unavailable if hub update failed +- Fix issue with floor temp offset slider not loading +- Fix hass.components.websocket_api deprecation warning in HA 2024.3 (issue [#455](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/455)) +- Fix unique ids not unique error when changing configuration options +- Add new automation to control hot water with a climate entity and an external temperature sensor on your water tank by @markchalloner. See wiki for more info. + +## v3.4.5 + +- Bump api to v1.5.12 to improve performance of improved retry handling +- Fixed issue caused by v3.4.4 that heating actuators and power tags error on load (issue [#449](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/449), [#450](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/450)) +- Fixed error setting up integration in config flow caused by session parameter being passed when no longer required (issue [#446](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/446)) +- Fixed issue on 3 channel hubs with Heating sensor names +- Fixed issue with signal sensor showing unknown on startup until first refresh +- Changed preset icon to HA standard + +## v3.4.4 + +- Bump api to v1.5.11 +- Improved api retry handling for inconsitant errors coming from the hub causing errors in the log and entities to go unavailable (issues [#434](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/434), #436, #439) +- Fixed Validation of translation placeholders error for German language (issue [#434](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/434) +- Fixed diagnostic download failure (issue [#444](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/444)) +- Fixed error with wall plugs not providing power data (issue [#446](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/446)) +- Set state of target temp sensors to Unavailable when the climate HVAC mode is off (issue [#447](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/447), #378) +- Added ability to set a different IP port for hub - PR#430 - thanks @simick +- Enabled statistics on batteries - PR#445 - thanks @msalway +- Added number_of_trvs, number_of_trvs_locked and is_roomstat_locked attributes to climate entities (issue [#374](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/374)) + +## v3.4.3 + +- Fixed Warning error in logs caused by new HA2024.2 requirement to explicity support Turn On/Off for climate entities (issue [#435](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/435)) +- Bump api to v1.5.7 to fix issue setting lower target temp when in passive mode + +## v3.4.2 + +- Reverted to using aiohttp for communication and resolved issues caused by HA2023.12 +- Bumped api to v1.5.5 +- Fixed issue where hub communication would error due to command characters in payload (issue [#418](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/418)) +- Updated schedule card to allow hiding of hot water schedule (issue [#415](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/415)) +- Included version in card resources to improve updating of new versions +- Added more v2 hub features and attributes +- Improved error handling/logging when hub offline and command is issued + +## v3.4.1 + +- Corrected error deleting schedule +- Handle space at end of secret key and prevent error (issue [#409](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/409)) +- Updated schedule card to v1.3.2 - fixed some UI bugs, added ability to hide info and assignments via config (issue [#404](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/404)) + +## v3.4.0beta + +- Fixed issue in HA 2023.12 with errors reading hub +- Add PowerTagE support (v2 hub) +- Add tilt functions for shutters (v2 hub) +- Fixed issue whereby non ASCII chars are removed in device/room names - issues #396 + +## v3.3.11 + +- Add check for overrides to prevent turning off away modewhen selecting cancel overrides when none exist (Wiser hub bug) +- Correct sensor device class and native values to fix history not displaying issue in HA 2023.11 + +## v3.3.10 + +- bump api to v1.3.8 to fix passive mode error if room trv/roomstat goes offline +- make battery sensor unavailable if no battery info provided by hub (previously showed 0%) +- add uptime and last reset reason to Hub signal sensor +- make LTS sensors normal sensors instead of diagnostic sensors so they show in Area card - issue #381 +- save schedule service will now create directory for file if it doesn't exist +- correct relative modulation level magnitude + +## v3.3.9 + +- bump api to v1.3.5 to fix warning regarding async not awaited on extra config + +## v3.3.8 + +- Fix extra key issue preventing loading on HA 2023.8.0 and above + +## v3.3.7 + +- Fix issue with zigbee card unable to save layout in stack card +- Updated libraries for schedule card to fix security vulnerabilities +- Fix colours missing on on/off schedule in schedule card +- Minor UI improvements in schedule card + +## v3.3.6 + +- Improved Zigbee network card + +## v3.3.5 + +- Fix for eroneous current temp if lost signal with TRV - issue #369 +- Reduced log error level for failed update form hub to warning diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4a7b5f8d --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,166 @@ +# Contributing + +Thanks for helping out with the Wiser Home Assistant integration. Issues, pull +requests, and bug fixes are all welcome and greatly appreciated. + +## Before you start + +- **Search the [issues](https://github.com/asantaga/wiserHomeAssistantPlatform/issues) + first.** If your problem is already reported, add your details there rather + than opening a duplicate. +- For anything larger than a bug fix, open an issue to discuss the approach + before writing code. It saves everyone a wasted PR. +- Check the [Wiki](https://github.com/asantaga/wiserHomeAssistantPlatform/wiki) + for setup and usage documentation. + +## Reporting bugs + +Please include: + +- The integration version (Settings → Devices & Services → Wiser), and your + Home Assistant version. +- Your hub model (v1 or v2) and firmware version. +- Depending on the bug we may ask for a **diagnostics download** from the integration's device page — it redacts + secrets and captures the hub data we need. +- Relevant log output. Add this to `configuration.yaml`, restart, and reproduce: + + ```yaml + logger: + default: warning + logs: + custom_components.wiser: debug + aioWiserHeatAPI: debug + ``` + +## Where things live + +| What | Where | +| --- | --- | +| Integration source | `custom_components/wiser/` | +| Hub communication | the separate `aioWiserHeatAPI` library, pinned in `manifest.json` | +| Frontend panel/card plumbing | `custom_components/wiser/frontend/` | +| Frontend cards and panels | repositories declared in `custom_components/wiser/frontend/cards.json` | +| Tests | `tests/` | +| Packaging scripts | `scripts/` | + +Two things that trip people up: + +- If a change is really about how we talk to the hub, it probably belongs in + [aioWiserHeatAPI](https://github.com/msp1974/aioWiserHeatAPI) git repo, not here. +- **Card bundles are not committed to this repository.** They are downloaded + from the card repositories' GitHub releases at build time. Do not add built + `.js` card assets to `custom_components/wiser/frontend/`. +- Follow [Adding Wiser frontend cards and panels](docs/frontend-registry.md) to + register a new frontend repository and test its independent update entity. + +## Branching and pull requests + +- **Base every PR on `dev`.** All development happens there; `master` only + receives `dev` when we cut a release. +- Name your branch after the issue it fixes, e.g. `issue123`. +- Keep a PR to one logical change. Unrelated fixes are much easier to review as + separate PRs. +- Reference the issue in the PR description (`Fixes #123`). +- Add or update tests for behaviour you change, and add a `CHANGELOG.md` entry + under the unreleased/current heading, matching the existing style (one line, + ending with a link to the issue and/or PR). + +Releases are tagged from `master` as `vX.Y.Z` (e.g. `v3.4.20`). Release +candidates are cut from `dev` and tagged `vX.Y.Z-rcN` (e.g. `v4.0.0-rc1`). + +## Development setup + +Run the integration against a real Home Assistant instance. The quickest route +is a development instance (container or venv) with the integration mounted or +symlinked into place: + +```sh +ln -s /path/to/wiserHomeAssistantPlatform/custom_components/wiser \ + /path/to/ha-config/custom_components/wiser +``` + +Restart Home Assistant after each change — Home Assistant does not hot-reload +custom components. + +The minimum supported Home Assistant version is declared in `hacs.json` +(currently 2025.5); don't use APIs newer than that without raising the minimum +deliberately. + + +## Code style + +There is no enforced formatter config in this repository, so match the +surrounding code, which follows Home Assistant core conventions: + +- `from __future__ import annotations` at the top, type hints on new functions. +- Docstrings on every module, class, and function. +- Import grouping and ordering as in the existing modules. +- New user-visible strings go in `strings.json` and the files under + `translations/`; new icons go in `icons.json`. + +## Versioning + +The version is set in two places, and both must be changed together: + +- `version` in `custom_components/wiser/manifest.json`, which HACS and Home + Assistant read. +- `VERSION` in `custom_components/wiser/const.py`, which the integration reports + itself, for example in the hub sensor's `integration_version` attribute. + +The version must stay parseable by HACS and Home Assistant. Use `X.Y.Z` for +releases, `X.Y.Z-rcN` for release candidates, and `X.Y.ZbN` for beta builds on +`dev`. Do not invent suffixes such as `Dev` — HACS and Home Assistant reject +them, and the integration will fail to load with `does not have a valid version +key`. + +## Building a package + +```sh +# Stable release package +python3 scripts/build.py --release --channel stable + +# Prerelease package +python3 scripts/build.py --release --channel dev +``` + +The build downloads every card and panel declared in +`custom_components/wiser/frontend/cards.json` from its own GitHub repository. +Stable builds use stable frontend releases; prerelease builds use the most +recently published stable or prerelease release. In a running installation, +each frontend update entity follows the HACS **Pre-release** switch for this +integration; without that option it offers stable releases only. See +[Adding frontend cards and panels](docs/frontend-registry.md) for the registry +format and shared panel API. + +The output is `dist/wiser.zip`, with `dist/card-releases.json` recording the +frontend artifacts and checksums included. If an individual card or panel +repository is temporarily unavailable, its bundle is omitted and the +integration still builds; its Home Assistant update entity can install it +later. Invalid local development bundles and an invalid bundled registry still +fail the build. + +For an integration-only test package that retains the registry but +deliberately omits every card and panel bundle: + +```sh +python3 scripts/build.py --channel dev --without-frontend +``` + +To see which card releases would be selected without downloading card bundles +(the registry metadata is still fetched): + +```sh +python3 scripts/fetch_card_releases.py --channel dev --plan +``` + +The **Publish** GitHub Actions workflow runs the same build and attaches +`wiser.zip` when an integration release is published. Preview runs provide a +downloadable **wiser-package** artifact without publishing a release. + +## Licence + +By contributing, you agree that your contributions are licensed under the +project's MIT licence (see [LICENSE](LICENSE)). + +Thanks again, +Wiser Home Assistant Integration Team diff --git a/README.md b/README.md index 8bafdf60..bf72d2d4 100755 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Wiser Home Assistant Integration v3.4.21b0 +# Wiser Home Assistant Integration v4.0.0 [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/hacs/integration) [![downloads](https://shields.io/github/downloads/asantaga/wiserHomeAssistantPlatform/latest/total?style=for-the-badge)](https://github.com/asantaga/wiserHomeAssistantPlatform) @@ -18,11 +18,35 @@ Detailed information about this integration has now been moved to our [Wiki page For more information checkout the AMAZING community thread available on [https://community.home-assistant.io/t/drayton-wiser-home-assistant-integration/80965](https://community.home-assistant.io/t/drayton-wiser-home-assistant-integration/80965) -## What's New in 3.4? +## What's New in 4.0? -- Added support for v2 hub -- Added support for many new v2 hub devices -- Climate entity for controlling hot water with external tank temp sensor +This is a **major** and **big** release with a lot of new features, bug fixes and changes. + +- Entity and device naming now follows Home Assistant conventions, with devices associated with areas. Existing installations keep their current names automatically through the **Legacy naming** option - turn it off to adopt the new names. New installations on Home Assistant 2026.8 and later use the new names by default +- Wiser devices are assigned to Home Assistant areas matching your Wiser rooms, and room renames in the Wiser app no longer create duplicate devices +- Configurable OpenTherm monitoring and controls, plus optional OpenTherm hot water control with its own schedule. The hot water schedule is run by Home Assistant rather than the hub, so it does not change the boiler while Home Assistant is offline +- New device support, including the Schneider Mureva EV Link charger, smoke alarm operating states and scheduled setpoint sensors for rooms without heating control +- Brand new **UI**: a single **Wiser** sidebar panel with tabs for Schedules, Zigbee, Controls and Hub. It is shown by default and can be hidden with **Show Wiser in sidebar** in the integration's UI options +- New **Wiser Controls card** for dashboards, covering heating, hot water, shutters, lights and smart plugs, alongside the updated Wiser Schedule and Wiser Zigbee cards +- New **Wiser Hub panel** with an overview, live controls, diagnostics and recent history for each hub +- Cards and panels now update independently of the integration, through Home Assistant update entities +- Translations for Danish, Spanish, Finnish, Hungarian, Norwegian, Dutch, Portuguese and Swedish +- Many bug fixes see change log + +See the [change log](CHANGELOG.md) for the full list of changes in this release. + +Huge thanks to [@andyblac](https://github.com/andyblac) and [@lgo44](https://github.com/LGO44) for this release + +### Breaking changes in 4.0 + +**Please take a backup before upgrading.** + +- **The device registry is restructured.** The separate virtual controller device is merged into the physical hub device, and Wiser room devices move to stable identifiers. Entity IDs and history are preserved, but anything that targets a Wiser *device* rather than an entity - device-based automations and scripts, or dashboard cards pointing at a device - needs repointing. Check these after upgrading. +- **Entity unique IDs move to a new format.** This migration runs automatically and keeps your entity IDs and history. It is all-or-nothing: if it finds a conflict it stops without changing anything, and the integration will not load until it is resolved. +- **Downgrading is not supported.** To go back to 3.x, restore the backup you took before upgrading - reinstalling the older version on its own will not work. +- **Equipment power and energy sensors are now off by default**, including for existing installations. If you use them, for example in the Energy dashboard, enable **Equipment sensors** in the integration options after upgrading. +- **Room climate entities only report `demand_type` when OpenTherm is enabled.** On systems without OpenTherm this attribute is no longer present, so update any templates or automations that use it. `percentage_demand` is still reported on all systems. +- **The Heating and Hot Water circuit state sensors now report `on`/`off` instead of `On`/`Off`.** The dashboard still shows On and Off, but templates and automations that compare against `On` or `Off` need updating to the lowercase values. ## Installing @@ -30,190 +54,18 @@ For more information checkout the AMAZING community thread available on ## Change log -- v3.4.21b0 - - Added a Hot Water on/off switch entity - issue [#626](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/626). - - Added a UFH controller measured-temperature sensor - issue [#628](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/628). - - Fixed multi-gang dimmer lights breaking select, binary_sensor, switch, and sensor entities - issue [#681](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/681). - - Preserved light entities across the multi-gang unique_id migration - issue [#681](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/681). - - Added optimistic light state for instant UI feedback on toggles, kept stable across rapid repeated toggles. - - Fixed the device signal sensor to emit once per physical device instead of once per light channel on multi-gang dimmers. - - Fix for issue [#662](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/662). - -- v3.4.20 - - Added equipment data to smart plugs, heating actuators, and PowerTag E devices. - - Added LED-indicator support for on/off and dimmable lights. - - Added seasonal-comfort controls, including the target lift for shutters. - - Added an option to hide the hot-water schedule in the Wiser Schedule Card. - - Fixed the controller signal sensor to use the HeatHub name - issue [#639](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/639). - - Fixed schedule deletion and schedule-name inputs on current Home Assistant releases - issue [#461](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/461). - -- v3.4.19 - - Fixed: Breaking change in HA 2026.2 causes integration not to load - issue [#643](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/643) - - Fixed: V2 hubs with firmware lower than 4.42.23 cannot connect on https - -- v3.4.18 - - Fixed no long term stats for temp/humidity sensors issue [#598](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/598) - - Fixed deprecated ZeroconfServiceInfo - issue [#612](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/612) - - Fixed error 500 when selecting integration config - issue [#613](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/613) - - Fixed unable to create new schedule card - issue [#616](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/616) - - Fixed schedule card badges are misaligned - issue [#615](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/615) - - Changed: Communication to v2 hub now uses https over internal network - - Bumped schedule card to v1.5.0 - - Bumped api to v1.7.0 - -- v3.4.17 - - Fixed hot water climate does not respect away mode - issue [#579](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/579) - - Fixed deprecation error sets option flow config_entry explicitly - issue [#595](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/595) - - Fixed schedule card unable to edit - issue [#607](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/607) - -- v3.4.16 - - Fix incorrect identifer on hub device - - Fix deprecation warning for access to lovelace resource parameters - - Fix error on unloading services for hubs without hot water control - - Fix deprecation warning for ZeroconfServiceInfo - - Bump api to v1.6.6 to support SSL for future firmware - -- v3.4.15 - - Added experimental hw climate mode to operate differently. See wiki for details - - Changed min/max hw climate temp range from 40-80C to 10-80C - issue [#545](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/545) - - Fixed issue whereby hw climate errors if temp sensor not available after HA start - issue [#541](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/541) - - Fixed issue whereby hw climate config requires some entry in the temp sensor select option, even if not enabled - issue [#544](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/544) - - Fixed issue whereby hw min does not change when setting both via service call if temps are 1C or less different - issue [#547](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/547) - -- v3.4.14 - - Fixed issue causing integration not to load in some circumstances due to failed config entry migration - issue #539 - - Added binary sensor active state sensor - -- v3.4.13 - - Added support for PowerTag C - issue #528 - - BREAKING CHANGE - refactored HW climate automation - issues #481, #490. See wiki for updated instructions - - Added illuminance, humidity and temp sensors to devices with threshold sensors - issue #531 - - Added support for 2 gang light switch - issue #529 - - Added interacts with room climate switch to supported devices - - Fixed support for Binary sensors with threshold sensors - issue #530 - - Fixed incompatibility with Python3.13 and HA2024.12 - issue #535 - - Fixed events not correctly firing for climate changes - issue #526 - - Fixed error when saving schedule with an off slot - issue #536 - - Changed all hot water related sensors to now belong to a hot water device - - Bumped aiowiserheatapi to v1.6.3 - -- v3.4.12 - - Fixed issue assigning schedules with non ascii characters in name - issue #509 - - Fixed error when using HotWater climate automation - issue #517 - - Fixed wiser http path not registering - issue #521 - - Fixed issue causing integration to fail loading with BoilerInterface - issue #523 - - Added support for ButtonPanel (Wiser Odace) - issue #524 - - Bump api to v1.5.19 to resolve issues #509, #523, #524 - -- v3.4.11 - - Bump api to v1.5.18 to reduce Payload not completed errors - - Fix typo in dimable light color mode - issue #518 - -- v3.4.10 - - Fixed error loading sensors - issue #513 - -- v3.4.9 (Pulled) - - Fixed smoke alarm naming issue - issue #496 - - Set humidity to Unavailable if no value - issue #503 - - Add support for BoilerInterface - issue #499 - - Add support for WindowDoorSensor for v2 hub - - Add support for CFMT device for v2 hub - issue #507 - -- v3.4.8 - - Fix deprecation warning no waiting on setups - [#485](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/485) - - Fix color mode issue - [#479](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/479) - - Added smoke alarm sensors - [#457](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/457) - - Fixed missing save layout button in zigbee card - [#488](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/488) - -- v3.4.7 - - Bump api to v1.5.14 to improve handling of hub connection errors - - Fix - improve handling of hub update failures - [#434](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/434) - - Fix - set entity values to unknown if not provided in the hub update - [#471](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/471) - - Fix - removed use of async_add_job - [#463](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/463) - - Fix - add color modes to lights - [#458](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/458) - - Fix - use default boost temp with presets - [#467](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/467) - - Add ability to unassign a schedule via the assign schedule service - [#470](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/470) - -- v3.4.6 - - Bump api to v1.5.13 to improve retry handling to include hub conneciton error - - Prevent entities going unavailable if hub update failed - - Fix issue with floor temp offset slider not loading - - Fix hass.components.websocket_api deprecation warning in HA 2024.3 (issue [#455](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/455)) - - Fix unique ids not unique error when changing configuration options - - Add new automation to control hot water with a climate entity and an external temperature sensor on your water tank by @markchalloner. See wiki for more info. - -- v3.4.5 - - Bump api to v1.5.12 to improve performance of improved retry handling - - Fixed issue caused by v3.4.4 that heating actuators and power tags error on load (issue [#449](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/449), [#450](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/450)) - - Fixed error setting up integration in config flow caused by session parameter being passed when no longer required (issue [#446](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/446)) - - Fixed issue on 3 channel hubs with Heating sensor names - - Fixed issue with signal sensor showing unknown on startup until first refresh - - Changed preset icon to HA standard - -- v3.4.4 - - Bump api to v1.5.11 - - Improved api retry handling for inconsitant errors coming from the hub causing errors in the log and entities to go unavailable (issues [#434](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/434), #436, #439) - - Fixed Validation of translation placeholders error for German language (issue [#434](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/434) - - Fixed diagnostic download failure (issue [#444](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/444)) - - Fixed error with wall plugs not providing power data (issue [#446](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/446)) - - Set state of target temp sensors to Unavailable when the climate HVAC mode is off (issue [#447](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/447), #378) - - Added ability to set a different IP port for hub - PR#430 - thanks @simick - - Enabled statistics on batteries - PR#445 - thanks @msalway - - Added number_of_trvs, number_of_trvs_locked and is_roomstat_locked attributes to climate entities (issue [#374](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/374)) - -- v3.4.3 - - Fixed Warning error in logs caused by new HA2024.2 requirement to explicity support Turn On/Off for climate entities (issue [#435](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/435)) - - Bump api to v1.5.7 to fix issue setting lower target temp when in passive mode - -- v3.4.2 - - Reverted to using aiohttp for communication and resolved issues caused by HA2023.12 - - Bumped api to v1.5.5 - - Fixed issue where hub communication would error due to command characters in payload (issue [#418](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/418)) - - Updated schedule card to allow hiding of hot water schedule (issue [#415](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/415)) - - Included version in card resources to improve updating of new versions - - Added more v2 hub features and attributes - - Improved error handling/logging when hub offline and command is issued - -- v3.4.1 - - Corrected error deleting schedule - - Handle space at end of secret key and prevent error (issue [#409](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/409)) - - Updated schedule card to v1.3.2 - fixed some UI bugs, added ability to hide info and assignments via config (issue [#404](https://github.com/asantaga/wiserHomeAssistantPlatform/issues/404)) - -- v3.4.0beta - - Fixed issue in HA 2023.12 with errors reading hub - - Add PowerTagE support (v2 hub) - - Add tilt functions for shutters (v2 hub) - - Fixed issue whereby non ASCII chars are removed in device/room names - issues #396 - -- v3.3.11 - - Add check for overrides to prevent turning off away modewhen selecting cancel overrides when none exist (Wiser hub bug) - - Correct sensor device class and native values to fix history not displaying issue in HA 2023.11 - -- v3.3.10 - - bump api to v1.3.8 to fix passive mode error if room trv/roomstat goes offline - - make battery sensor unavailable if no battery info provided by hub (previously showed 0%) - - add uptime and last reset reason to Hub signal sensor - - make LTS sensors normal sensors instead of diagnostic sensors so they show in Area card - issue #381 - - save schedule service will now create directory for file if it doesn't exist - - correct relative modulation level magnitude - -- v3.3.9 - - bump api to v1.3.5 to fix warning regarding async not awaited on extra config - -- v3.3.8 - - Fix extra key issue preventing loading on HA 2023.8.0 and above - -- v3.3.7 - - Fix issue with zigbee card unable to save layout in stack card - - Updated libraries for schedule card to fix security vulnerabilities - - Fix colours missing on on/off schedule in schedule card - - Minor UI improvements in schedule card - -- v3.3.6 - - Improved Zigbee network card - -- v3.3.5 - - Fix for eroneous current temp if lost signal with TRV - issue #369 - - Reduced log error level for failed update form hub to warning - -A full change log can be seen on our wiki [here](https://github.com/asantaga/wiserHomeAssistantPlatform/wiki/Full-Change-Log) +See [CHANGELOG.md](CHANGELOG.md) for the full release history. + +Releases before v3.3.5 are listed on our wiki [here](https://github.com/asantaga/wiserHomeAssistantPlatform/wiki/Full-Change-Log). + +## Building + +See [Building a package](CONTRIBUTING.md#building-a-package) in the contributing guide. + +## Contributing + +We welcome and greatly appreciate all issues, pull requests, and bug fixes. Please base your PR on the `dev` branch — all development happens there. + +See [CONTRIBUTING.md](CONTRIBUTING.md) for how to report a bug, set up a development environment, run the tests, and get a pull request merged. + +Wiser Home Assistant Integration Team diff --git a/custom_components/wiser/__init__.py b/custom_components/wiser/__init__.py index 8faf9899..94c1c480 100755 --- a/custom_components/wiser/__init__.py +++ b/custom_components/wiser/__init__.py @@ -5,14 +5,21 @@ """ import asyncio +from functools import partial import logging +import re from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_NAME from homeassistant.core import HomeAssistant, callback from homeassistant.exceptions import ConfigEntryNotReady -from homeassistant.helpers import device_registry as dr, entity_registry as er +from homeassistant.helpers import ( + area_registry as ar, + device_registry as dr, + entity_registry as er, +) from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC +from homeassistant.helpers.storage import Store from .const import ( CONF_AUTOMATIONS_HW_AUTO_MODE, @@ -22,9 +29,19 @@ CONF_AUTOMATIONS_PASSIVE, CONF_AUTOMATIONS_PASSIVE_TEMP_INCREMENT, CONF_DEPRECATED_HW_TARGET_TEMP, + CONF_LEGACY_NAMING, + CONF_OPENTHERM_EVER_CONNECTED, + CONF_OPENTHERM_HOT_WATER_CAPABLE, + CONF_OPENTHERM_HOT_WATER_CONTROL, + CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP, + CONF_OPENTHERM_HOT_WATER_PRESETS, + CONF_WISER_ROOM_NAMES, DATA, + DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP, DOMAIN, + ENTITY_PREFIX, MANUFACTURER, + OPENTHERM_HOT_WATER_SCHEDULE, UPDATE_LISTENER, WISER_PLATFORMS, WISER_SERVICES, @@ -32,27 +49,69 @@ ) from .coordinator import WiserUpdateCoordinator from .device import ( + assign_device_area_if_unset, + confirmed_deleted_room_ids, + find_physical_device_candidates, + known_wiser_room_area_name, merge_legacy_hub_device, + migrate_entity_unique_id_duplicates, + migrate_physical_device, + migrate_room_entities, migrate_room_device, + move_devices_from_managed_area, register_hub_device, register_room_assigned_device, + remove_room_devices, + room_names_with_pending_deletions, ) from .entity_migration import migrate_entity_unique_ids from .frontend import JSModuleRegistration +from .frontend.entry_updates import async_handle_entry_update, integration_reload_settings +from .frontend.wiser_sidebar import async_update_wiser_panel from .helpers import ( + active_wiser_rooms, build_light_unique_id_migration, + build_physical_entity_unique_id_migration, get_device_name, get_hub_device_name, + get_hub_via_device_info, get_identifier, get_instance_count, + get_legacy_device_identifier, get_legacy_room_identifier, + get_physical_entity_unique_id, + get_room_entity_unique_id, + get_unique_id, +) +from .local_schedule import OpenThermHotWaterSchedule +from .opentherm_detection import ( + opentherm_entity_unique_ids, + opentherm_hot_water_control_is_supported, + opentherm_hot_water_limits, + opentherm_is_detected, ) from .services import async_setup_services +from .update import async_unload_card_updates from .websockets import async_register_websockets +ROOM_NAMES_STORAGE_VERSION = 1 +ROOM_NAMES_STORAGE_KEY = f"{DOMAIN}.{{}}.room_names" +ROOM_NAMES_STORE = "room_names_store" + _LOGGER = logging.getLogger(__name__) +def _initial_room_names(stored_room_names, config_entry_room_names): + """Return validated migration and runtime room-name state.""" + if isinstance(stored_room_names, dict): + room_names = dict(stored_room_names) + return room_names, room_names + if isinstance(config_entry_room_names, dict): + room_names = dict(config_entry_room_names) + return room_names, room_names + return {}, None + + async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: """Migrate old entry.""" _LOGGER.debug( @@ -112,8 +171,12 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> migrated_count, ) + if config_entry.minor_version < 5: + # Keep room context visible for installations upgrading to UI options. + new_options.setdefault(CONF_LEGACY_NAMING, True) + hass.config_entries.async_update_entry( - config_entry, options=new_options, minor_version=4, version=1 + config_entry, options=new_options, minor_version=5, version=1 ) _LOGGER.debug( @@ -125,18 +188,25 @@ async def async_migrate_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> return True -async def async_migrate_light_unique_ids(hass: HomeAssistant, config_entry, data) -> None: - """Preserve light entities across the multi-gang dimmer fix (#681/#683). +async def async_migrate_physical_entity_unique_ids( + hass: HomeAssistant, + config_entry, + data, + previous_room_names=None, +) -> None: + """Preserve physical entities while adopting stable unique IDs. - The fix keys every light-derived entity on the per-channel ``light_id`` - instead of the physical device ``id`` (see build_light_unique_id_migration), - which changes the unique_ids of the light, its mode/LED/power-on selects, - its away-mode switch and its four capability binary_sensors. Without this - the pre-fix entities orphan on update, losing their history and dashboard - references. This renames the matching registry entries to the new scheme so - their entity_ids survive the update untouched. + This includes the multi-gang light migration and physical entities whose + historical unique IDs included mutable device or room names. """ - mapping = build_light_unique_id_migration(data) + mapping = { + **build_physical_entity_unique_id_migration( + data, previous_room_names + ), + # Light channels need the more specific per-channel migration when a + # physical device exposes more than one light. + **build_light_unique_id_migration(data), + } if not mapping: return @@ -147,10 +217,25 @@ def _migrate(entry: er.RegistryEntry) -> dict | None: new_unique_id = mapping.get(entry.unique_id) if not new_unique_id: return None - # async_update_entity raises if the target unique_id already exists, so - # a partially-migrated setup can't be allowed to abort config entry - # setup: skip and keep the pre-fix entity as an orphan instead. - if ent_reg.async_get_entity_id(entry.domain, entry.platform, new_unique_id): + # The original migration could create the target entry while the + # capability binary sensors continued to register with their old + # unique_id. Keep both registry records so history is recoverable; + # the legacy-ID entry becomes unavailable after platform setup. + target_entity_id = ent_reg.async_get_entity_id( + entry.domain, entry.platform, new_unique_id + ) + if target_entity_id: + target_entry = ent_reg.async_get(target_entity_id) + if ( + target_entry + and target_entry.config_entry_id == config_entry.entry_id + ): + _LOGGER.warning( + "Wiser: retaining duplicate legacy entity %s; target is %s", + entry.entity_id, + target_entity_id, + ) + return None _LOGGER.warning( "Wiser: not migrating unique_id %s -> %s, target already exists", entry.unique_id, @@ -158,7 +243,7 @@ def _migrate(entry: er.RegistryEntry) -> dict | None: ) return None _LOGGER.info( - "Wiser: migrating light unique_id %s -> %s", + "Wiser: migrating entity unique_id %s -> %s", entry.unique_id, new_unique_id, ) @@ -178,23 +263,89 @@ async def async_setup_entry(hass: HomeAssistant, config_entry): if not coordinator.last_update_status == "Success": raise ConfigEntryNotReady - # Update listener for config option changes - update_listener = config_entry.add_update_listener(_async_update_listener) + room_names_store = Store( + hass, + ROOM_NAMES_STORAGE_VERSION, + ROOM_NAMES_STORAGE_KEY.format(config_entry.entry_id), + ) + stored_room_names = await room_names_store.async_load() + previous_room_names, runtime_room_names = _initial_room_names( + stored_room_names, + config_entry.data.get(CONF_WISER_ROOM_NAMES), + ) + + # This must run before both the update listener and reload-settings snapshot. + # A setup-time flag change is then included in the initial snapshot without + # scheduling a reload; a later change is observed and reloads exactly once. + # This prevents a standard boiler's dormant OpenTherm endpoint from creating + # entities while allowing a real installation to survive temporary outages. + _remember_opentherm_connection(hass, config_entry, coordinator) hass.data[DOMAIN][config_entry.entry_id] = { DATA: coordinator, - UPDATE_LISTENER: update_listener, + CONF_WISER_ROOM_NAMES: runtime_room_names, + ROOM_NAMES_STORE: room_names_store, } + opentherm = coordinator.wiserhub.system.opentherm + if ( + config_entry.options.get(CONF_OPENTHERM_HOT_WATER_CONTROL, False) + and opentherm_is_detected(config_entry, opentherm) + and opentherm_hot_water_control_is_supported(config_entry, opentherm) + ): + minimum, maximum = opentherm_hot_water_limits(opentherm) + local_hot_water_schedule = OpenThermHotWaterSchedule( + hass, + config_entry.entry_id, + minimum=float(minimum), + maximum=float(maximum), + default_temperature=float( + config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP, + DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP, + ) + ), + presets=config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_PRESETS, [] + ), + ) + await local_hot_water_schedule.async_load() + hass.data[DOMAIN][config_entry.entry_id][ + OPENTHERM_HOT_WATER_SCHEDULE + ] = local_hot_water_schedule + + # If OpenTherm connects for the first time after startup, saving the flag + # causes one integration reload so its entities are added. Future outages + # retain those entities and their registry/history records. + config_entry.async_on_unload( + coordinator.async_add_listener( + partial( + _remember_opentherm_connection, + hass, + config_entry, + coordinator, + ) + ) + ) + update_hub_device_names(hass) # Register the physical hub before its entities and connected devices. hub_device = await async_update_device_registry(hass, config_entry) coordinator.hub_device_id = hub_device.id - # Create physical devices with their Wiser room as Home Assistant's initial - # area. The registry keeps any area the user chooses later. - register_room_assigned_devices(hass, config_entry, hub_device.id) + # Physical device identifiers used to contain mutable room or device names. + # Migrate them before platform setup so renames cannot duplicate devices. + migrate_physical_device_registry( + hass, + config_entry, + hub_device.id, + previous_room_names, + ) + + # Create physical devices in their Wiser room's Home Assistant area. Later + # room renames move devices only while they remain in that managed area. + register_room_assigned_devices(hass, config_entry) # Move existing hub entities off the old virtual Controller record. merge_legacy_hub_device_registry(hass, config_entry) @@ -202,23 +353,72 @@ async def async_setup_entry(hass: HomeAssistant, config_entry): # Give logical room devices stable IDs and concise device names. migrate_room_device_registry(hass, config_entry) - # Remap light entity unique_ids from the pre-#683 scheme before the - # platforms create entities, so existing light entities are preserved - # instead of orphaned (see async_migrate_light_unique_ids). - await async_migrate_light_unique_ids(hass, config_entry, coordinator) + current_room_names = _current_wiser_room_names(coordinator) + confirmed_deleted_ids = _confirmed_deleted_room_ids( + hass, + config_entry, + previous_room_names, + current_room_names, + ) + remove_deleted_room_devices( + hass, + config_entry, + previous_room_names, + confirmed_deleted_ids, + ) + sync_wiser_room_areas(hass, config_entry, previous_room_names) + _store_wiser_room_names( + hass, + config_entry, + room_names_with_pending_deletions( + previous_room_names, + current_room_names, + confirmed_deleted_ids, + ), + ) - # Setup platforms - await hass.config_entries.async_forward_entry_setups(config_entry, WISER_PLATFORMS) + # Register listeners only after setup-time registry and config migrations. + update_listener = config_entry.add_update_listener(_async_update_listener) + hass.data[DOMAIN][config_entry.entry_id].update( + { + UPDATE_LISTENER: update_listener, + "reload_settings": integration_reload_settings(config_entry), + } + ) + config_entry.async_on_unload( + coordinator.async_add_listener( + partial( + _sync_wiser_room_names, + hass, + config_entry, + coordinator, + ) + ) + ) - # Setup websocket services for frontend cards - await async_register_websockets(hass, coordinator) + # Remap historical name-based entity IDs before platform setup so existing + # entities, history, and dashboard references are retained. + await async_migrate_physical_entity_unique_ids( + hass, + config_entry, + coordinator, + previous_room_names, + ) # Setup services await async_setup_services(hass, coordinator) + # Setup platforms after services so entities can safely restore scheduled + # actions from async_added_to_hass. + await hass.config_entries.async_forward_entry_setups(config_entry, WISER_PLATFORMS) + + # Setup websocket services for frontend cards + await async_register_websockets(hass, coordinator) + # Register custom cards moodule_register = JSModuleRegistration(hass) await moodule_register.async_register() + await async_update_wiser_panel(hass) _LOGGER.info( "Wiser Component Setup Completed (%s)", coordinator.wiserhub.system.name @@ -226,6 +426,88 @@ async def async_setup_entry(hass: HomeAssistant, config_entry): return True +@callback +def _remember_opentherm_connection(hass, config_entry, coordinator) -> None: + """Persist confirmed OpenTherm connection and hot-water capability. + + The flags intentionally live in config-entry data so the existing entry + update listener reloads platforms when runtime detection changes. They + also prevent a partial first response after restart from removing entities. + """ + system = getattr(coordinator.wiserhub, "system", None) + if system is None: + return + + opentherm = getattr(system, "opentherm", None) + remembered = config_entry.data.get(CONF_OPENTHERM_EVER_CONNECTED, False) + hot_water_capable = config_entry.data.get( + CONF_OPENTHERM_HOT_WATER_CAPABLE, False + ) + entry_data = dict(config_entry.data) + + if getattr(opentherm, "enabled", None) is False: + if remembered: + entry_data.pop(CONF_OPENTHERM_EVER_CONNECTED, None) + if hot_water_capable: + entry_data.pop(CONF_OPENTHERM_HOT_WATER_CAPABLE, None) + remembered = False + elif ( + not remembered + and getattr(opentherm, "connection_status", None) == "Connected" + ): + entry_data[CONF_OPENTHERM_EVER_CONNECTED] = True + remembered = True + + boiler_parameters = getattr(opentherm, "boiler_parameters", None) + if ( + not hot_water_capable + and getattr(boiler_parameters, "hw_setpoint_read_write", None) is True + ): + entry_data[CONF_OPENTHERM_HOT_WATER_CAPABLE] = True + + if entry_data != config_entry.data: + hass.config_entries.async_update_entry(config_entry, data=entry_data) + + detected = bool(opentherm and remembered) + if getattr(coordinator, "_wiser_opentherm_detected", None) is detected: + return + + coordinator._wiser_opentherm_detected = detected + _sync_opentherm_entity_registry( + hass, + config_entry, + coordinator, + detected=detected, + ) + + +@callback +def _sync_opentherm_entity_registry( + hass, config_entry, coordinator, detected=None +) -> None: + """Hide false OpenTherm entities while preserving their registry data.""" + system = getattr(coordinator.wiserhub, "system", None) + if system is None: + return + + opentherm = getattr(system, "opentherm", None) + if detected is None: + detected = opentherm_is_detected(config_entry, opentherm) + unique_ids = opentherm_entity_unique_ids(coordinator) + registry = er.async_get(hass) + + for entry in er.async_entries_for_config_entry(registry, config_entry.entry_id): + if (entry.domain, entry.unique_id) not in unique_ids: + continue + if not detected and entry.disabled_by is None: + registry.async_update_entity( + entry.entity_id, + disabled_by=er.RegistryEntryDisabler.INTEGRATION, + ) + elif detected and entry.disabled_by == er.RegistryEntryDisabler.INTEGRATION: + registry.async_update_entity(entry.entity_id, disabled_by=None) + + async def async_update_device_registry(hass: HomeAssistant, config_entry): """Update device registry.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA] @@ -255,46 +537,438 @@ def merge_legacy_hub_device_registry(hass: HomeAssistant, config_entry): ) -def register_room_assigned_devices( - hass: HomeAssistant, config_entry, hub_device_id: str -): +def register_room_assigned_devices(hass: HomeAssistant, config_entry): """Register physical Wiser devices in their matching Wiser room.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA] + area_registry = ar.async_get(hass) device_registry = dr.async_get(hass) for device in data.wiserhub.devices.all: room = data.wiserhub.rooms.get_by_device_id(device.id) if room is None: continue - register_room_assigned_device( + device_entry = register_room_assigned_device( device_registry, config_entry.entry_id, (DOMAIN, get_identifier(data, device.id)), - hub_device_id, room.name, + get_hub_via_device_info(data), manufacturer=MANUFACTURER, name=get_device_name(data, device.id), model=device.product_type, sw_version=device.firmware_version, ) + assign_device_area_if_unset( + device_registry, area_registry, device_entry, room.name + ) + + +def migrate_physical_device_registry( + hass: HomeAssistant, + config_entry, + hub_device_id: str, + previous_room_names=None, +): + """Migrate physical devices from name-derived to stable identifiers.""" + data = hass.data[DOMAIN][config_entry.entry_id][DATA] + device_registry = dr.async_get(hass) + entity_registry = er.async_get(hass) + registry_devices = list( + dr.async_entries_for_config_entry( + device_registry, config_entry.entry_id + ) + ) + registry_entities = list( + er.async_entries_for_config_entry( + entity_registry, config_entry.entry_id + ) + ) + itrv_identifier_prefix = ( + f"{data.wiserhub.system.name} {ENTITY_PREFIX} iTRV " + ) + previous_room_names = previous_room_names or {} + physical_entity_migration = build_physical_entity_unique_id_migration( + data, previous_room_names + ) + physical_entity_ids_by_target = {} + for old_unique_id, new_unique_id in physical_entity_migration.items(): + physical_entity_ids_by_target.setdefault(new_unique_id, set()).add( + old_unique_id + ) + for device in data.wiserhub.devices.all: + stable_identifier = (DOMAIN, get_identifier(data, device.id)) + legacy_identifier = ( + DOMAIN, + get_legacy_device_identifier(data, device.id), + ) + stable_entity_ids = { + get_unique_id(data, "sensor", device.product_type, device.id), + get_unique_id(data, "sensor", "Battery", device.id), + } + stable_entity_ids.update( + { + get_physical_entity_unique_id( + data, "sensor", device.id, entity_type + ) + for entity_type in ( + "power", + "energy", + "energy_received", + ) + } + ) + possible_devices = find_physical_device_candidates( + registry_devices, + registry_entities, + {stable_identifier, legacy_identifier}, + stable_entity_ids, + ) + identifier_prefix = f"{data.wiserhub.system.name} " + historical_device_names = { + value[len(identifier_prefix):] + for candidate in possible_devices + for domain, value in candidate.identifiers + if domain == DOMAIN and value.startswith(identifier_prefix) + } + historical_entity_migration = ( + build_physical_entity_unique_id_migration( + data, + previous_room_names, + {device.id: historical_device_names}, + {device.id}, + ) + ) + for old_unique_id, new_unique_id in historical_entity_migration.items(): + matching_entries = [ + entry + for entry in registry_entities + if entry.platform == DOMAIN + and entry.unique_id in {old_unique_id, new_unique_id} + ] + migrate_entity_unique_id_duplicates( + entity_registry, + matching_entries, + new_unique_id, + ) + room = data.wiserhub.rooms.get_by_device_id(device.id) + temperature_type = None + old_temperature_names = set() + possible_room_names = set() + if room is not None: + possible_room_names.add(room.name) + if previous_name := previous_room_names.get(str(room.id)): + possible_room_names.add(previous_name) + + if device.product_type == "iTRV": + temperature_type = "smartvalve_temp" + for entry in registry_devices: + if entry.model != "iTRV": + continue + for domain, value in entry.identifiers: + if domain != DOMAIN or not value.startswith( + itrv_identifier_prefix + ): + continue + suffix = value[len(itrv_identifier_prefix):] + possible_room_names.add(suffix) + possible_room_names.add(re.sub(r"-\d+$", "", suffix)) + old_temperature_names.update( + f"LTS Temperature iTRV {room_name}" + for room_name in possible_room_names + ) + elif device.product_type == "SmokeAlarmDevice": + temperature_type = "smokealarm_temp" + if room is not None: + old_temperature_names.add( + f"{room.name} {device.name} Temperature" + ) + else: + old_temperature_names.add( + f"{device.name} {device.id} Temperature" + ) + elif device.product_type == "UnderFloorHeating": + temperature_type = "ufh_measured_temp" + old_temperature_names.add(f"{device.name} Measured Temperature") + elif device.product_type in {"HeatingActuator", "CFMT"} and getattr( + device, "floor_temperature_sensor", None + ): + temperature_type = "floor_current_temp" + old_temperature_names.add( + f"LTS Floor Temperature " + f"{room.name if room is not None else device.name}" + ) + + sensor_id_migrations = {} + if temperature_type is not None: + sensor_id_migrations[ + get_physical_entity_unique_id( + data, "sensor", device.id, temperature_type + ) + ] = { + get_unique_id(data, "sensor", name, device.id) + for name in old_temperature_names + } + + room_attached_sensor_ids = set() + for entity_type in ( + "humidity", + "power", + "energy", + "energy_received", + ): + stable_sensor_id = get_physical_entity_unique_id( + data, "sensor", device.id, entity_type + ) + old_sensor_ids = physical_entity_ids_by_target.get( + stable_sensor_id, set() + ) + if old_sensor_ids: + sensor_id_migrations[stable_sensor_id] = old_sensor_ids + if entity_type == "humidity": + # Room-stat humidity is displayed on the logical room device. + # Its entity must not make that room device a physical-device + # migration candidate. + room_attached_sensor_ids.add(stable_sensor_id) + + for stable_sensor_id, old_sensor_ids in sensor_id_migrations.items(): + matching_sensor_entries = [ + entry + for entry in registry_entities + if entry.domain == "sensor" + and entry.platform == DOMAIN + and entry.unique_id + in old_sensor_ids | {stable_sensor_id} + ] + matching_device_ids = { + entry.device_id + for entry in matching_sensor_entries + if entry.device_id is not None + } + + if stable_sensor_id not in room_attached_sensor_ids: + possible_devices.extend( + entry + for entry in registry_devices + if entry.id in matching_device_ids + ) + + migrate_entity_unique_id_duplicates( + entity_registry, + matching_sensor_entries, + stable_sensor_id, + ) + + device_entry = migrate_physical_device( + device_registry, + entity_registry, + config_entry.entry_id, + stable_identifier, + possible_devices, + get_device_name(data, device.id), + manufacturer=MANUFACTURER, + model=device.product_type, + sw_version=device.firmware_version, + creation_parent_info=get_hub_via_device_info(data), + via_device_id=hub_device_id, + ) + if room is not None: + assign_device_area_if_unset( + device_registry, + ar.async_get(hass), + device_entry, + room.name, + ) def migrate_room_device_registry(hass: HomeAssistant, config_entry): """Migrate all logical room devices away from name-derived identifiers.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA] + area_registry = ar.async_get(hass) device_registry = dr.async_get(hass) entity_registry = er.async_get(hass) - for room in data.wiserhub.rooms.all: - migrate_room_device( + for room in active_wiser_rooms(data): + device_entry = migrate_room_device( device_registry, entity_registry, config_entry.entry_id, (DOMAIN, get_identifier(data, room.id, "room")), (DOMAIN, get_legacy_room_identifier(data, room.id)), get_device_name(data, room.id, "room"), + creation_parent_info=get_hub_via_device_info(data), + ) + assign_device_area_if_unset( + device_registry, area_registry, device_entry, room.name + ) + migrate_room_entities( + entity_registry, + device_entry.id, + lambda entity_type: get_room_entity_unique_id( + data, room.id, entity_type + ), + ) + + +def _current_wiser_room_names(coordinator): + """Return Wiser room names keyed by their stable room IDs.""" + return { + str(room.id): room.name for room in active_wiser_rooms(coordinator) + } + + +def _store_wiser_room_names(hass, config_entry, room_names) -> None: + """Persist room names without triggering a config-entry reload.""" + entry_data = hass.data[DOMAIN][config_entry.entry_id] + if entry_data.get(CONF_WISER_ROOM_NAMES) == room_names: + return + room_names = dict(room_names) + entry_data[CONF_WISER_ROOM_NAMES] = room_names + entry_data[ROOM_NAMES_STORE].async_delay_save( + lambda: room_names, + 1, + ) + + +def _confirmed_deleted_room_ids( + hass, + config_entry, + previous_room_names, + current_room_names, +): + """Track missing rooms across successful coordinator refreshes.""" + missing_counts = hass.data[DOMAIN][config_entry.entry_id].setdefault( + "missing_room_counts", {} + ) + return confirmed_deleted_room_ids( + previous_room_names, + current_room_names, + missing_counts, + ) + + +def _device_by_identifier(device_registry, config_entry_id, identifier): + """Return a device registry entry using current and older HA APIs.""" + get_by_identifier = getattr( + device_registry, "async_get_device_by_identifier", None + ) + if get_by_identifier: + return get_by_identifier(identifier, config_entry_id) + return device_registry.async_get_device(identifiers={identifier}) + + +def remove_deleted_room_devices( + hass, + config_entry, + previous_room_names, + deleted_room_ids, +) -> int: + """Remove registry records for rooms deleted from the Wiser system.""" + data = hass.data[DOMAIN][config_entry.entry_id][DATA] + identifiers = [] + + for room_id in deleted_room_ids: + identifiers.extend( + ( + (DOMAIN, get_identifier(data, room_id, "room")), + ( + DOMAIN, + f"{data.wiserhub.system.name} {ENTITY_PREFIX} " + f"{previous_room_names[room_id]}", + ), + ) + ) + + removed = remove_room_devices( + dr.async_get(hass), + er.async_get(hass), + config_entry.entry_id, + identifiers, + ) + if removed: + _LOGGER.info("Removed %s deleted Wiser room device(s)", removed) + return removed + + +def sync_wiser_room_areas(hass, config_entry, previous_room_names) -> int: + """Move Wiser devices when their integration-managed room is renamed.""" + data = hass.data[DOMAIN][config_entry.entry_id][DATA] + device_registry = dr.async_get(hass) + area_registry = ar.async_get(hass) + current_rooms = active_wiser_rooms(data) + current_room_names = {room.name for room in current_rooms} + moved = 0 + + for room in current_rooms: + room_device = _device_by_identifier( + device_registry, + config_entry.entry_id, + (DOMAIN, get_identifier(data, room.id, "room")), + ) + previous_name = previous_room_names.get(str(room.id)) + if previous_name is None and room_device and room_device.area_id: + previous_name = known_wiser_room_area_name( + area_registry, + room_device.area_id, + current_room_names, + ) + + room_devices = [room_device] + for device in data.wiserhub.devices.all: + device_room = data.wiserhub.rooms.get_by_device_id(device.id) + if device_room is None or device_room.id != room.id: + continue + room_devices.append( + _device_by_identifier( + device_registry, + config_entry.entry_id, + (DOMAIN, get_identifier(data, device.id)), + ) + ) + + moved += move_devices_from_managed_area( + device_registry, + area_registry, + room_devices, + previous_name, + room.name, ) + return moved + + +def _sync_wiser_room_names(hass, config_entry, coordinator) -> None: + """Apply Wiser room renames after a coordinator refresh.""" + previous = hass.data[DOMAIN][config_entry.entry_id].get( + CONF_WISER_ROOM_NAMES, {} + ) + current = _current_wiser_room_names(coordinator) + if previous == current: + hass.data[DOMAIN][config_entry.entry_id].setdefault( + "missing_room_counts", {} + ).clear() + return + confirmed_deleted_ids = _confirmed_deleted_room_ids( + hass, + config_entry, + previous, + current, + ) + remove_deleted_room_devices( + hass, + config_entry, + previous, + confirmed_deleted_ids, + ) + sync_wiser_room_areas(hass, config_entry, previous) + stored_room_names = room_names_with_pending_deletions( + previous, + current, + confirmed_deleted_ids, + ) + if stored_room_names == previous: + return + _store_wiser_room_names(hass, config_entry, stored_room_names) + def update_hub_device_names(hass: HomeAssistant): """Keep physical hub names concise while distinguishing multiple hubs.""" @@ -316,19 +990,22 @@ def update_hub_device_names(hass: HomeAssistant): async def _async_update_listener(hass: HomeAssistant, config_entry): """Handle options update.""" - await hass.config_entries.async_reload(config_entry.entry_id) + await async_handle_entry_update(hass, config_entry) async def async_remove_config_entry_device( hass: HomeAssistant, config_entry, device_entry ) -> bool: - """Delete device if not entities.""" - if device_entry.model == "Controller" or ( - DOMAIN, - config_entry.data.get(CONF_NAME), - ) in device_entry.identifiers: + """Allow device removal while protecting the physical HeatHub.""" + data = hass.data.get(DOMAIN, {}).get(config_entry.entry_id, {}).get(DATA) + hub_name = ( + data.wiserhub.system.name + if data is not None + else config_entry.data.get(CONF_NAME) + ) + if (DOMAIN, hub_name) in device_entry.identifiers: _LOGGER.error( - "You cannot delete the Wiser HeatHub using device delete. Please remove the integration instead" + "You cannot delete the Wiser HeatHub using device delete. Please remove the integration instead" ) return False return True @@ -367,7 +1044,9 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry): _LOGGER.debug("Unload integration") if unload_ok: + await async_unload_card_updates(hass, config_entry) hass.data[DOMAIN].pop(config_entry.entry_id) + await async_update_wiser_panel(hass) update_hub_device_names(hass) return unload_ok diff --git a/custom_components/wiser/binary_sensor.py b/custom_components/wiser/binary_sensor.py index 8fa2150f..aed9aa42 100644 --- a/custom_components/wiser/binary_sensor.py +++ b/custom_components/wiser/binary_sensor.py @@ -2,22 +2,32 @@ import logging +from aioWiserHeatAPI.const import TEXT_UNKNOWN + from homeassistant.components.binary_sensor import ( BinarySensorDeviceClass, BinarySensorEntity, ) from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers import entity_registry as er from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import CONF_OPENTHERM_SENSORS, DATA, DOMAIN, ENTITY_PREFIX, MANUFACTURER +from .const import CONF_OPENTHERM_SENSORS, DATA, DOMAIN, MANUFACTURER from .entity import WiserEntityMixin from .helpers import ( + active_wiser_rooms, get_device_name, get_hub_device_info, get_hub_via_device_info, get_identifier, + get_legacy_unique_id, + get_light_binary_sensor_unique_id, + get_light_entity_name, + get_physical_entity_unique_id, + get_room_entity_unique_id, get_unique_id, + get_uuid_unique_id, ) from .opentherm import ( OPENTHERM_BINARY_SENSOR_KEYS, @@ -27,15 +37,83 @@ opentherm_sensor_value, opentherm_sensor_is_enabled, ) +from .opentherm_detection import opentherm_is_detected _LOGGER = logging.getLogger(__name__) +SUMMER_DISCOMFORT_PREVENTION = "Summer Discomfort Prevention" +SUMMER_COMFORT_AVAILABLE = "Summer Comfort Available" +PCM_DEVICE_LIMIT_REACHED = "PCM Device Limit Reached" +SUMMER_COMFORT_BINARY_SENSOR_TYPES = ( + SUMMER_DISCOMFORT_PREVENTION, + SUMMER_COMFORT_AVAILABLE, +) +PCM_BINARY_SENSOR_TYPES = (PCM_DEVICE_LIMIT_REACHED,) + def _entity_translation_key(name: str) -> str: """Return the stable translation key for a fixed Wiser entity label.""" return name.lower().replace(" ", "_") +def _supports_v2_system_property(data, property_name: str) -> bool: + """Return whether the hub supplies a second-generation system property.""" + if data.hub_version < 2: + return False + return getattr(data.wiserhub.system, property_name, None) is not None + + +def _supports_summer_discomfort_prevention(data) -> bool: + """Return whether the hub supplies summer-discomfort data.""" + return _supports_v2_system_property( + data, "summer_discomfort_prevention" + ) + + +def _supports_summer_comfort_available(data) -> bool: + """Return whether the hub supplies summer-comfort availability data.""" + return _supports_v2_system_property( + data, "summer_comfort_available" + ) + + +def _supports_pcm_binary_sensor(data) -> bool: + """Return whether the hub supplies PCM system data.""" + if data.hub_version < 2: + return False + return getattr(data.wiserhub.system, "pcm_version", TEXT_UNKNOWN) not in ( + None, + TEXT_UNKNOWN, + ) + + +def _requires_v2_entity_cleanup(data) -> bool: + """Return whether the hub is definitively unable to support V2 entities.""" + return data.hub_version < 2 + + +def _remove_unsupported_v2_entities( + hass: HomeAssistant, config_entry_id: str, data, sensor_types +) -> None: + """Remove V2-only registry entries from an unsupported hub.""" + registry = er.async_get(hass) + legacy_hub_name = data.wiserhub.system.name.replace("WiserHeat", "HeatHub") + for sensor_type in sensor_types: + unique_id = get_uuid_unique_id( + get_legacy_unique_id( + data, + "sensor", + sensor_type, + f"{legacy_hub_name} {sensor_type}", + ) + ) + entity_id = registry.async_get_entity_id("binary_sensor", DOMAIN, unique_id) + if entity_id is not None: + entry = registry.async_get(entity_id) + if entry and entry.config_entry_id == config_entry_id: + registry.async_remove(entity_id) + + async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): """Set up Wiser climate device.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA] # Get Handler @@ -43,7 +121,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie binary_sensors = [] opentherm = getattr(data.wiserhub.system, "opentherm", None) - if opentherm is not None and opentherm.enabled: + if opentherm_is_detected(config_entry, opentherm): configured_sensors = config_entry.options.get(CONF_OPENTHERM_SENSORS) binary_sensors.extend( WiserOpenThermAttributeBinarySensor(data, key) @@ -60,15 +138,29 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie ) ) - # System sensors - if data.wiserhub.system: - binary_sensors.extend( - [ - WiserSummerDiscomfortPrevention(data, 0, "Summer Discomfort Prevention"), - WiserSummerComfortAvailable(data, 0, "Summer Comfort Available"), - WiserPCMDeviceLimitReached(data, 0, "PCM Device Limit Reached") - ] - ) + if _supports_summer_discomfort_prevention(data): + binary_sensors.append( + WiserSummerDiscomfortPrevention( + data, 0, SUMMER_DISCOMFORT_PREVENTION + ) + ) + + if _supports_summer_comfort_available(data): + binary_sensors.append( + WiserSummerComfortAvailable(data, 0, SUMMER_COMFORT_AVAILABLE) + ) + + if _supports_pcm_binary_sensor(data): + binary_sensors.append( + WiserPCMDeviceLimitReached(data, 0, PCM_DEVICE_LIMIT_REACHED) + ) + if _requires_v2_entity_cleanup(data): + _remove_unsupported_v2_entities( + hass, + config_entry.entry_id, + data, + SUMMER_COMFORT_BINARY_SENSOR_TYPES + PCM_BINARY_SENSOR_TYPES, + ) # Smoke alarm sensors for device in data.wiserhub.devices.smokealarms.all: @@ -80,6 +172,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie WiserFaultWarning(data, device.id, "Fault Warning"), WiserRemoteAlarm(data, device.id, "Remote Alarm"), WiserBatteryDefect(data, device.id, "Battery Defect"), + WiserACMains(data, device.id, "AC Mains"), + WiserTestMode(data, device.id, "Test Mode"), + WiserHushMode(data, device.id, "Hush Mode"), ] ) @@ -94,7 +189,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie ) # Room binary sensors - for room in data.wiserhub.rooms.all: + for room in active_wiser_rooms(data): binary_sensors.extend( [ WiserRoomWindow(data, room.id, "Window Detection Active"), @@ -201,12 +296,9 @@ def is_on(self): @property def unique_id(self): - """Return uniqueid.""" - legacy_name = ( - f"{get_device_name(self._data, self._device_id)} {self._sensor_type}" - ) - return get_unique_id( - self._data, "binary_sensor", self._sensor_type, legacy_name + """Return a name-independent unique ID.""" + return get_physical_entity_unique_id( + self._data, "binary_sensor", self._device_id, self._sensor_type ) @property @@ -253,6 +345,7 @@ def __init__(self, coordinator, sensor_key) -> None: @property def available(self): + # Keep the entity during outages without exposing stale boiler state. opentherm = self._data.wiserhub.system.opentherm return ( super().available @@ -315,10 +408,13 @@ def __init__(self, coordinator, device_id=0, sensor_type="") -> None: def _handle_coordinator_update(self) -> None: """Handle updated data from the coordinator.""" _LOGGER.debug(f"{self._sensor_type} device update requested") - HeatHub = self._data.wiserhub.system.name - HeatHub = HeatHub.replace("WiserHeat","HeatHub") - return f"{HeatHub} {self._sensor_type}" - + self._state = getattr( + self._data.wiserhub.system, + self._sensor_type.replace(" ", "_").lower(), + None, + ) + self.async_write_ha_state() + @property def is_on(self): """Return the state of the sensor.""" @@ -377,7 +473,11 @@ def is_on(self): @property def unique_id(self): """Return uniqueid.""" - return get_unique_id(self._data, "sensor", self._sensor_type, self._room_id) + return get_room_entity_unique_id( + self._data, + self._room_id, + f"binary_sensor_{self._attr_translation_key}", + ) @property def device_info(self): @@ -439,8 +539,47 @@ class WiserRemoteAlarm(BaseBinarySensor): """Smoke Alarm sensor.""" +class WiserACMains(BaseBinarySensor): + """Smoke alarm mains-power status sensor.""" + + _attr_device_class = BinarySensorDeviceClass.POWER + + +class WiserTestMode(BaseBinarySensor): + """Smoke alarm test-mode status sensor.""" + + _attr_icon = "mdi:test-tube" + + +class WiserHushMode(BaseBinarySensor): + """Smoke alarm hush-mode status sensor.""" + + _attr_icon = "mdi:volume-off" + + class WiserEquipment(BaseBinarySensor): - """Base binary sensor class.""" + """Binary sensor backed by optional equipment data.""" + + @property + def available(self) -> bool: + """Return whether equipment data is currently available.""" + return ( + super().available + and self._device is not None + and getattr(self._device, "equipment", None) is not None + ) + + @callback + def _handle_coordinator_update(self) -> None: + """Refresh state without failing when equipment data is missing.""" + self._device = self._data.wiserhub.devices.get_by_id(self._device_id) + equipment = getattr(self._device, "equipment", None) + self._state = ( + getattr(equipment, self._sensor_type.replace(" ", "_").lower()) + if equipment is not None + else None + ) + self.async_write_ha_state() ## binary sensor of System @@ -463,7 +602,7 @@ class WiserSummerComfortAvailable(SystemBinarySensor): _attr_device_class = BinarySensorDeviceClass.HEAT class WiserPCMDeviceLimitReached(SystemBinarySensor): - """Summer Comfort Available sensor.""" + """PCM device limit reached sensor.""" _attr_device_class = BinarySensorDeviceClass.POWER @@ -485,7 +624,14 @@ def __init__(self, coordinator, light_id, sensor_type=""): @property def name(self): """Return the name of the sensor.""" - return f"{ENTITY_PREFIX} {self._light.name} {self._sensor_type}" + return get_light_entity_name(self._data, self._light, self._sensor_type) + + @property + def unique_id(self): + """Return the per-channel light capability sensor unique ID.""" + return get_light_binary_sensor_unique_id( + self._data, self._light, self._sensor_type + ) class WiserStateIsTiltSupported(BaseBinarySensor): diff --git a/custom_components/wiser/button.py b/custom_components/wiser/button.py index ebd23aae..a0a13aab 100644 --- a/custom_components/wiser/button.py +++ b/custom_components/wiser/button.py @@ -84,7 +84,7 @@ async def async_force_update(self, delay: int = 0): """Force an update from the hub.""" _LOGGER.debug("Hub update initiated by %s", self._name) if delay: - asyncio.sleep(delay) + await asyncio.sleep(delay) state = await self.async_get_last_state() if state is not None and state.state is not None: self.__last_pressed = ( # pylint: disable=unused-private-member diff --git a/custom_components/wiser/climate.py b/custom_components/wiser/climate.py index 638bb232..2f7edef9 100755 --- a/custom_components/wiser/climate.py +++ b/custom_components/wiser/climate.py @@ -54,9 +54,10 @@ get_device_name, get_hub_via_device_info, get_identifier, - get_room_name, + get_room_entity_unique_id, get_uuid_unique_id, hub_error_handler, + room_has_climate, ) from .schedules import WiserScheduleEntity from .temperature import room_target_temperature @@ -142,7 +143,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie wiser_rooms = [ WiserRoom(hass, coordinator, room.id) for room in coordinator.wiserhub.rooms.all - if len(room.devices) > 0 + if room_has_climate(room) ] async_add_entities(wiser_rooms, True) @@ -498,7 +499,6 @@ def extra_state_attributes(self): attrs["away_mode_supressed"] = self._room.away_mode_suppressed attrs["heating_type"] = self._room.heating_type attrs["number_of_heating_actuators"] = self._room.number_of_heating_actuators - attrs["demand_type"] = self._room.demand_type # Status attrs["target_temperature_origin"] = self._room.target_temperature_origin @@ -508,13 +508,22 @@ def extra_state_attributes(self): attrs["is_passive"] = self._room.is_passive_mode attrs["control_output_state"] = "On" if self._room.is_heating else "Off" attrs["heating_rate"] = self._room.heating_rate + attrs["percentage_demand"] = self._room.percentage_demand + + # OpenTherm + opentherm = getattr(self._data.wiserhub.system, "opentherm", None) + if opentherm is not None and opentherm.enabled: + attrs["demand_type"] = self._room.demand_type + if hasattr(self._room, "is_calling_for_heat"): + attrs["is_calling_for_heat"] = self._room.is_calling_for_heat + else: + attrs["is_calling_for_heat"] = self._room.percentage_demand > 0 # If boosted show boost end time if self._room.is_boosted: attrs["boost_end"] = self._room.boost_end_time attrs["boost_time_remaining"] = int(self._room.boost_time_remaining / 60) - attrs["percentage_demand"] = self._room.percentage_demand attrs["comfort_mode_score"] = self._room.comfort_mode_score attrs["control_direction"] = self._room.control_direction attrs["displayed_setpoint"] = self._room.displayed_setpoint @@ -699,12 +708,7 @@ def temperature_unit(self): @property def unique_id(self): """Return unique Id.""" - legacy_name = get_room_name(self._data, self._room_id) - legacy_unique_id = ( - f"{self._data.wiserhub.system.name}-WiserRoom-" - f"{self._room_id}-{legacy_name}" - ) - return get_uuid_unique_id(legacy_unique_id) + return get_room_entity_unique_id(self._data, self._room_id, "climate") @hub_error_handler async def async_boost_heating( diff --git a/custom_components/wiser/config_flow.py b/custom_components/wiser/config_flow.py index c7acd9a1..668bd3cb 100755 --- a/custom_components/wiser/config_flow.py +++ b/custom_components/wiser/config_flow.py @@ -23,6 +23,8 @@ CONF_PASSWORD, CONF_PORT, CONF_SCAN_INTERVAL, + MAJOR_VERSION, + MINOR_VERSION, UnitOfTemperature, ) from homeassistant.core import HomeAssistant, callback @@ -31,12 +33,16 @@ BooleanSelector, EntitySelector, EntitySelectorConfig, + SELECTORS, SelectSelectorMode, + Selector, + make_selector_config_schema, selector, ) from homeassistant.helpers.service_info.zeroconf import ZeroconfServiceInfo from .const import ( + CONF_ADDITIONAL_SENSORS, CONF_AUTOMATIONS_HW_AUTO_MODE, CONF_AUTOMATIONS_HW_BOOST_MODE, CONF_AUTOMATIONS_HW_CLIMATE, @@ -45,23 +51,35 @@ CONF_AUTOMATIONS_HW_SENSOR_ENTITY_ID, CONF_AUTOMATIONS_PASSIVE, CONF_AUTOMATIONS_PASSIVE_TEMP_INCREMENT, + CONF_SCHEDULE_SETPOINT_ROOMS, + CONF_EQUIPMENT_SENSORS, CONF_HEATING_BOOST_TEMP, CONF_HEATING_BOOST_TIME, CONF_HOSTNAME, CONF_HW_BOOST_TIME, + CONF_LEGACY_NAMING, + CONF_OPENTHERM_HOT_WATER_CONTROL, + CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP, + CONF_OPENTHERM_HOT_WATER_PRESETS, + CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID, CONF_OPENTHERM_SENSORS, CONF_RESTORE_MANUAL_TEMP_OPTION, CONF_SETPOINT_MODE, + CONF_SHOW_WISER_SIDEBAR, CUSTOM_DATA_STORE, DEFAULT_BOOST_TEMP, DEFAULT_BOOST_TEMP_TIME, DEFAULT_PASSIVE_TEMP_INCREMENT, + DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP, + DEFAULT_OPENTHERM_HOT_WATER_PRESET_TEMP, DEFAULT_SCAN_INTERVAL, DATA, DOMAIN, WISER_RESTORE_TEMP_DEFAULT_OPTIONS, WISER_SETPOINT_MODES, HWCycleModes, + MAX_OPENTHERM_HOT_WATER_PRESETS, + OPENTHERM_HOT_WATER_DEFAULT_BOOSTS, ) from .opentherm import ( OPENTHERM_SENSOR_CATEGORIES, @@ -70,9 +88,100 @@ detected_opentherm_sensor_keys, opentherm_sensor_is_enabled, ) +from .opentherm_detection import ( + opentherm_hot_water_control_is_supported, + opentherm_hot_water_limits, + opentherm_is_detected, +) +from .helpers import room_has_climate _LOGGER = logging.getLogger(__name__) + +def _default_opentherm_hot_water_presets(minimum=35.0, maximum=55.0): + """Return the default timed hot-water boosts from the original control.""" + return [ + { + "name": f"Boost {duration} Mins", + "icon": f"mdi:numeric-{index}", + "temperature": min( + max(DEFAULT_OPENTHERM_HOT_WATER_PRESET_TEMP, minimum), maximum + ), + "duration_minutes": duration, + } + for index, duration in enumerate( + OPENTHERM_HOT_WATER_DEFAULT_BOOSTS, start=1 + ) + ] + + +def _validate_opentherm_hot_water_presets( + value, minimum=35.0, maximum=55.0, *, clamp_temperature=False +): + """Validate the custom preset selector value.""" + if not isinstance(value, list): + raise vol.Invalid("Presets must be a list") + if len(value) > MAX_OPENTHERM_HOT_WATER_PRESETS: + raise vol.Invalid("Too many presets") + + presets = [] + names = set() + for item in value: + if not isinstance(item, dict): + raise vol.Invalid("Each preset must be an object") + name = str(item.get("name", "")).strip() + folded_name = name.casefold() + if not name or folded_name in {"off", "manual", "auto", "custom"}: + raise vol.Invalid("Preset name is empty or reserved") + if folded_name in names: + raise vol.Invalid("Preset names must be unique") + try: + temperature = float(item.get("temperature")) + duration = int(item.get("duration_minutes", 0)) + except (TypeError, ValueError) as err: + raise vol.Invalid( + "Preset temperature and duration must be numbers" + ) from err + icon = str(item.get("icon", "")).strip() + if clamp_temperature: + temperature = min(max(temperature, minimum), maximum) + if not minimum <= temperature <= maximum or not 0 <= duration <= 999: + raise vol.Invalid("Preset temperature or duration is out of range") + presets.append( + { + "name": name, + "icon": icon, + "temperature": temperature, + "duration_minutes": duration, + } + ) + names.add(folded_name) + return presets + + +@SELECTORS.register("opentherm_presets_v2") +class OpenThermPresetsSelector(Selector): + """Render the preset editor supplied by the Wiser frontend module.""" + + # Keep the selector name versioned. Home Assistant registers custom + # elements once per browser session, so reusing the old name would leave a + # previously loaded implementation in place. + selector_type = "opentherm_presets_v2" + CONFIG_SCHEMA = make_selector_config_schema( + { + vol.Optional("minimum", default=35.0): vol.Coerce(float), + vol.Optional("maximum", default=55.0): vol.Coerce(float), + } + ) + + def __call__(self, data): + return _validate_opentherm_hot_water_presets( + data, + float(self.config.get("minimum", 35.0)), + float(self.config.get("maximum", 55.0)), + ) + + DATA_SCHEMA = vol.Schema( { vol.Required(CONF_HOST): str, @@ -115,7 +224,7 @@ class WiserFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): """ VERSION = 1 - MINOR_VERSION = 4 + MINOR_VERSION = 5 CONNECTION_CLASS = config_entries.CONN_CLASS_LOCAL_POLL def __init__(self) -> None: @@ -156,7 +265,11 @@ async def async_step_user(self, user_input=None): user_input[CONF_NAME] = validated["title"] return self.async_create_entry( - title=validated["title"], data=user_input + title=validated["title"], + data=user_input, + options={ + CONF_LEGACY_NAMING: (MAJOR_VERSION, MINOR_VERSION) < (2026, 8) + }, ) return self.async_show_form( @@ -217,7 +330,11 @@ async def async_step_zeroconf_confirm( # Add hub name to config user_input[CONF_NAME] = validated["title"] return self.async_create_entry( - title=validated["title"], data=user_input + title=validated["title"], + data=user_input, + options={ + CONF_LEGACY_NAMING: (MAJOR_VERSION, MINOR_VERSION) < (2026, 8) + }, ) return self.async_show_form( @@ -251,18 +368,229 @@ def _opentherm(self): entry_data = self.hass.data.get(DOMAIN, {}).get(self.config_entry.entry_id) if not entry_data: return None - return getattr(entry_data[DATA].wiserhub.system, "opentherm", None) + opentherm = getattr(entry_data[DATA].wiserhub.system, "opentherm", None) + return ( + opentherm + if opentherm_is_detected(self.config_entry, opentherm) + else None + ) + + def _equipment_available(self): + """Return whether the loaded entry has equipment data available.""" + entry_data = self.hass.data.get(DOMAIN, {}).get(self.config_entry.entry_id) + if not entry_data: + return False + devices = entry_data[DATA].wiserhub.devices + + def all_devices(collection_name): + collection = getattr(devices, collection_name, None) + return getattr(collection, "all", ()) + + return any( + getattr(device, "equipment", None) is not None + for collection_name in ( + "smartplugs", + "power_tags", + "power_tags_c", + "heating_actuators", + "evse", + ) + for device in all_devices(collection_name) + ) + + def _rooms_without_climate(self): + """Return loaded Wiser rooms that do not create climate entities.""" + entry_data = self.hass.data.get(DOMAIN, {}).get(self.config_entry.entry_id) + if not entry_data: + return [] + return [ + room + for room in entry_data[DATA].wiserhub.rooms.all + if not room_has_climate(room) + ] async def async_step_init(self, user_input=None): """Handle options flow.""" - menu_options = ["main_params", "automation_params"] + menu_options = [ + "main_params", + "automation_params", + "ui_options", + ] if self._opentherm() is not None: - menu_options.append("opentherm_sensors") + menu_options.append("opentherm") + if self._equipment_available() or self.config_entry.options.get( + CONF_EQUIPMENT_SENSORS, False + ): + menu_options.append("equipment_sensors") return self.async_show_menu( step_id="init", menu_options=menu_options, ) + async def async_step_opentherm(self, user_input=None): + """Choose an OpenTherm feature to configure.""" + if self._opentherm() is None: + return self.async_abort(reason="opentherm_not_available") + menu_options = ["opentherm_sensors"] + if opentherm_hot_water_control_is_supported( + self.config_entry, self._opentherm() + ): + menu_options.insert(0, "opentherm_hot_water_control") + return self.async_show_menu( + step_id="opentherm", + menu_options=menu_options, + ) + + async def async_step_opentherm_hot_water_control(self, user_input=None): + """Configure Home Assistant control of the OpenTherm DHW setpoint.""" + opentherm = self._opentherm() + if opentherm is None or not opentherm_hot_water_control_is_supported( + self.config_entry, opentherm + ): + return self.async_abort(reason="opentherm_not_available") + minimum, maximum = opentherm_hot_water_limits(opentherm) + try: + manual_default = float( + self.config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP, + DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP, + ) + ) + except (TypeError, ValueError): + manual_default = DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP + manual_default = min(max(manual_default, minimum), maximum) + try: + preset_default = _validate_opentherm_hot_water_presets( + self.config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_PRESETS, + _default_opentherm_hot_water_presets(minimum, maximum), + ), + minimum, + maximum, + clamp_temperature=True, + ) + except vol.Invalid: + preset_default = _default_opentherm_hot_water_presets( + minimum, maximum + ) + errors = {} + if user_input is not None: + try: + user_input[CONF_OPENTHERM_HOT_WATER_PRESETS] = ( + _validate_opentherm_hot_water_presets( + user_input.get(CONF_OPENTHERM_HOT_WATER_PRESETS, []), + minimum, + maximum, + ) + ) + except vol.Invalid: + errors["base"] = "invalid_hot_water_presets" + else: + options = self.config_entry.options | user_input + if not user_input.get( + CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID + ): + options.pop( + CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID, None + ) + # Remove the prototype option now that Off follows the minimum + # domestic-hot-water temperature reported by OpenTherm. + options.pop("opentherm_hot_water_off_temperature", None) + return self.async_create_entry(data=options) + return self.async_show_form( + step_id="opentherm_hot_water_control", + data_schema=vol.Schema( + { + vol.Optional( + CONF_OPENTHERM_HOT_WATER_CONTROL, + default=self.config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_CONTROL, False + ), + ): BooleanSelector(), + vol.Optional( + CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP, + default=manual_default, + ): vol.All( + vol.Coerce(float), + vol.Range(min=minimum, max=maximum), + ), + vol.Optional( + CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID, + description={ + "suggested_value": self.config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID + ) + }, + ): EntitySelector( + EntitySelectorConfig( + domain="sensor", + device_class="temperature", + multiple=False, + ) + ), + vol.Optional( + CONF_OPENTHERM_HOT_WATER_PRESETS, + default=preset_default, + ): OpenThermPresetsSelector( + {"minimum": minimum, "maximum": maximum} + ), + } + ), + errors=errors, + ) + + async def async_step_equipment_sensors(self, user_input=None): + """Configure detailed equipment data sensors.""" + if not self._equipment_available() and not self.config_entry.options.get( + CONF_EQUIPMENT_SENSORS, False + ): + return self.async_abort(reason="equipment_not_available") + if user_input is not None: + return self.async_create_entry( + data=self.config_entry.options | user_input + ) + return self.async_show_form( + step_id="equipment_sensors", + data_schema=vol.Schema( + { + vol.Optional( + CONF_EQUIPMENT_SENSORS, + default=self.config_entry.options.get( + CONF_EQUIPMENT_SENSORS, False + ), + ): BooleanSelector(), + } + ), + ) + + async def async_step_ui_options(self, user_input=None): + """Configure Wiser UI options, including legacy naming and sidebar panels.""" + if user_input is not None: + return self.async_create_entry( + data=self.config_entry.options | user_input + ) + + return self.async_show_form( + step_id="ui_options", + data_schema=vol.Schema( + { + vol.Optional( + CONF_LEGACY_NAMING, + default=self.config_entry.options.get( + CONF_LEGACY_NAMING, True + ), + ): BooleanSelector(), + vol.Optional( + CONF_SHOW_WISER_SIDEBAR, + default=self.config_entry.options.get( + CONF_SHOW_WISER_SIDEBAR, + True, + ), + ): BooleanSelector(), + } + ), + ) + async def async_step_opentherm_sensors(self, user_input=None): """Choose an OpenTherm sensor category.""" opentherm = self._opentherm() @@ -426,6 +754,9 @@ async def async_step_automation_params(self, user_input=None): return self.async_create_entry(data=options) options = self.config_entry.options + additional_sensor_options = options.get(CONF_ADDITIONAL_SENSORS, {}) + if not isinstance(additional_sensor_options, dict): + additional_sensor_options = {} data_schema = { vol.Required(CONF_AUTOMATIONS_PASSIVE): section( @@ -531,12 +862,38 @@ async def async_step_automation_params(self, user_input=None): ), {"collapsed": True}, ), + vol.Required(CONF_ADDITIONAL_SENSORS): section( + vol.Schema( + { + vol.Optional( + CONF_SCHEDULE_SETPOINT_ROOMS, + default=[ + str(room_id) + for room_id in additional_sensor_options.get( + CONF_SCHEDULE_SETPOINT_ROOMS, [] + ) + ], + ): selector( + { + "select": { + "options": [ + {"value": str(room.id), "label": room.name} + for room in self._rooms_without_climate() + ], + "multiple": True, + "mode": SelectSelectorMode.DROPDOWN, + } + } + ), + } + ), + {"collapsed": True}, + ), } return self.async_show_form( step_id="automation_params", data_schema=vol.Schema(data_schema) ) - class CannotConnect(exceptions.HomeAssistantError): """Error to indicate we cannot connect.""" diff --git a/custom_components/wiser/const.py b/custom_components/wiser/const.py index 3bb16874..3435121e 100755 --- a/custom_components/wiser/const.py +++ b/custom_components/wiser/const.py @@ -6,24 +6,33 @@ """ from enum import StrEnum +import json +from pathlib import Path -VERSION = "3.4.21b0" +VERSION = "4.0.0" DOMAIN = "wiser" +CONF_LEGACY_NAMING = "legacy_naming" DATA_WISER_CONFIG = "wiser_config" URL_BASE = "/wiser" +CONF_SHOW_WISER_SIDEBAR = "show_wiser_sidebar" +CONF_WISER_PANEL_CONFIG = "wiser_panel_config" +CONF_WISER_ROOM_NAMES = "wiser_room_names" + + +def _load_card_manifest(): + """Load the shared card registry used by packaging and runtime setup.""" + try: + registry = json.loads( + (Path(__file__).parent / "frontend/cards.json").read_text("utf-8") + ) + return registry.get("cards", []) if isinstance(registry, dict) else registry + except FileNotFoundError: + # Keep setup resilient if an incomplete installation omits the registry. + return [] + + +CARD_MANIFEST = _load_card_manifest() -JSMODULES = [ - { - "name": "Wiser Schedule Card", - "filename": "wiser-schedule-card.js", - "version": "1.5.6", - }, - { - "name": "Wiser Zigbee Card", - "filename": "wiser-zigbee-card.js", - "version": "2.1.2", - }, -] WISER_PLATFORMS = [ "binary_sensor", @@ -35,6 +44,8 @@ "number", "light", "cover", + "water_heater", + "update", ] DATA = "data" UPDATE_TRACK = "update_track" @@ -78,6 +89,7 @@ class HWCycleModes(StrEnum): SETPOINT_MODE_BOOST_AUTO = "boost auto mode only" # Custom Configs +CONF_ADDITIONAL_SENSORS = "additional_sensors" CONF_AUTOMATIONS_PASSIVE = "automations_passive_mode" CONF_AUTOMATIONS_PASSIVE_TEMP_INCREMENT = "passive_mode_temperature_increments" CONF_HEATING_BOOST_TEMP = "heating_boost_temp" @@ -94,7 +106,23 @@ class HWCycleModes(StrEnum): CONF_AUTOMATIONS_HW_BOOST_MODE = "hotwater_boost_mode" CONF_AUTOMATIONS_HW_SENSOR_ENTITY_ID = "hotwater_sensor_entity_id" CONF_DEPRECATED_HW_TARGET_TEMP = "hotwater_target_temperature" +CONF_OPENTHERM_EVER_CONNECTED = "opentherm_ever_connected" +CONF_OPENTHERM_HOT_WATER_CAPABLE = "opentherm_hot_water_capable" CONF_OPENTHERM_SENSORS = "opentherm_sensors" +CONF_OPENTHERM_HOT_WATER_CONTROL = "opentherm_hot_water_control" +CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP = "opentherm_hot_water_manual_temperature" +CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID = ( + "opentherm_hot_water_sensor_entity_id" +) +CONF_OPENTHERM_HOT_WATER_PRESETS = "opentherm_hot_water_presets" +CONF_EQUIPMENT_SENSORS = "equipment_sensors" +OPENTHERM_HOT_WATER_SCHEDULE = "opentherm_hot_water_schedule" + +DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP = 45.0 +DEFAULT_OPENTHERM_HOT_WATER_PRESET_TEMP = 55.0 +MAX_OPENTHERM_HOT_WATER_PRESETS = 10 +OPENTHERM_HOT_WATER_DEFAULT_BOOSTS = (15, 30, 45) +CONF_SCHEDULE_SETPOINT_ROOMS = "schedule_setpoint_rooms" # Custom Attributes ATTR_OPENTHERM_ENDPOINT = "endpoint" diff --git a/custom_components/wiser/coordinator.py b/custom_components/wiser/coordinator.py index 9905137b..32d02422 100644 --- a/custom_components/wiser/coordinator.py +++ b/custom_components/wiser/coordinator.py @@ -38,6 +38,7 @@ CONF_HEATING_BOOST_TEMP, CONF_HEATING_BOOST_TIME, CONF_HW_BOOST_TIME, + CONF_LEGACY_NAMING, CONF_RESTORE_MANUAL_TEMP_OPTION, CONF_SETPOINT_MODE, CUSTOM_DATA_STORE, @@ -107,12 +108,13 @@ def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None: ) self.hub_version = 0 - self.hub_device_id: str | None = None self.last_update_time = datetime.now() self.last_update_status = "" self.minimum_temp = TEMP_MINIMUM self.maximum_temp = TEMP_MAXIMUM + self.legacy_naming = config_entry.options.get(CONF_LEGACY_NAMING, True) + # Main option params self.boost_temp = config_entry.options.get( CONF_HEATING_BOOST_TEMP, DEFAULT_BOOST_TEMP diff --git a/custom_components/wiser/cover.py b/custom_components/wiser/cover.py index 6727c989..def76882 100644 --- a/custom_components/wiser/cover.py +++ b/custom_components/wiser/cover.py @@ -25,7 +25,7 @@ get_device_name, get_hub_via_device_info, get_identifier, - get_uuid_unique_id, + get_physical_entity_unique_id, hub_error_handler, ) from .schedules import WiserScheduleEntity @@ -81,7 +81,7 @@ def __init__(self, coordinator, shutter_id) -> None: async def async_force_update(self, delay: int = 0): _LOGGER.debug(f"Hub update initiated by {self.name}") if delay: - asyncio.sleep(delay) + await asyncio.sleep(delay) await self._data.async_refresh() @callback @@ -147,13 +147,10 @@ def is_closing(self): @property def unique_id(self): - """Return unique Id.""" - legacy_name = f"{get_device_name(self._data, self._device_id)} Control" - legacy_unique_id = ( - f"{self._data.wiserhub.system.name}-Wisershutter-" - f"{self._device_id}-{legacy_name}" + """Return a name-independent unique ID.""" + return get_physical_entity_unique_id( + self._data, "cover", self._device_id, "control" ) - return get_uuid_unique_id(legacy_unique_id) @property def extra_state_attributes(self): diff --git a/custom_components/wiser/device.py b/custom_components/wiser/device.py index 778a716b..e688083d 100644 --- a/custom_components/wiser/device.py +++ b/custom_components/wiser/device.py @@ -1,12 +1,105 @@ """Device registry helpers for the Wiser integration.""" +def assign_device_area_if_unset( + device_registry, area_registry, device, suggested_area +): + """Assign a device to its Wiser room without overriding a user area.""" + if device is None or device.area_id is not None or not suggested_area: + return device + + area = area_registry.async_get_or_create(suggested_area) + return device_registry.async_update_device(device.id, area_id=area.id) + + +def move_devices_from_managed_area( + device_registry, + area_registry, + devices, + previous_area_name, + new_area_name, +): + """Move Wiser devices that remain in their previous managed area.""" + if not previous_area_name or previous_area_name == new_area_name: + return 0 + + previous_area = area_registry.async_get_area_by_name(previous_area_name) + if previous_area is None: + return 0 + + devices_to_move = [ + device + for device in {device.id: device for device in devices if device}.values() + if device.area_id == previous_area.id + ] + if not devices_to_move: + return 0 + + new_area = area_registry.async_get_or_create(new_area_name) + for device in devices_to_move: + device_registry.async_update_device(device.id, area_id=new_area.id) + return len(devices_to_move) + + +def known_wiser_room_area_name(area_registry, area_id, room_names): + """Return an area's name only when it matches a current Wiser room.""" + if not area_id: + return None + + area = area_registry.async_get_area(area_id) + if area is None or area.name not in set(room_names): + return None + return area.name + + +def confirmed_deleted_room_ids( + previous_room_ids, + current_room_ids, + missing_counts, + required_observations=2, + required_empty_observations=3, +): + """Return rooms absent from consecutive successful hub responses.""" + missing_room_ids = set(previous_room_ids) - set(current_room_ids) + for room_id in list(missing_counts): + if room_id not in missing_room_ids: + missing_counts.pop(room_id) + for room_id in missing_room_ids: + missing_counts[room_id] = missing_counts.get(room_id, 0) + 1 + + required_count = ( + required_empty_observations + if not current_room_ids + else required_observations + ) + return { + room_id + for room_id in missing_room_ids + if missing_counts[room_id] >= required_count + } + + +def room_names_with_pending_deletions( + previous_room_names, + current_room_names, + confirmed_deleted_ids, +): + """Retain missing room names until their deletion is confirmed.""" + retained = { + room_id: name + for room_id, name in previous_room_names.items() + if room_id not in confirmed_deleted_ids + } + retained.update(current_room_names) + return retained + + def register_room_assigned_device( device_registry, config_entry_id, identifier, - parent_device_id, suggested_area, + parent_device_info, **device_info, ): """Register a physical device in its Wiser room on first creation.""" @@ -14,11 +107,213 @@ def register_room_assigned_device( config_entry_id=config_entry_id, identifiers={identifier}, suggested_area=suggested_area, - via_device_id=parent_device_id, + **parent_device_info, **device_info, ) +def find_physical_device_candidates( + registry_devices, + registry_entities, + identifiers, + stable_entity_ids, +): + """Find physical devices by identifier or immutable entity identity.""" + entity_device_ids = { + entry.device_id + for entry in registry_entities + if entry.platform == "wiser" + and entry.unique_id in stable_entity_ids + and entry.device_id is not None + } + return [ + entry + for entry in registry_devices + if identifiers.intersection(entry.identifiers) + or entry.id in entity_device_ids + ] + + +def migrate_physical_device( + device_registry, + entity_registry, + config_entry_id, + identifier, + candidates, + name, + creation_parent_info=None, + via_device_id=None, + **device_info, +): + """Give a physical device a stable ID and merge duplicate registry rows.""" + get_by_identifier = getattr( + device_registry, "async_get_device_by_identifier", None + ) + stable_device = ( + get_by_identifier(identifier, config_entry_id) + if get_by_identifier + else device_registry.async_get_device(identifiers={identifier}) + ) + + candidates = list({device.id: device for device in candidates}.values()) + if stable_device is not None and all( + device.id != stable_device.id for device in candidates + ): + candidates.append(stable_device) + if candidates: + canonical = min( + candidates, + key=lambda device: ( + getattr(device, "created_at", None) is None, + getattr(device, "created_at", None), + device.id, + ), + ) + else: + canonical = None + + if canonical is None: + return device_registry.async_get_or_create( + config_entry_id=config_entry_id, + identifiers={identifier}, + name=name, + **(creation_parent_info or {}), + **device_info, + ) + + for duplicate in candidates: + if duplicate.id == canonical.id: + continue + for entity in list(entity_registry.entities.values()): + if entity.device_id == duplicate.id: + entity_registry.async_update_entity( + entity.entity_id, device_id=canonical.id + ) + device_registry.async_remove_device(duplicate.id) + preserved_identifiers = { + candidate_identifier + for candidate in candidates + for candidate_identifier in candidate.identifiers + if candidate_identifier[0] != identifier[0] + } + preserved_connections = { + connection + for candidate in candidates + for connection in getattr(candidate, "connections", set()) + } + updates = { + "new_identifiers": preserved_identifiers | {identifier}, + "name": name, + **device_info, + } + if via_device_id is not None: + updates["via_device_id"] = via_device_id + if preserved_connections: + updates["new_connections"] = preserved_connections + return device_registry.async_update_device(canonical.id, **updates) + + +def migrate_entity_unique_id_duplicates( + entity_registry, + entries, + new_unique_id, +): + """Migrate one entity while retaining historical duplicates.""" + entries = list({entry.entity_id: entry for entry in entries}.values()) + if not entries: + return None + + existing_entity_id = entity_registry.async_get_entity_id( + entries[0].domain, entries[0].platform, new_unique_id + ) + existing = ( + entity_registry.async_get(existing_entity_id) + if existing_entity_id is not None + else None + ) + if existing is not None and all( + entry.entity_id != existing.entity_id for entry in entries + ): + entries.append(existing) + canonical = existing or min( + entries, + key=lambda entry: ( + getattr(entry, "created_at", None) is None, + getattr(entry, "created_at", None), + entry.entity_id, + ), + ) + if canonical.unique_id != new_unique_id: + entity_registry.async_update_entity( + canonical.entity_id, new_unique_id=new_unique_id + ) + return canonical + + +def migrate_room_entities( + entity_registry, + room_device_id, + unique_id_for_type, +): + """Migrate room-name-derived entities and remove their duplicates.""" + groups = {} + for entry in list(entity_registry.entities.values()): + if entry.device_id != room_device_id or entry.platform != "wiser": + continue + + entity_type = None + translation_key = getattr(entry, "translation_key", None) + unique_id = str(getattr(entry, "unique_id", "")).casefold() + if entry.domain == "climate": + entity_type = "climate" + elif entry.domain == "binary_sensor": + if ( + translation_key == "window_detection_active" + or translation_key is None + ): + entity_type = "binary_sensor_window_detection_active" + elif entry.domain == "switch": + if translation_key: + entity_type = f"switch_{translation_key}" + elif "-passive-mode-switch-" in unique_id: + entity_type = "switch_passive_mode" + elif "-room-switch-" in unique_id: + if unique_id.endswith(" window detection"): + entity_type = "switch_window_detection" + elif unique_id.endswith(" include in summer comfort"): + entity_type = "switch_include_in_summer_comfort" + elif entry.domain == "sensor": + if translation_key == "heating_demand": + entity_type = "heating_demand" + elif translation_key == "target_temperature": + entity_type = "current_target_temp" + elif "-sensor-current_target_temp-" in unique_id: + entity_type = "current_target_temp" + elif "-sensor-current_temp-" in unique_id: + entity_type = "current_temp" + elif "-sensor-room-" in unique_id: + entity_type = "heating_demand" + elif ( + translation_key is None + and "temperature" + in { + str(getattr(entry, "device_class", None)), + str(getattr(entry, "original_device_class", None)), + } + ): + entity_type = "current_temp" + + if entity_type is not None: + groups.setdefault(entity_type, []).append(entry) + + for entity_type, entries in groups.items(): + migrate_entity_unique_id_duplicates( + entity_registry, + entries, + unique_id_for_type(entity_type), + ) + + def register_hub_device( device_registry, config_entry_id, @@ -107,8 +402,9 @@ def migrate_room_device( identifier, legacy_identifier, name, + creation_parent_info=None, ): - """Migrate a name-derived Wiser room device to its stable identifier.""" + """Migrate or create a Wiser room device with its stable identifier.""" get_by_identifier = getattr( device_registry, "async_get_device_by_identifier", None ) @@ -122,9 +418,14 @@ def migrate_room_device( ) if room_device is not None: - device_registry.async_update_device( - room_device.id, new_identifiers={identifier}, name=name - ) + updates = {"new_identifiers": {identifier}, "name": name} + if ( + room_device.area_id is None + and legacy_device is not None + and legacy_device.area_id is not None + ): + updates["area_id"] = legacy_device.area_id + room_device = device_registry.async_update_device(room_device.id, **updates) if legacy_device is not None and legacy_device.id != room_device.id: for entity in list(entity_registry.entities.values()): if entity.device_id == legacy_device.id: @@ -141,4 +442,40 @@ def migrate_room_device( name=name, ) - return None + return device_registry.async_get_or_create( + config_entry_id=config_entry_id, + identifiers={identifier}, + name=name, + **(creation_parent_info or {}), + ) + + +def remove_room_devices( + device_registry, + entity_registry, + config_entry_id, + identifiers, +): + """Remove deleted logical room devices and their registry entities.""" + get_by_identifier = getattr( + device_registry, "async_get_device_by_identifier", None + ) + removed_device_ids = set() + + for identifier in identifiers: + device = ( + get_by_identifier(identifier, config_entry_id) + if get_by_identifier + else device_registry.async_get_device(identifiers={identifier}) + ) + if device is None or device.id in removed_device_ids: + continue + + for entity in list(entity_registry.entities.values()): + if entity.device_id == device.id: + entity_registry.async_remove(entity.entity_id) + + device_registry.async_remove_device(device.id) + removed_device_ids.add(device.id) + + return len(removed_device_ids) diff --git a/custom_components/wiser/entity.py b/custom_components/wiser/entity.py index 374a06a9..35fd487d 100644 --- a/custom_components/wiser/entity.py +++ b/custom_components/wiser/entity.py @@ -14,6 +14,13 @@ def suggested_object_id(self): hub_identifier = (DOMAIN, self._data.wiserhub.system.name) if device_info and hub_identifier in device_info.get("identifiers", set()): - return get_hub_entity_object_id(self._data, self.name) + # Home Assistant builds default entity IDs from area + effective + # device name + suggested object ID. Use the registry name here so + # a user override is handled differently from our default hub name. + device_entry = getattr(self, "device_entry", None) + device_name = None + if device_entry is not None: + device_name = device_entry.name_by_user or device_entry.name + return get_hub_entity_object_id(self._data, self.name, device_name) return super().suggested_object_id diff --git a/custom_components/wiser/frontend/__init__.py b/custom_components/wiser/frontend/__init__.py index 79ac53f3..79f5e7da 100644 --- a/custom_components/wiser/frontend/__init__.py +++ b/custom_components/wiser/frontend/__init__.py @@ -1,20 +1,55 @@ -"""View Assist Javascript module registration.""" +"""Register Wiser frontend resources and independently updated card bundles.""" +import asyncio +from hashlib import sha256 import logging import os from pathlib import Path +import re +from urllib.parse import parse_qs, urlsplit +from homeassistant.components.frontend import add_extra_js_url from homeassistant.components.http import StaticPathConfig from homeassistant.components.lovelace import MODE_STORAGE, LovelaceData from homeassistant.const import MAJOR_VERSION, MINOR_VERSION from homeassistant.core import HomeAssistant from homeassistant.helpers.event import async_call_later -from ..const import JSMODULES, URL_BASE # noqa: TID252 +from .card_files import CARD_CACHE, CARD_CACHE_URL, resolve_card +from .registry import async_load_registry, get_manifest + +from ..const import URL_BASE # noqa: TID252 _LOGGER = logging.getLogger(__name__) +def card_version(path: Path) -> str: + """Read the standard version marker or hash an unversioned static asset.""" + version_pattern = r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?" + try: + contents = path.read_bytes() + except OSError: + return "missing" + source = contents.decode("utf-8", errors="replace") + # Every registry-provided bundle uses the same build metadata contract. + marker = re.search( + rf"/\*!\s*WISER-CARD-VERSION {re.escape(path.stem)}\s+({version_pattern})\s*\*/", + source, + ) + if marker: + return marker[1] + # Still refresh caches for unfamiliar builds rather than advertise a stale + # version from const.py. This is a content identifier, not a release number. + return f"sha256-{sha256(contents).hexdigest()[:16]}" + + +async def async_card_resource(hass, filename): + """Resolve the active card path, cache-busted URL, and version off the loop.""" + return await hass.async_add_executor_job( + resolve_card, hass.config.path(), filename, card_version + ) + + class JSModuleRegistration: """Register Javascript modules.""" @@ -22,17 +57,31 @@ def __init__(self, hass: HomeAssistant) -> None: """Initialise.""" self.hass = hass self.lovelace: LovelaceData = self.hass.data.get("lovelace") + if self.lovelace is None: + self.resource_mode = None + return - # Fix for change to name of mode to reousrce_mode in 2026.2 - if MAJOR_VERSION >= 2026 and MINOR_VERSION >= 2: + # Fix for change to name of mode to resource_mode in 2026.2 + if (MAJOR_VERSION, MINOR_VERSION) >= (2026, 2): self.resource_mode = self.lovelace.resource_mode else: self.resource_mode = self.lovelace.mode async def async_register(self): - """Register view_assist path.""" + """Register Wiser static paths, icons, and card resources.""" + await async_load_registry(self.hass) await self._async_register_path() - + icon_version = await self.hass.async_add_executor_job( + card_version, Path(__file__).parent / "wiser-icons.js" + ) + add_extra_js_url(self.hass, f"{URL_BASE}/wiser-icons.js?v={icon_version}") + hot_water_version = await self.hass.async_add_executor_job( + card_version, Path(__file__).parent / "wiser-hot-water-control.js" + ) + add_extra_js_url( + self.hass, + f"{URL_BASE}/wiser-hot-water-control.js?v={hot_water_version}", + ) if self.lovelace and self.resource_mode == MODE_STORAGE: await self._async_wait_for_lovelace_resources() @@ -40,13 +89,24 @@ async def async_register(self): # install card resources async def _async_register_path(self): """Register resource path if not already registered.""" + cache = Path(self.hass.config.path(CARD_CACHE)) + await self.hass.async_add_executor_job(lambda: cache.mkdir(parents=True, exist_ok=True)) + if not self.hass.data.get("wiser_card_cache_registered"): + try: + await self.hass.http.async_register_static_paths( + [StaticPathConfig(CARD_CACHE_URL, cache, False)] + ) + except RuntimeError: + # Another hub may have registered the shared path during setup. + _LOGGER.debug("Card cache path already registered") + self.hass.data["wiser_card_cache_registered"] = True try: await self.hass.http.async_register_static_paths( [StaticPathConfig(URL_BASE, Path(__file__).parent, False)] ) _LOGGER.debug("Registered resource path from %s", Path(__file__).parent) except RuntimeError: - # Runtime error is likley this is already registered. + # Runtime error is likely this is already registered. _LOGGER.debug("Resource path already registered") async def _async_wait_for_lovelace_resources(self) -> None: @@ -65,6 +125,12 @@ async def _check_lovelace_resources_loaded(now): async def _async_register_modules(self): """Register modules if not already registered.""" + lock = self.hass.data.setdefault("wiser_card_resource_lock", asyncio.Lock()) + async with lock: + await self._async_register_modules_locked() + + async def _async_register_modules_locked(self): + """Register modules while holding the shared resource lock.""" _LOGGER.debug("Installing javascript modules") # Get resources already registered @@ -74,67 +140,104 @@ async def _async_register_modules(self): if resource["url"].startswith(URL_BASE) ] - for module in JSMODULES: + for module in get_manifest(self.hass): + if not module.get("card", True): + continue url = f"{URL_BASE}/{module.get('filename')}" + filenames = [module["filename"], *module.get("legacy_filenames", [])] - card_registered = False - - for resource in resources: - if self._get_resource_path(resource["url"]) == url: - card_registered = True - # check version - if self._get_resource_version(resource["url"]) != module.get( - "version" - ): - # Update card version - _LOGGER.debug( - "Updating %s to version %s", - module.get("name"), - module.get("version"), - ) - await self.lovelace.resources.async_update_item( - resource.get("id"), - { - "res_type": "module", - "url": url + "?v=" + module.get("version"), - }, - ) - # Remove old gzipped files - await self.async_remove_gzip_files() - else: - _LOGGER.debug( - "%s already registered as version %s", - module.get("name"), - module.get("version"), + _, active_url, version = await async_card_resource(self.hass, module["filename"]) + if version == "missing": + # Newly discovered cards become resources only after installation. + continue + + matching_resources = [ + resource + for resource in resources + if ( + self._get_resource_path(resource["url"]) + in {f"{URL_BASE}/{filename}" for filename in filenames} + or any( + self._get_resource_path(resource["url"]).startswith( + f"{CARD_CACHE_URL}/{Path(filename).stem}-" ) + for filename in filenames + ) + ) + ] + + if matching_resources: + # Prefer the most recently listed active resource, otherwise + # migrate the most recently listed legacy resource in place. + active_resources = [ + resource + for resource in matching_resources + if resource["url"] == active_url + ] + retained = (active_resources or matching_resources)[-1] + + if retained["url"] != active_url: + _LOGGER.debug( + "Updating %s to version %s", + module.get("name"), + version, + ) + await self.lovelace.resources.async_update_item( + retained.get("id"), + {"res_type": "module", "url": active_url}, + ) + await self.async_remove_gzip_files() + else: + _LOGGER.debug( + "%s already registered as version %s", + module.get("name"), + version, + ) - if not card_registered: + for duplicate in matching_resources: + if duplicate is retained: + continue + _LOGGER.debug( + "Removing duplicate %s resource %s", + module.get("name"), + duplicate.get("id"), + ) + await self.lovelace.resources.async_delete_item( + duplicate.get("id") + ) + else: _LOGGER.debug( "Registering %s as version %s", module.get("name"), - module.get("version"), + version, ) await self.lovelace.resources.async_create_item( - {"res_type": "module", "url": url + "?v=" + module.get("version")} + {"res_type": "module", "url": active_url} ) def _get_resource_path(self, url: str): return url.split("?")[0] def _get_resource_version(self, url: str): - if version := url.split("?")[1].replace("v=", ""): - return version - return 0 + return parse_qs(urlsplit(url).query).get("v", [None])[0] async def async_unregister(self): """Unload lovelace module resource.""" if self.resource_mode == MODE_STORAGE: - for module in JSMODULES: - url = f"{URL_BASE}/{module.get('filename')}" + for module in get_manifest(self.hass): + if not module.get("card", True): + continue + filenames = [module["filename"], *module.get("legacy_filenames", [])] wiser_resources = [ resource for resource in self.lovelace.resources.async_items() - if str(resource["url"]).startswith(url) + if any( + str(resource["url"]).startswith(f"{URL_BASE}/{filename}") + or str(resource["url"]).startswith( + f"{CARD_CACHE_URL}/{Path(filename).stem}-" + ) + for filename in filenames + ) ] for resource in wiser_resources: await self.lovelace.resources.async_delete_item(resource.get("id")) diff --git a/custom_components/wiser/frontend/card_files.py b/custom_components/wiser/frontend/card_files.py new file mode 100644 index 00000000..8640332c --- /dev/null +++ b/custom_components/wiser/frontend/card_files.py @@ -0,0 +1,93 @@ +"""Persistent, verified card overrides outside the integration installation.""" + +from hashlib import sha256 +import json +import logging +from pathlib import Path +import re +import tempfile + +from awesomeversion import AwesomeVersion +from awesomeversion.exceptions import AwesomeVersionException + +from ..const import URL_BASE + +_LOGGER = logging.getLogger(__name__) + +CARD_CACHE = ".storage/wiser_cards" +CARD_CACHE_URL = f"{URL_BASE}/cards" + + +def newer_version(candidate, installed): + """Compare semantic release versions without offering downgrades.""" + try: + return AwesomeVersion(candidate) > AwesomeVersion(installed) + except (AwesomeVersionException, ValueError, TypeError): + return False + + +def resolve_card(config_dir, filename, version_reader): + """Prefer a verified downloaded card only while newer than the bundled card.""" + bundled = Path(__file__).parent / filename + version = version_reader(bundled) + fallback = (bundled, f"{URL_BASE}/{filename}?v={version}", version) + directory = Path(config_dir) / CARD_CACHE + try: + record = json.loads((directory / f"{filename}.json").read_text()) + cached = directory / record["file"] + # Metadata must not redirect static serving outside the card cache. + digest = record["digest"] + expected = f"{Path(filename).stem}-{digest}.js" + if ( + record["file"] != expected + or len(digest) != 64 + or any(c not in "0123456789abcdef" for c in digest) + ): + return fallback + if version != "missing" and not newer_version(record["version"], version): + return fallback + if sha256(cached.read_bytes()).hexdigest() != digest: + return fallback + return cached, f"{CARD_CACHE_URL}/{cached.name}?v={record['version']}", record["version"] + except (OSError, ValueError, KeyError, TypeError): + return fallback + + +def store_card(config_dir, filename, version, contents): + """Write immutable assets, then atomically switch the active metadata.""" + directory = Path(config_dir) / CARD_CACHE + directory.mkdir(parents=True, exist_ok=True) + digest = sha256(contents).hexdigest() + asset_name = f"{Path(filename).stem}-{digest}.js" + _atomic_write(directory / asset_name, contents) + metadata = {"file": asset_name, "version": version, "digest": digest} + _atomic_write(directory / f"{filename}.json", json.dumps(metadata).encode()) + + +def prune_card_cache(config_dir, filename, keep_paths): + """Retain current and previous assets after a successful frontend refresh.""" + directory = Path(config_dir) / CARD_CACHE + pattern = re.compile(rf"{re.escape(Path(filename).stem)}-[0-9a-f]{{64}}\.js") + try: + for path in directory.iterdir(): + if pattern.fullmatch(path.name) and path not in keep_paths: + path.unlink(missing_ok=True) + except OSError as err: + # Cache housekeeping must not turn a successful installation into a failure. + _LOGGER.warning("Unable to clean up %s card cache: %s", filename, err) + + +def _atomic_write(path, contents): + """Keep the previous working file intact if a write fails.""" + with tempfile.NamedTemporaryFile(dir=path.parent, delete=False) as stream: + temporary = Path(stream.name) + try: + stream.write(contents) + stream.flush() + except BaseException: + temporary.unlink(missing_ok=True) + raise + try: + temporary.replace(path) + finally: + temporary.unlink(missing_ok=True) diff --git a/custom_components/wiser/frontend/cards.json b/custom_components/wiser/frontend/cards.json new file mode 100644 index 00000000..4a701dca --- /dev/null +++ b/custom_components/wiser/frontend/cards.json @@ -0,0 +1,41 @@ +{ + "schema_version": 1, + "cards": [ + { + "id": "schedule", + "name": "Wiser Schedule Card", + "filename": "wiser-schedule-card.js", + "repository": "andyblac/wiser-schedule-card", + "component": "wiser-schedule-card", + "panel": "wiser-schedules-panel" + }, + { + "id": "zigbee", + "name": "Wiser Zigbee Card", + "filename": "wiser-zigbee-card.js", + "repository": "andyblac/wiser-zigbee-card", + "component": "wiser-zigbee-card", + "panel": "wiser-zigbee-panel" + }, + { + "id": "rooms", + "name": "Wiser Controls Card", + "filename": "wiser-controls-card.js", + "repository": "andyblac/wiser-controls-card", + "component": "wiser-controls-card", + "panel": "wiser-controls-panel", + "legacy_filenames": [ + "wiser-rooms-card.js" + ] + }, + { + "id": "hub", + "name": "Wiser Hub Panel", + "filename": "wiser-hub-panel.js", + "repository": "andyblac/wiser-hub-panel", + "component": "wiser-hub-panel", + "panel": "wiser-hub-panel", + "card": false + } + ] +} diff --git a/custom_components/wiser/frontend/entry_updates.py b/custom_components/wiser/frontend/entry_updates.py new file mode 100644 index 00000000..7a6b1220 --- /dev/null +++ b/custom_components/wiser/frontend/entry_updates.py @@ -0,0 +1,30 @@ +"""Apply panel preferences live and reload other integration settings.""" + +from .wiser_sidebar import async_update_wiser_panel +from ..const import ( + CONF_SHOW_WISER_SIDEBAR, + CONF_WISER_PANEL_CONFIG, + DOMAIN, +) + + +def integration_reload_settings(entry): + """Capture connection and entity options, excluding panel-only preferences.""" + return ( + dict(entry.data), + { + key: value + for key, value in entry.options.items() + if key not in {CONF_SHOW_WISER_SIDEBAR, CONF_WISER_PANEL_CONFIG} + }, + ) + + +async def async_handle_entry_update(hass, entry): + """Apply panel preferences live; retain reloads for other integration changes.""" + loaded = hass.data.get(DOMAIN, {}).get(entry.entry_id) + settings = integration_reload_settings(entry) + if loaded is not None and loaded.get("reload_settings") == settings: + await async_update_wiser_panel(hass) + return + await hass.config_entries.async_reload(entry.entry_id) diff --git a/custom_components/wiser/frontend/manifest.py b/custom_components/wiser/frontend/manifest.py new file mode 100644 index 00000000..41112a18 --- /dev/null +++ b/custom_components/wiser/frontend/manifest.py @@ -0,0 +1,69 @@ +"""Validate frontend definitions consistently during packaging and runtime.""" + +import re + + +REGISTRY_SCHEMA_VERSION = 1 + + +def validate_registry(value): + """Validate the versioned registry while accepting legacy card arrays.""" + if isinstance(value, list): + return validate_manifest(value) + if not isinstance(value, dict): + raise ValueError("Invalid frontend registry document") + if value.get("schema_version") != REGISTRY_SCHEMA_VERSION: + raise ValueError("Unsupported frontend registry schema version") + return validate_manifest(value.get("cards")) + + +def validate_manifest(value): + """Validate names and paths before using a remotely supplied registry.""" + if not isinstance(value, list) or not 1 <= len(value) <= 100: + raise ValueError("Frontend registry must contain between 1 and 100 cards") + seen = {key: set() for key in ("id", "filename", "component", "panel")} + patterns = { + "id": r"[a-z0-9-]+", + "filename": r"wiser-[a-z0-9-]+-(?:card|panel)\.js", + "repository": r"[A-Za-z0-9_-]+/[A-Za-z0-9_.-]+", + "component": r"wiser-[a-z0-9-]+", + "panel": r"wiser-[a-z0-9-]+-panel", + } + result = [] + for card in value: + if not isinstance(card, dict): + raise ValueError("Invalid frontend card definition") + name = card.get("name") + if not isinstance(name, str) or not name.strip() or len(name) > 100: + raise ValueError("Invalid frontend card name") + for key, pattern in patterns.items(): + item = card.get(key) + if key == "panel" and item is None: + continue + if not isinstance(item, str) or not re.fullmatch(pattern, item): + raise ValueError(f"Invalid frontend card {key}") + if key in seen: + if item in seen[key]: + raise ValueError(f"Duplicate frontend card {key}") + seen[key].add(item) + legacy_filenames = card.get("legacy_filenames", []) + if not isinstance(legacy_filenames, list): + raise ValueError("Invalid frontend card legacy filenames") + for filename in legacy_filenames: + if ( + not isinstance(filename, str) + or not re.fullmatch(patterns["filename"], filename) + or filename in seen["filename"] + ): + raise ValueError("Invalid or duplicate frontend legacy filename") + seen["filename"].add(filename) + definition = {key: card.get(key) for key in ("name", *patterns)} + is_card = card.get("card", True) + if type(is_card) is not bool: + raise ValueError("Invalid frontend card flag") + if not is_card: + definition["card"] = False + if legacy_filenames: + definition["legacy_filenames"] = legacy_filenames + result.append(definition) + return result diff --git a/custom_components/wiser/frontend/registry.py b/custom_components/wiser/frontend/registry.py new file mode 100644 index 00000000..02dc9a09 --- /dev/null +++ b/custom_components/wiser/frontend/registry.py @@ -0,0 +1,33 @@ +"""Load frontend definitions maintained inside the integration.""" + +import json +import logging +from pathlib import Path + +from ..const import CARD_MANIFEST +from .manifest import validate_registry + +_LOGGER = logging.getLogger(__name__) +REGISTRY_DATA = "wiser_frontend_registry" +REGISTRY_PATH = Path(__file__).parent / "cards.json" + + +def get_manifest(hass): + """Return the last valid local frontend registry.""" + return hass.data.get(REGISTRY_DATA, CARD_MANIFEST) + + +def _load_registry(): + """Read and validate the local registry.""" + return validate_registry(json.loads(REGISTRY_PATH.read_text("utf-8"))) + + +async def async_load_registry(hass): + """Reload local definitions so a manual update check sees file edits.""" + try: + manifest = await hass.async_add_executor_job(_load_registry) + except (OSError, ValueError, TypeError, KeyError) as error: + _LOGGER.warning("Unable to load Wiser frontend registry: %s", error) + return get_manifest(hass) + hass.data[REGISTRY_DATA] = manifest + return manifest diff --git a/custom_components/wiser/frontend/wiser-hot-water-control.js b/custom_components/wiser/frontend/wiser-hot-water-control.js new file mode 100644 index 00000000..e2d7d046 --- /dev/null +++ b/custom_components/wiser/frontend/wiser-hot-water-control.js @@ -0,0 +1,1990 @@ +const FEATURE_TYPE = "opentherm-hot-water-control-modes-feature"; +const PRESETS_FEATURE_TYPE = + "opentherm-hot-water-control-presets-feature"; +const MORE_INFO_TYPE = "opentherm-hot-water-control-more-info"; +const NATIVE_TEMPERATURE_CONTROL = + "ha-state-control-water_heater-temperature"; +const DEFAULT_MAX_WIDTH = 560; +const PRESETS_ATTRIBUTE = "opentherm_presets"; +const PRESETS_SELECTOR_KEY = "opentherm_presets_v2"; +const PRESETS_SELECTOR_TYPE = `ha-selector-${PRESETS_SELECTOR_KEY}`; +const PRESETS_DIALOG_TYPE = "opentherm-presets-dialog-v2"; +const MAX_PRESETS = 10; +const MDI_ARROW_LEFT = + "M20,11V13H8L13.5,18.5L12.08,19.92L4.16,12L12.08,4.08L13.5,5.5L8,11H20Z"; + +const localize = (hass, key, fallback, replacements) => + hass?.localize?.(key, replacements) || fallback; + +const showTemperatureError = (element, error) => { + element.dispatchEvent(new CustomEvent("hass-notification", { + bubbles: true, + composed: true, + detail: { message: error?.message || "Unable to change hot-water temperature.", duration: 10000 }, + })); +}; + +const clonePresets = (presets) => + Array.isArray(presets) + ? presets.map((preset) => ({ ...preset })) + : []; + +const presetIcon = (presets, index) => { + const preset = presets[index]; + const configuredIcon = String(preset?.icon || "").trim(); + if (configuredIcon) { + return configuredIcon; + } + return `mdi:numeric-${Math.max(1, index + 1)}`; +}; + +class OpenThermTemperatureSourceSelector extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this._formChanged = (event) => { + if (event.target !== this._form) return; + const device = event.detail?.value?.device_id; + if (device === this._device) return; + this._device = device; + this._value = { mode: this._isWiser() ? "wiser" : "custom", entity_id: this._customEntity }; + this._update(); + // Let the outer form finish applying its device change before our update. + queueMicrotask(() => { if (this.isConnected) this._emit(); }); + }; + } + + set hass(value) { this._hass = value; this._update(); } + set selector(value) { + this._selector = value?.opentherm_temperature_source || {}; + if (this._device === undefined) this._device = this._selector.device_id; + this._update(); + } + set value(value) { + this._value = value; + if (value?.mode === "custom") this._customEntity = value.entity_id; + this._update(); + } + get value() { return this._value; } + set label(value) { this._label = value; this._update(); } + set disabled(value) { this._disabled = value; this._update(); } + set required(value) { this._required = value; } + + connectedCallback() { + if (!this._toggle) { + const style = document.createElement("style"); + style.textContent = ` + :host { display: block; } + .row { display: flex; align-items: center; gap: 16px; } + .label { flex: 1; color: var(--primary-text-color); font-size: var(--ha-font-size-m, 14px); } + ha-button-toggle-group { flex: none; margin-inline-start: auto; } + ha-selector { display: block; margin-top: 12px; } + [hidden] { display: none !important; } + `; + const row = document.createElement("div"); + row.className = "row"; + this._labelElement = document.createElement("div"); + this._labelElement.className = "label"; + this._toggle = document.createElement("ha-button-toggle-group"); + this._toggle.setAttribute("no-wrap", ""); + this._toggle.addEventListener("value-changed", (event) => { + event.stopPropagation(); + if (this._disabled) return; + this._value = { mode: event.detail.value, entity_id: this._customEntity }; + this._update(); + this._emit(); + }); + this._picker = document.createElement("ha-selector"); + this._picker.selector = { entity: { domain: "sensor", device_class: "temperature" } }; + this._picker.addEventListener("value-changed", (event) => { + event.stopPropagation(); + if (this._disabled) return; + this._customEntity = event.detail.value; + this._value = { mode: "custom", entity_id: this._customEntity }; + this._emit(); + }); + row.append(this._labelElement, this._toggle); + this.shadowRoot.append(style, row, this._picker); + } + // ha-form owns the control-device field in a different shadow tree. + let parent = this.parentNode; + while (parent) { + if (parent.localName === "ha-form") { this._form = parent; break; } + parent = parent.parentNode || parent.host; + } + this._form?.addEventListener("value-changed", this._formChanged); + this._update(); + } + + disconnectedCallback() { + this._form?.removeEventListener("value-changed", this._formChanged); + this._form = undefined; + } + + _isWiser() { return this._selector?.wiser_devices?.includes(this._device) || false; } + _emit() { + this.dispatchEvent(new CustomEvent("value-changed", { + bubbles: true, composed: true, detail: { value: this._value }, + })); + } + reportValidity() { + return this._value?.mode === "wiser" || !this._isWiser() || Boolean(this._customEntity); + } + _update() { + if (!this._toggle) return; + this._labelElement.textContent = this._label || "Current temperature"; + this._toggle.buttons = [ + { value: "wiser", label: "Wiser" }, + { value: "custom", label: localize(this._hass, "ui.components.entity.entity-name-picker.mode_custom", "Custom") }, + ]; + this._toggle.active = this._value?.mode; + this._toggle.disabled = Boolean(this._disabled); + this._toggle.hidden = !this._isWiser(); + this._picker.hidden = this._isWiser() && this._value?.mode === "wiser"; + this._picker.hass = this._hass; + this._picker.value = this._customEntity; + this._picker.disabled = Boolean(this._disabled); + this._picker.required = this._isWiser() && !this._picker.hidden; + } +} + +if (!customElements.get("ha-selector-opentherm_temperature_source")) { + customElements.define("ha-selector-opentherm_temperature_source", OpenThermTemperatureSourceSelector); +} + +class OpenThermFailureNotificationsSelector extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this._value = { persistent: false, mobile: false }; + } + + set hass(value) { this._hass = value; this._update(); } + set selector(value) { + this._selector = value?.opentherm_failure_notifications || {}; + this._update(); + } + set value(value) { + this._value = { persistent: false, mobile: false, ...(value || {}) }; + this._update(); + } + get value() { return this._value; } + set label(value) { this._label = value; this._update(); } + set disabled(value) { this._disabled = value; this._update(); } + set required(value) { this._required = value; } + + connectedCallback() { + if (!this._persistentSwitch) { + const style = document.createElement("style"); + style.textContent = ` + :host { display: block; } + .heading { color: var(--primary-text-color); font-size: var(--ha-font-size-m, 14px); margin-bottom: 8px; } + .row { align-items: center; display: flex; min-height: 48px; } + .row-label { color: var(--primary-text-color); flex: 1; } + ha-switch { flex: none; } + ha-selector { display: block; margin: 4px 0 12px; } + [hidden] { display: none !important; } + `; + this._heading = document.createElement("div"); + this._heading.className = "heading"; + const persistentRow = document.createElement("div"); + persistentRow.className = "row"; + const persistentLabel = document.createElement("div"); + persistentLabel.className = "row-label"; + persistentLabel.textContent = "Persistent notification"; + this._persistentSwitch = document.createElement("ha-switch"); + this._persistentSwitch.addEventListener("change", () => { + if (this._disabled) return; + this._value = { ...this._value, persistent: this._persistentSwitch.checked }; + this._emit(); + }); + persistentRow.append(persistentLabel, this._persistentSwitch); + + const mobileRow = document.createElement("div"); + mobileRow.className = "row"; + const mobileLabel = document.createElement("div"); + mobileLabel.className = "row-label"; + mobileLabel.textContent = "Mobile notification"; + this._mobileSwitch = document.createElement("ha-switch"); + this._mobileSwitch.addEventListener("change", () => { + if (this._disabled) return; + this._value = { ...this._value, mobile: this._mobileSwitch.checked }; + this._update(); + this._emit(); + }); + mobileRow.append(mobileLabel, this._mobileSwitch); + + this._picker = document.createElement("ha-selector"); + this._picker.addEventListener("value-changed", (event) => { + event.stopPropagation(); + if (this._disabled) return; + this._value = { ...this._value, mobile_service: event.detail.value }; + this._emit(); + }); + this.shadowRoot.append(style, this._heading, persistentRow, mobileRow, this._picker); + } + this._update(); + } + + _emit() { + this.dispatchEvent(new CustomEvent("value-changed", { + bubbles: true, composed: true, detail: { value: this._value }, + })); + } + + reportValidity() { + return !this._value?.mobile || Boolean(this._value?.mobile_service); + } + + _update() { + if (!this._persistentSwitch) return; + this._heading.textContent = this._label || "Failure notifications"; + this._persistentSwitch.checked = Boolean(this._value?.persistent); + this._mobileSwitch.checked = Boolean(this._value?.mobile); + this._persistentSwitch.disabled = Boolean(this._disabled); + this._mobileSwitch.disabled = Boolean(this._disabled); + this._picker.hidden = !this._value?.mobile; + this._picker.hass = this._hass; + this._picker.selector = { + select: { + mode: "dropdown", + options: this._selector?.mobile_services || [], + }, + }; + this._picker.value = this._value?.mobile_service; + this._picker.label = "Mobile device"; + this._picker.disabled = Boolean(this._disabled); + this._picker.required = Boolean(this._value?.mobile); + } +} + +if (!customElements.get("ha-selector-opentherm_failure_notifications")) { + customElements.define( + "ha-selector-opentherm_failure_notifications", + OpenThermFailureNotificationsSelector + ); +} + +class OpenThermPresetsSelector extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + } + + set hass(hass) { + this._hass = hass; + this._render(); + } + + set selector(selector) { + this._selector = selector?.[PRESETS_SELECTOR_KEY] || selector || {}; + } + + set value(value) { + this._value = clonePresets(value); + this._render(); + } + + get value() { + return this._value; + } + + set disabled(disabled) { + this._disabled = disabled; + this._render(); + } + + set label(label) { + this._label = label; + this._updateRenderedState(); + } + + set required(required) { + this._required = required; + } + + connectedCallback() { + this._ensureRendered(); + this._updateRenderedState(); + } + + reportValidity() { + return true; + } + + _openPresets() { + if (this._disabled) { + return; + } + + const dialogParams = { + label: this._label || "Presets", + presets: clonePresets(this._value), + minimum: Number(this._selector?.minimum ?? 35), + maximum: Number(this._selector?.maximum ?? 55), + saveCallback: (presets) => { + this._value = clonePresets(presets); + this.dispatchEvent( + new CustomEvent("value-changed", { + bubbles: true, + composed: true, + detail: { value: clonePresets(this._value) }, + }) + ); + this._updateRenderedState(); + }, + }; + // The options flow is already hosted inside a Home Assistant dialog. Its + // dialog manager does not consistently handle a second `show-dialog` + // event from a custom selector, so mount this editor at document level. + // Reuse the single editor element between openings to avoid duplicates. + let dialog = document.body.querySelector(PRESETS_DIALOG_TYPE); + if (!dialog) { + dialog = document.createElement(PRESETS_DIALOG_TYPE); + document.body.append(dialog); + } + dialog.hass = this._hass; + dialog.showDialog(dialogParams); + } + + _render() { + if (!this.isConnected) { + return; + } + + this._ensureRendered(); + this._updateRenderedState(); + } + + _ensureRendered() { + if (!this.shadowRoot || this._button) { + return; + } + + const style = document.createElement("style"); + style.textContent = ` + :host { display: block; } + ha-button { width: 100%; } + .summary { + color: var(--secondary-text-color); + font-size: var(--ha-font-size-s, 12px); + margin-top: var(--ha-space-1, 4px); + } + `; + + const button = document.createElement("ha-button"); + button.setAttribute("type", "button"); + button.textContent = "Presets"; + button.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + this._openPresets(); + }); + + const summary = document.createElement("div"); + summary.className = "summary"; + + this._button = button; + this._summary = summary; + this.shadowRoot.replaceChildren(style, button, summary); + } + + _updateRenderedState() { + if (!this._button || !this._summary) { + return; + } + this._button.textContent = this._label || "Presets"; + this._button.disabled = Boolean(this._disabled); + const count = this._value?.length || 0; + this._summary.textContent = `${count} of ${MAX_PRESETS} presets configured`; + } +} + +class OpenThermPresetsDialog extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this._editingIndex = undefined; + } + + set hass(hass) { + this._hass = hass; + } + + showDialog(params) { + this._params = params; + this._minimum = Number.isFinite(params.minimum) ? params.minimum : 35; + this._maximum = Number.isFinite(params.maximum) ? params.maximum : 55; + this._presets = clonePresets(params.presets).slice(0, MAX_PRESETS); + this._editingIndex = undefined; + this._ensureDialog(); + this._dialog.open = true; + this._renderContent(); + } + + closeDialog() { + if (this._dialog) { + this._dialog.open = false; + } + return true; + } + + _ensureDialog() { + if (this._dialog) { + return; + } + + const style = document.createElement("style"); + style.textContent = ` + .preset-list { + display: flex; + flex-direction: column; + gap: var(--ha-space-2, 8px); + width: 100%; + } + .preset-section { + display: flex; + flex-direction: column; + gap: var(--ha-space-2, 8px); + } + .preset-rows { + display: flex; + flex-direction: column; + gap: var(--ha-space-2, 8px); + } + .preset-row { + align-items: center; + background: var(--secondary-background-color); + border-radius: var(--ha-border-radius-md, 12px); + display: grid; + gap: var(--ha-space-3, 12px); + grid-template-columns: auto auto minmax(0, 1fr) auto; + padding: var(--ha-space-3, 12px); + } + .drag-handle { + align-items: center; + color: var(--secondary-text-color); + cursor: grab; + display: flex; + justify-content: center; + touch-action: none; + } + .drag-handle:active { cursor: grabbing; } + .preset-row ha-icon { color: var(--primary-color); } + .preset-name { font-weight: var(--ha-font-weight-medium, 500); } + .preset-detail { + color: var(--secondary-text-color); + font-size: var(--ha-font-size-s, 12px); + margin-top: 2px; + } + .empty { + color: var(--secondary-text-color); + padding: var(--ha-space-6, 24px); + text-align: center; + } + .preset-editor-content { + display: grid; + gap: var(--ha-space-2, 8px); + grid-template-columns: minmax(0, 1fr); + width: 100%; + } + .preset-editor-content > * { + box-sizing: border-box; + min-width: 0; + width: 100%; + } + .dialog-footer { + margin-block-end: var(--ha-space-2, 8px); + margin-inline: var(--ha-space-2, 8px); + } + ha-alert { display: block; margin-bottom: var(--ha-space-3, 12px); } + ha-alert[hidden] { display: none; } + `; + + const dialog = document.createElement("ha-dialog"); + dialog.addEventListener("closed", () => { + if (this._editingIndex !== undefined && this._params) { + this._editingIndex = undefined; + this._dialog.open = false; + this._renderContent(); + window.requestAnimationFrame(() => { + if (this._params) { + this._dialog.open = true; + } + }); + return; + } + this._params = undefined; + }); + this._dialog = dialog; + this.shadowRoot.replaceChildren(style, dialog); + } + + _button(label, handler, { primary = false, danger = false } = {}) { + const button = document.createElement("ha-button"); + button.setAttribute("type", "button"); + button.textContent = label; + if (primary) { + button.setAttribute("variant", "brand"); + } + if (danger) { + button.setAttribute("variant", "danger"); + } + button.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + handler(event); + }); + return button; + } + + _movePreset(oldIndex, newIndex) { + if ( + !Number.isInteger(oldIndex) || + !Number.isInteger(newIndex) || + oldIndex === newIndex || + oldIndex < 0 || + newIndex < 0 + ) { + return; + } + const [moved] = this._presets.splice(oldIndex, 1); + if (!moved) { + return; + } + this._presets.splice(newIndex, 0, moved); + this._renderContent(); + } + + _returnToList() { + this._editingIndex = undefined; + this._renderContent(); + } + + _renderContent() { + if (!this._dialog || !this._params) { + return; + } + + if (this._editingIndex === undefined) { + this._renderList(); + } else { + this._renderEditor(); + } + } + + _renderList() { + this._dialog.setAttribute( + "header-title", + this._params.label || "Presets" + ); + const content = document.createElement("div"); + content.className = "preset-list"; + + if (!this._presets.length) { + const empty = document.createElement("div"); + empty.className = "empty"; + empty.textContent = "No presets configured"; + content.append(empty); + } + + let rows; + if (this._presets.length) { + const section = document.createElement("div"); + section.className = "preset-section"; + const sortable = document.createElement("ha-sortable"); + sortable.setAttribute("handle-selector", ".drag-handle"); + sortable.setAttribute("draggable-selector", ".preset-row"); + sortable.setAttribute("group", "opentherm-presets"); + sortable.addEventListener("item-moved", (event) => { + event.stopPropagation(); + const { oldIndex, newIndex } = event.detail || {}; + this._movePreset(Number(oldIndex), Number(newIndex)); + }); + rows = document.createElement("div"); + rows.className = "preset-rows"; + sortable.append(rows); + section.append(sortable); + content.append(section); + } + + this._presets.forEach((preset, index) => { + const row = document.createElement("div"); + row.className = "preset-row"; + + const icon = document.createElement("ha-icon"); + icon.setAttribute("icon", presetIcon(this._presets, index)); + + const handle = document.createElement("div"); + handle.className = "drag-handle"; + handle.setAttribute("title", `Drag to reorder ${preset.name}`); + handle.setAttribute("aria-label", `Drag to reorder ${preset.name}`); + const handleIcon = document.createElement("ha-icon"); + handleIcon.setAttribute("icon", "mdi:drag-horizontal-variant"); + handle.append(handleIcon); + + const text = document.createElement("div"); + const name = document.createElement("div"); + name.className = "preset-name"; + name.textContent = preset.name; + const detail = document.createElement("div"); + detail.className = "preset-detail"; + const duration = Number(preset.duration_minutes) || 0; + const temperature = Number(preset.temperature); + detail.textContent = `${ + Number.isFinite(temperature) ? temperature : preset.temperature + } °C${duration ? ` · ${duration} min` : ""}`; + text.append(name, detail); + + const edit = this._button( + localize(this._hass, "ui.common.edit", "Edit"), + () => { + this._editingIndex = index; + this._draft = { ...preset }; + this._renderContent(); + } + ); + edit.setAttribute("appearance", "plain"); + row.append(handle, icon, text, edit); + rows?.append(row); + }); + + if (this._presets.length < MAX_PRESETS) { + const add = this._button( + localize(this._hass, "ui.common.add", "Add"), + () => { + this._editingIndex = -1; + this._draft = { + name: "", + icon: "mdi:water", + temperature: Math.min(Math.max(45, this._minimum), this._maximum), + duration_minutes: 0, + }; + this._renderContent(); + } + ); + content.append(add); + } + + const cancel = this._button( + localize(this._hass, "ui.common.cancel", "Cancel"), + () => this.closeDialog() + ); + cancel.setAttribute("appearance", "plain"); + const apply = this._button( + localize(this._hass, "ui.common.apply", "Apply"), + () => { + this._params.saveCallback(clonePresets(this._presets)); + this.closeDialog(); + }, + { primary: true } + ); + const footer = document.createElement("div"); + footer.className = "dialog-footer"; + footer.setAttribute("slot", "footer"); + footer.append(cancel, apply); + + this._dialog.replaceChildren(content, footer); + } + + _renderEditor() { + const isNew = this._editingIndex === -1; + this._dialog.setAttribute( + "header-title", + isNew + ? "Add preset" + : String(this._draft?.name || "").trim() || "Preset" + ); + + const content = document.createElement("div"); + content.className = "preset-editor-content"; + const alert = document.createElement("ha-alert"); + alert.setAttribute("alert-type", "error"); + alert.hidden = true; + content.append(alert); + + const form = document.createElement("ha-form"); + form.hass = this._hass; + form.data = { ...this._draft }; + // Keep the native HA form controls, but use a compact grid row gap in + // this small editor instead of the standard 24px form-grid spacing. + form.style.setProperty( + "--ha-space-6", + "var(--ha-space-2, 8px)" + ); + const buildSchema = () => [ + { + name: "", + type: "grid", + column_min_width: "240px", + schema: [ + { name: "name", required: true, selector: { text: {} } }, + { + name: "icon", + required: false, + selector: { icon: {} }, + }, + { + name: "temperature", + required: true, + selector: { + number: { + min: this._minimum, + max: this._maximum, + step: 1, + mode: "box", + unit_of_measurement: "°C", + }, + }, + }, + { + name: "duration_minutes", + required: true, + selector: { + number: { + min: 0, + max: 999, + step: 1, + mode: "box", + unit_of_measurement: "min", + }, + }, + }, + ], + }, + ]; + form.schema = buildSchema(); + const labels = { + name: localize(this._hass, "ui.common.name", "Name"), + icon: localize( + this._hass, + "ui.components.selectors.selector.types.icon", + "Icon" + ), + temperature: localize( + this._hass, + "ui.dialogs.more_info_control.climate.temperature", + "Temperature" + ), + duration_minutes: localize( + this._hass, + "ui.components.selectors.selector.types.duration", + "Duration" + ), + }; + form.computeLabel = (schema) => labels[schema.name] || schema.name; + form.addEventListener("value-changed", (event) => { + this._draft = { ...event.detail.value }; + if (!isNew) { + this._dialog.setAttribute( + "header-title", + String(this._draft.name || "").trim() || "Preset" + ); + } + alert.hidden = true; + }); + + this._forms = [form]; + this._alert = alert; + content.append(form); + + const cancel = this._button( + localize(this._hass, "ui.common.cancel", "Cancel"), + () => this._returnToList() + ); + cancel.setAttribute("appearance", "plain"); + const save = this._button( + localize(this._hass, "ui.common.save", "Save"), + () => this._saveDraft(), + { primary: true } + ); + const footer = document.createElement("div"); + footer.className = "dialog-footer"; + footer.setAttribute("slot", "footer"); + if (!isNew) { + const remove = this._button( + localize(this._hass, "ui.common.delete", "Delete"), + () => { + this._presets.splice(this._editingIndex, 1); + this._editingIndex = undefined; + this._renderContent(); + }, + { danger: true } + ); + remove.style.marginInlineEnd = "auto"; + footer.append(remove); + } + footer.append(cancel, save); + + const headerBack = document.createElement("ha-icon-button"); + headerBack.path = MDI_ARROW_LEFT; + headerBack.label = localize(this._hass, "ui.common.back", "Back"); + headerBack.setAttribute("slot", "headerNavigationIcon"); + headerBack.addEventListener("click", (event) => { + event.preventDefault(); + event.stopPropagation(); + this._returnToList(); + }); + this._dialog.replaceChildren(content, footer, headerBack); + } + + _saveDraft() { + if ( + this._forms?.some( + (form) => form.reportValidity && !form.reportValidity() + ) + ) { + return; + } + + const preset = { + name: String(this._draft.name || "").trim(), + icon: String(this._draft.icon || "").trim(), + temperature: Number(this._draft.temperature), + duration_minutes: Math.max( + 0, + Math.min(999, Number(this._draft.duration_minutes) || 0) + ), + }; + const duplicate = this._presets.some( + (item, index) => + index !== this._editingIndex && + String(item.name).trim().toLocaleLowerCase() === + preset.name.toLocaleLowerCase() + ); + const reserved = ["off", "manual", "auto", "custom"].includes( + preset.name.toLowerCase() + ); + if ( + !preset.name || + !Number.isFinite(preset.temperature) || + preset.temperature < this._minimum || + preset.temperature > this._maximum || + duplicate || reserved + ) { + this._alert.textContent = reserved + ? "Off, Manual, Auto and Custom are reserved names. Choose another preset name." + : duplicate + ? "Preset names must be unique." + : `Enter a name and temperature between ${this._minimum} and ${this._maximum} °C.`; + this._alert.hidden = false; + return; + } + + if (this._editingIndex === -1) { + this._presets.push(preset); + } else { + this._presets[this._editingIndex] = preset; + } + this._presets = clonePresets(this._presets); + this._editingIndex = undefined; + this._renderContent(); + } +} + +if (!customElements.get(PRESETS_SELECTOR_TYPE)) { + customElements.define(PRESETS_SELECTOR_TYPE, OpenThermPresetsSelector); +} + +if (!customElements.get(PRESETS_DIALOG_TYPE)) { + customElements.define(PRESETS_DIALOG_TYPE, OpenThermPresetsDialog); +} + +const PRIMARY_MODES = ["off", "Manual", "Auto"]; +const WATER_HEATER_OPERATION_MODE = 2; + +const BOOST_MODES = [ + "Boost 15 Mins", + "Boost 30 Mins", + "Boost 45 Mins", +]; + +const MODE_ICONS = { + off: "mdi:power", + Manual: "mdi:water-thermometer", + Auto: "mdi:calendar-clock", + "Shower 1": "mdi:shower-head", + "Shower 2": "mdi:shower", + "Boost 15 Mins": "mdi:numeric-1", + "Boost 30 Mins": "mdi:numeric-2", + "Boost 45 Mins": "mdi:numeric-3", +}; + +const supportsWaterHeater = (hass, context) => { + const entityId = context?.entity_id; + const stateObj = entityId ? hass.states[entityId] : undefined; + const supportedFeatures = Number( + stateObj?.attributes?.supported_features || 0 + ); + return Boolean( + entityId?.startsWith("water_heater.") && + stateObj && + ((supportedFeatures & WATER_HEATER_OPERATION_MODE) !== 0 || + Array.isArray(stateObj.attributes.operation_list)) + ); +}; + +const entityOperationModes = (stateObj, presetNames) => { + const operationList = stateObj?.attributes?.operation_list; + if (Array.isArray(operationList) && operationList.length) { + return operationList; + } + return presetNames.length + ? [...new Set(["off", "Manual", ...presetNames])] + : PRIMARY_MODES; +}; + +class OpenThermHotWaterControlModesCardFeature extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this._valueChanged = this._valueChanged.bind(this); + } + + static getStubConfig() { + return { type: `custom:${FEATURE_TYPE}` }; + } + + setConfig(config) { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + this._render(); + } + + set hass(hass) { + this._hass = hass; + this._render(); + } + + get hass() { + return this._hass; + } + + set context(context) { + this._context = context; + this._render(); + } + + get context() { + return this._context; + } + + connectedCallback() { + this._render(); + } + + get _stateObj() { + const entityId = this._context?.entity_id; + return entityId && this._hass ? this._hass.states[entityId] : undefined; + } + + _availableModes(stateObj) { + const entityModes = entityOperationModes( + stateObj, + this._presetNames(stateObj) + ); + // Keep the main native-style control limited to the three operating + // modes. Presets have their own dropdown feature, including when an older + // tile configuration stored preset names in operation_modes. + return PRIMARY_MODES.filter((mode) => entityModes.includes(mode)); + } + + _presetData(stateObj = this._stateObj) { + const presets = stateObj?.attributes?.[PRESETS_ATTRIBUTE]; + if (!Array.isArray(presets)) { + return []; + } + + return presets + .map((preset) => { + if (!preset || typeof preset !== "object") { + return null; + } + + const name = typeof preset.name === "string" ? preset.name.trim() : ""; + if (!name) { + return null; + } + + return { + name, + icon: + typeof preset.icon === "string" && preset.icon.trim() + ? preset.icon.trim() + : MODE_ICONS[name], + }; + }) + .filter(Boolean); + } + + _presetNames(stateObj = this._stateObj) { + const presets = this._presetData(stateObj); + if (presets.length > 0) { + return presets.map((preset) => preset.name); + } + + return ( + stateObj?.attributes?.preset_modes ?? [] + ); + } + + _option(mode) { + const icon = document.createElement("ha-icon"); + const iconSrc = this._presetData().find((preset) => preset.name === mode)?.icon; + icon.setAttribute("icon", iconSrc || MODE_ICONS[mode] || "mdi:water"); + return { + value: mode, + label: mode, + ariaLabel: mode, + icon, + }; + } + + async _valueChanged(event) { + event.stopPropagation(); + const stateObj = this._stateObj; + const value = event.detail?.item?.value ?? event.detail?.value; + if (!stateObj || !value) { + return; + } + + try { + if (value === "off") { + await this._hass.callService("water_heater", "turn_off", { + entity_id: stateObj.entity_id, + }); + return; + } + + await this._hass.callService("water_heater", "set_operation_mode", { + entity_id: stateObj.entity_id, + operation_mode: value, + }); + } catch (error) { + showTemperatureError(this, error); + this._renderSignature = undefined; + this._render(); + } + } + + _render() { + if (!this.shadowRoot) { + return; + } + + const stateObj = this._stateObj; + if ( + !this._config || + !this._hass || + !stateObj || + !supportsWaterHeater(this._hass, this._context) + ) { + this.shadowRoot.replaceChildren(); + return; + } + + const availableModes = this._availableModes(stateObj); + const selectedValue = + stateObj.state === "Custom" + ? "Manual" + : availableModes.includes(stateObj.state) + ? stateObj.state + : undefined; + const renderSignature = JSON.stringify([ + stateObj.state, + stateObj.state === "unavailable", + availableModes, + this._config.max_width, + ]); + if (this._select && this._renderSignature === renderSignature) { + void this._replaceNativeCurrentlyLabel(); + return; + } + + if (this._select) { + this._select.options = availableModes.map((mode) => this._option(mode)); + this._select.value = selectedValue; + this._select.disabled = stateObj.state === "unavailable"; + this._renderSignature = renderSignature; + void this._replaceNativeCurrentlyLabel(); + return; + } + + const style = document.createElement("style"); + style.textContent = ` + :host { + display: block; + height: var(--feature-height, 42px); + } + ha-control-select { + --control-select-thickness: var(--feature-height, 42px); + --control-select-border-radius: var(--feature-border-radius, 12px); + --control-select-color: var( + --state-water_heater-active-color, + var(--primary-color) + ); + } + `; + + const select = document.createElement("ha-control-select"); + select.options = availableModes.map((mode) => this._option(mode)); + select.value = selectedValue; + select.label = localize( + this._hass, + "ui.card.water_heater.mode", + "Mode" + ); + select.hideOptionLabel = true; + select.disabled = stateObj.state === "unavailable"; + const configuredMaxWidth = Number(this._config.max_width); + select.style.maxWidth = `${ + Number.isFinite(configuredMaxWidth) && configuredMaxWidth > 0 + ? configuredMaxWidth + : DEFAULT_MAX_WIDTH + }px`; + select.style.marginInline = "auto"; + select.addEventListener("value-changed", this._valueChanged); + + this._select = select; + this._renderSignature = renderSignature; + this.shadowRoot.replaceChildren(style, select); + void this._replaceNativeCurrentlyLabel(); + } + + async _replaceNativeCurrentlyLabel() { + let parent = this; + let thermostatCard; + while (parent) { + const root = parent.getRootNode(); + parent = root?.host; + if (parent?.localName === "hui-thermostat-card") { + thermostatCard = parent; + break; + } + } + + const control = thermostatCard?.shadowRoot?.querySelector( + NATIVE_TEMPERATURE_CONTROL + ); + if (!control?.shadowRoot) { + return; + } + + if (control.updateComplete) { + await control.updateComplete; + } + + const labels = control.shadowRoot.querySelectorAll(".info > p.label"); + const currentLabel = labels[labels.length - 1]; + const value = currentLabel?.querySelector("span"); + if (!currentLabel || !value) { + return; + } + + const icon = document.createElement("ha-icon"); + icon.setAttribute("icon", "mdi:thermometer"); + icon.setAttribute( + "aria-label", + this._hass.formatEntityAttributeName?.( + this._stateObj, + "current_temperature" + ) || "Current temperature" + ); + icon.style.setProperty("--mdc-icon-size", "18px"); + currentLabel.style.display = "flex"; + currentLabel.style.alignItems = "center"; + currentLabel.style.justifyContent = "center"; + currentLabel.style.gap = "4px"; + currentLabel.replaceChildren(icon, value); + } +} + +class OpenThermHotWaterControlPresetsCardFeature extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this._valueChanged = this._valueChanged.bind(this); + } + + static getStubConfig() { + return { type: `custom:${PRESETS_FEATURE_TYPE}` }; + } + + setConfig(config) { + if (!config) { + throw new Error("Invalid configuration"); + } + this._config = config; + this._render(); + } + + set hass(hass) { + this._hass = hass; + this._render(); + } + + get hass() { + return this._hass; + } + + set context(context) { + this._context = context; + this._render(); + } + + get context() { + return this._context; + } + + connectedCallback() { + this._render(); + } + + get _stateObj() { + const entityId = this._context?.entity_id; + return entityId && this._hass ? this._hass.states[entityId] : undefined; + } + + _availableModes(stateObj) { + const presetData = this._presetData(stateObj); + const presetNames = this._presetNames(stateObj); + const entityModes = entityOperationModes(stateObj, presetNames); + const configuredModes = this._config.preset_modes + ? [...new Set([...this._config.preset_modes, ...presetNames])] + : presetNames; + + return configuredModes.filter((mode) => { + const inEntity = entityModes.includes(mode); + if (!inEntity) { + return false; + } + + if (!presetData.length) { + return BOOST_MODES.includes(mode); + } + + return presetData.some((preset) => preset.name === mode); + }); + } + + _presetData(stateObj = this._stateObj) { + const presets = stateObj?.attributes?.[PRESETS_ATTRIBUTE]; + if (!Array.isArray(presets)) { + return []; + } + + return presets + .map((preset) => { + if (!preset || typeof preset !== "object") { + return null; + } + + const name = typeof preset.name === "string" ? preset.name.trim() : ""; + if (!name) { + return null; + } + + return { + name, + icon: + typeof preset.icon === "string" && preset.icon.trim() + ? preset.icon.trim() + : MODE_ICONS[name], + durationMinutes: Number(preset.duration_minutes) || 0, + }; + }) + .filter(Boolean); + } + + _presetNames(stateObj = this._stateObj) { + const presets = this._presetData(stateObj); + if (presets.length > 0) { + return presets.map((preset) => preset.name); + } + + return stateObj?.attributes?.preset_modes ?? []; + } + + _presetIcon(mode, stateObj = this._stateObj) { + return ( + this._presetData(stateObj).find((preset) => preset.name === mode)?.icon || + MODE_ICONS[mode] + ); + } + + async _valueChanged(event) { + event.stopPropagation(); + const stateObj = this._stateObj; + const value = event.detail?.item?.value ?? event.detail?.value; + if (!stateObj || !value) { + return; + } + + try { + await this._hass.callService("water_heater", "set_operation_mode", { + entity_id: stateObj.entity_id, + operation_mode: value, + }); + } catch (error) { + showTemperatureError(this, error); + this._renderSignature = undefined; + this._render(); + } + } + + _render() { + if (!this.shadowRoot) { + return; + } + + const stateObj = this._stateObj; + if ( + !this._config || + !this._hass || + !stateObj || + !supportsWaterHeater(this._hass, this._context) + ) { + this.shadowRoot.replaceChildren(); + return; + } + + const availableModes = this._availableModes(stateObj); + this.style.display = availableModes.length ? "" : "none"; + const renderSignature = JSON.stringify([ + stateObj.state === "unavailable", + availableModes, + this._presetData(stateObj), + ]); + if (this._select && this._renderSignature === renderSignature) { + this._select.value = availableModes.includes(stateObj.state) + ? stateObj.state + : undefined; + this._select.disabled = stateObj.state === "unavailable"; + return; + } + + const style = document.createElement("style"); + style.textContent = ` + :host { + display: block; + height: auto; + } + .controls { + display: flex; + flex-direction: column; + gap: var(--feature-button-spacing, 12px); + } + ha-control-select-menu { + box-sizing: border-box; + --control-select-menu-height: var(--feature-height, 42px); + --control-select-menu-border-radius: var(--feature-border-radius, 12px); + --control-select-menu-focus-color: var(--feature-color); + display: block; + line-height: var(--ha-line-height-condensed); + width: 100%; + } + `; + + const controls = document.createElement("div"); + controls.className = "controls"; + const select = document.createElement("ha-control-select-menu"); + select.options = availableModes.map((mode) => ({ + value: mode, + label: mode, + icon: this._presetIcon(mode, stateObj), + })); + select.value = availableModes.includes(stateObj.state) + ? stateObj.state + : undefined; + select.label = localize(this._hass, "ui.card.climate.preset", "Preset"); + select.showArrow = true; + select.hideLabel = true; + const icon = document.createElement("ha-icon"); + icon.setAttribute("slot", "icon"); + icon.setAttribute("icon", "mdi:tune-variant"); + select.append(icon); + select.disabled = stateObj.state === "unavailable"; + select.addEventListener("wa-select", this._valueChanged); + controls.append(select); + + this._select = select; + this.style.gridRow = ""; + this._renderSignature = renderSignature; + this.shadowRoot.replaceChildren(style, controls); + } +} + +class OpenThermHotWaterControlMoreInfo extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + } + set hass(hass) { + this._hass = hass; + this._render(); + } + + get hass() { + return this._hass; + } + + set stateObj(stateObj) { + this._stateObj = stateObj; + this._render(); + } + + get stateObj() { + return this._stateObj; + } + + _presetNames() { + const presets = this._stateObj?.attributes?.[PRESETS_ATTRIBUTE]; + if (Array.isArray(presets) && presets.length > 0) { + return presets + .map((preset) => + typeof preset?.name === "string" ? preset.name.trim() : "" + ) + .filter(Boolean); + } + + return this._stateObj?.attributes?.preset_modes ?? []; + } + + _listPresetNames() { + return this._presetNames(); + } + + connectedCallback() { + this._render(); + } + + async _ensureNativeTemperatureControl() { + if (customElements.get(NATIVE_TEMPERATURE_CONTROL)) { + return; + } + + if (!this._nativeControlPromise) { + this._nativeControlPromise = (async () => { + if (window.loadCardHelpers && this._stateObj) { + const helpers = await window.loadCardHelpers(); + const loader = helpers.createCardElement({ + type: "thermostat", + entity: this._stateObj.entity_id, + }); + loader.hass = this._hass; + } + await customElements.whenDefined(NATIVE_TEMPERATURE_CONTROL); + })(); + } + + await this._nativeControlPromise; + this._render(); + } + + _formatCurrentTemperature(stateObj) { + if (this._hass.formatEntityAttributeValue) { + return this._hass.formatEntityAttributeValue( + stateObj, + "current_temperature" + ); + } + return `${stateObj.attributes.current_temperature} ${ + stateObj.attributes.temperature_unit || "°C" + }`; + } + + _formatCurrentTemperatureLabel(stateObj) { + if (this._hass.formatEntityAttributeName) { + return this._hass.formatEntityAttributeName( + stateObj, + "current_temperature" + ); + } + return "Current temperature"; + } + + _render() { + if (!this.shadowRoot || !this._hass || !this._stateObj) { + return; + } + + if (!customElements.get(NATIVE_TEMPERATURE_CONTROL)) { + const loading = document.createElement("ha-circular-progress"); + loading.setAttribute("active", ""); + this.shadowRoot.replaceChildren(loading); + void this._ensureNativeTemperatureControl(); + return; + } + + const currentTemperature = this._stateObj.attributes.current_temperature; + if (this._temperatureControl) { + this._temperatureControl.stateObj = this._stateObj; + this._current.hidden = currentTemperature == null; + if (currentTemperature != null) { + this._currentLabel.textContent = + this._formatCurrentTemperatureLabel(this._stateObj); + this._currentValue.textContent = this._formatCurrentTemperature( + this._stateObj + ); + } + this._modes.hass = this._hass; + this._modes.context = { entity_id: this._stateObj.entity_id }; + if (this._presets) { + this._presets.hass = this._hass; + this._presets.context = { entity_id: this._stateObj.entity_id }; + } + return; + } + + const style = document.createElement("style"); + style.textContent = ` + :host { + display: block; + color: var(--primary-text-color); + } + .current { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-bottom: var(--ha-space-4, 16px); + text-align: center; + } + .current .label { + color: var(--secondary-text-color); + font-size: var(--ha-font-size-m, 14px); + line-height: var(--ha-line-height-condensed, 1.2); + } + .current .value { + margin-top: var(--ha-space-1, 4px); + font-size: var(--ha-font-size-xl, 24px); + font-weight: var(--ha-font-weight-medium, 500); + line-height: var(--ha-line-height-condensed, 1.2); + } + .temperature { + display: flex; + justify-content: center; + overflow: hidden; + width: 100%; + } + ha-state-control-water_heater-temperature { + box-sizing: border-box; + width: min(100%, 320px); + padding: 8px; + } + .features { + align-items: center; + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: var(--feature-button-spacing, 12px); + justify-content: center; + margin-top: var(--ha-space-4, 16px); + margin-inline: auto; + max-width: 520px; + padding-inline: var(--ha-space-4, 16px); + width: 100%; + } + .modes { + width: 100%; + } + .presets { + width: 100%; + } + ha-circular-progress { + display: block; + margin: 40px auto; + } + `; + + const content = document.createElement("div"); + + const current = document.createElement("div"); + current.className = "current"; + current.hidden = currentTemperature == null; + + const label = document.createElement("div"); + label.className = "label"; + const value = document.createElement("div"); + value.className = "value"; + if (currentTemperature != null) { + label.textContent = this._formatCurrentTemperatureLabel(this._stateObj); + value.textContent = this._formatCurrentTemperature(this._stateObj); + } + current.append(label, value); + content.append(current); + + const temperature = document.createElement("div"); + temperature.className = "temperature"; + const temperatureControl = document.createElement( + NATIVE_TEMPERATURE_CONTROL + ); + temperatureControl.stateObj = this._stateObj; + temperatureControl.preventInteractionOnScroll = true; + temperature.append(temperatureControl); + content.append(temperature); + + const features = document.createElement("div"); + features.className = "features"; + + const modesContainer = document.createElement("div"); + modesContainer.className = "modes"; + const modes = document.createElement(FEATURE_TYPE); + modes.setConfig({ + type: `custom:${FEATURE_TYPE}`, + max_width: 320, + }); + modes.hass = this._hass; + modes.context = { entity_id: this._stateObj.entity_id }; + modesContainer.append(modes); + + const presetsContainer = document.createElement("div"); + presetsContainer.className = "presets"; + const presets = document.createElement(PRESETS_FEATURE_TYPE); + const presetModes = this._listPresetNames(); + presets.setConfig({ + type: `custom:${PRESETS_FEATURE_TYPE}`, + }); + if (presetModes.length > 0) { + presets.hass = this._hass; + presets.context = { entity_id: this._stateObj.entity_id }; + presetsContainer.append(presets); + features.append(modesContainer, presetsContainer); + this._presets = presets; + } else { + features.append(modesContainer); + this._presets = undefined; + } + + content.append(features); + + this._temperatureControl = temperatureControl; + this._current = current; + this._currentLabel = label; + this._currentValue = value; + this._modes = modes; + if (presetModes.length === 0) { + this._presets = undefined; + } + this.shadowRoot.replaceChildren(style, content); + } +} + +if (!customElements.get(FEATURE_TYPE)) { + customElements.define(FEATURE_TYPE, OpenThermHotWaterControlModesCardFeature); +} + +if (!customElements.get(PRESETS_FEATURE_TYPE)) { + customElements.define( + PRESETS_FEATURE_TYPE, + OpenThermHotWaterControlPresetsCardFeature + ); +} + +if (!customElements.get(MORE_INFO_TYPE)) { + customElements.define(MORE_INFO_TYPE, OpenThermHotWaterControlMoreInfo); +} + +window.customCardFeatures = window.customCardFeatures || []; +if (!window.customCardFeatures.some((feature) => feature.type === FEATURE_TYPE)) { + window.customCardFeatures.push({ + type: FEATURE_TYPE, + name: "Hot-water control modes", + isSupported: supportsWaterHeater, + }); +} + +if ( + !window.customCardFeatures.some( + (feature) => feature.type === PRESETS_FEATURE_TYPE + ) +) { + window.customCardFeatures.push({ + type: PRESETS_FEATURE_TYPE, + name: "Hot-water control presets", + isSupported: supportsWaterHeater, + }); +} + +const OPENTHERM_FEATURE_TYPES = new Set([ + `custom:${FEATURE_TYPE}`, + `custom:${PRESETS_FEATURE_TYPE}`, +]); + +// Home Assistant normally rebuilds a custom feature after its element is +// defined. If that notification loses the cold-load race, the feature host can +// retain its temporary error element. Ask the owning view to recreate its +// cards once now that all OpenTherm feature elements are registered. +const rebuiltOpenThermFeatureTargets = new WeakSet(); + +const rebuildFailedOpenThermFeature = (rebuildTarget) => { + if ( + !OPENTHERM_FEATURE_TYPES.has(rebuildTarget?.feature?.type) || + !rebuildTarget.shadowRoot?.querySelector("hui-error-card") + ) { + return; + } + + let owner = rebuildTarget; + while (owner && owner.localName !== "hui-view") { + owner = owner.parentNode || owner.getRootNode?.().host; + } + if (!owner || rebuiltOpenThermFeatureTargets.has(owner)) { + return; + } + + rebuiltOpenThermFeatureTargets.add(owner); + rebuildTarget.dispatchEvent( + new CustomEvent("ll-rebuild", { bubbles: true, composed: true }) + ); +}; + +const INLINE_FEATURE_BACKPORT_ATTRIBUTE = + "data-opentherm-inline-feature-backport"; +const INLINE_LAYOUT_BACKPORT_ATTRIBUTE = + "data-opentherm-inline-layout-backport"; + +const removeOpenThermInlineLayout = (tile) => { + tile?.shadowRoot + ?.querySelector(`style[${INLINE_LAYOUT_BACKPORT_ATTRIBUTE}]`) + ?.remove(); + // Clean up the layout style used by versions 2.5.8 and 2.5.9. + tile?.shadowRoot + ?.querySelector("ha-tile-container") + ?.shadowRoot + ?.querySelector(`style[${INLINE_LAYOUT_BACKPORT_ATTRIBUTE}]`) + ?.remove(); +}; + +const installOpenThermInlineLayout = (tile) => { + const root = tile?.shadowRoot; + if ( + !root || + root.querySelector(`style[${INLINE_LAYOUT_BACKPORT_ATTRIBUTE}]`) + ) { + return; + } + + const style = document.createElement("style"); + style.setAttribute(INLINE_LAYOUT_BACKPORT_ATTRIBUTE, ""); + style.textContent = ` + ha-card { + display: flex !important; + flex-direction: column !important; + } + ha-card > ha-tile-container { + flex: 1 0 auto; + height: auto !important; + } + ha-card > hui-card-features[${INLINE_FEATURE_BACKPORT_ATTRIBUTE}] { + --feature-color: var(--tile-color); + box-sizing: border-box; + flex: none; + padding: 0 var(--ha-space-3, 12px) var(--ha-space-3, 12px); + width: 100%; + } + `; + root.append(style); +}; + +const needsOpenThermInlineFeatureBackport = (tile) => { + const config = tile?._config; + if ( + !config || + config.vertical || + config.features_position !== "inline" || + !Array.isArray(config.features) || + config.features.length < 2 + ) { + return false; + } + + return config.features + .slice(1) + .some((feature) => OPENTHERM_FEATURE_TYPES.has(feature.type)); +}; + +const restoreOpenThermInlineFeatures = (tile) => { + if (!tile?.shadowRoot) { + return; + } + + const injectedSelector = `hui-card-features[${INLINE_FEATURE_BACKPORT_ATTRIBUTE}]`; + const injected = tile.shadowRoot.querySelector(injectedSelector); + if (!needsOpenThermInlineFeatureBackport(tile)) { + injected?.remove(); + removeOpenThermInlineLayout(tile); + return; + } + + const container = tile.shadowRoot.querySelector("ha-tile-container"); + const card = tile.shadowRoot.querySelector("ha-card"); + if (!container || !card) { + return; + } + + // Newer Home Assistant versions render the remaining inline features + // themselves. Never duplicate or replace that native feature container. + const nativeBottomFeatures = container.querySelector( + `hui-card-features[slot="features"]:not([${INLINE_FEATURE_BACKPORT_ATTRIBUTE}])` + ); + const remainingFeatures = tile._config.features.slice(1); + const nativeFeatures = nativeBottomFeatures?.features; + const nativeHasRemainingFeatures = + Array.isArray(nativeFeatures) && + nativeFeatures.length === remainingFeatures.length && + nativeFeatures.every( + (feature, index) => feature.type === remainingFeatures[index].type + ); + if (nativeHasRemainingFeatures) { + injected?.remove(); + removeOpenThermInlineLayout(tile); + return; + } + + installOpenThermInlineLayout(tile); + + const bottomFeatures = + injected || document.createElement("hui-card-features"); + bottomFeatures.setAttribute(INLINE_FEATURE_BACKPORT_ATTRIBUTE, ""); + bottomFeatures.removeAttribute("slot"); + // Keep the mode buttons and preset selector as separate full-width rows. + bottomFeatures.columns = 1; + bottomFeatures.hass = tile.hass; + bottomFeatures.context = + tile._featureContext || { entity_id: tile._config.entity }; + bottomFeatures.color = tile._config.color; + bottomFeatures.features = remainingFeatures; + + if (bottomFeatures.parentElement !== card) { + card.append(bottomFeatures); + } +}; + +const installOpenThermInlineFeatureBackport = async () => { + await customElements.whenDefined("hui-tile-card"); + const TileCard = customElements.get("hui-tile-card"); + const prototype = TileCard?.prototype; + if (!prototype || prototype._openThermInlineFeatureBackport) { + return; + } + + prototype._openThermInlineFeatureBackport = true; + + const originalUpdated = prototype.updated; + prototype.updated = function (...args) { + const result = originalUpdated?.apply(this, args); + queueMicrotask(() => restoreOpenThermInlineFeatures(this)); + return result; + }; + + // Older Tile cards report no bottom feature rows while Inline is selected. + // Account for the restored rows so masonry and Sections layouts allocate the + // same height as the visible controls. Native mixed-layout versions expose + // _featureLayout and already include these rows in their own calculations. + const originalGetCardSize = prototype.getCardSize; + if (originalGetCardSize) { + prototype.getCardSize = function (...args) { + let size = originalGetCardSize.apply(this, args); + if ( + typeof this._featureLayout !== "function" && + needsOpenThermInlineFeatureBackport(this) + ) { + size += this._config.features.slice(1).length; + } + return size; + }; + } + + const originalGetGridOptions = prototype.getGridOptions; + if (originalGetGridOptions) { + prototype.getGridOptions = function (...args) { + const nativeOptions = originalGetGridOptions.apply(this, args); + if ( + typeof this._featureLayout === "function" || + !needsOpenThermInlineFeatureBackport(this) + ) { + return nativeOptions; + } + + const addedRows = this._config.features.slice(1).length; + return { + ...nativeOptions, + rows: + typeof nativeOptions.rows === "number" + ? nativeOptions.rows + addedRows + : nativeOptions.rows, + min_rows: + typeof nativeOptions.min_rows === "number" + ? nativeOptions.min_rows + addedRows + : nativeOptions.min_rows, + }; + }; + } + + refreshOpenThermFeatureLayout(); +}; + +const observedOpenThermFeatureRoots = new WeakSet(); +let openThermFeatureObserver; +let openThermFeatureObservationEnded = false; +let openThermFeatureObservationStopScheduled = false; + +const observeOpenThermFeatureRoot = (root) => { + if ( + !root || + openThermFeatureObservationEnded || + observedOpenThermFeatureRoots.has(root) || + (!(root instanceof Document) && !(root instanceof ShadowRoot)) + ) { + return; + } + + if (!openThermFeatureObserver) { + openThermFeatureObserver = new MutationObserver((mutations) => { + for (const mutation of mutations) { + for (const node of mutation.addedNodes) { + if (node.nodeType === Node.ELEMENT_NODE) { + refreshOpenThermFeatureLayout(node); + } + } + } + }); + } + observedOpenThermFeatureRoots.add(root); + openThermFeatureObserver.observe(root, { childList: true, subtree: true }); + if (!openThermFeatureObservationStopScheduled) { + openThermFeatureObservationStopScheduled = true; + window.setTimeout(() => { + openThermFeatureObservationEnded = true; + openThermFeatureObserver?.disconnect(); + }, 60000); + } +}; + +const refreshOpenThermFeatureLayout = (root = document) => { + observeOpenThermFeatureRoot(root); + const elements = [ + ...(root.nodeType === Node.ELEMENT_NODE ? [root] : []), + ...(root.querySelectorAll?.("*") || []), + ]; + for (const element of elements) { + if (element.localName === "hui-tile-card") { + restoreOpenThermInlineFeatures(element); + } + + if (element.localName === "hui-card-feature") { + rebuildFailedOpenThermFeature(element); + } + + if (element.shadowRoot) { + observeOpenThermFeatureRoot(element.shadowRoot); + refreshOpenThermFeatureLayout(element.shadowRoot); + } + } +}; + + +void installOpenThermInlineFeatureBackport(); diff --git a/custom_components/wiser/frontend/wiser-icons.js b/custom_components/wiser/frontend/wiser-icons.js new file mode 100644 index 00000000..a8567e3e --- /dev/null +++ b/custom_components/wiser/frontend/wiser-icons.js @@ -0,0 +1,22 @@ +// Wiser sidebar icons. Loaded globally so icons work before a card is opened. +(() => { + const icons = { + // W glyph traced from the official Wiser wordmark. + wiser: { + path: "M391,155 L399,149 L414,144 L457,143 L478,149 L487,155 L497,167 L524,249 L629,600 L632,604 L744,103 L755,83 L765,72 L780,62 L796,57 L817,57 L834,62 L851,72 L865,86 L711,699 L703,714 L690,727 L678,732 L607,732 L591,727 L577,713 L439,272 L300,713 L286,727 L270,732 L199,732 L183,724 L170,708 L166,699 L12,86 L26,72 L43,62 L60,57 L81,57 L97,62 L112,72 L122,83 L132,101 L154,191 L245,604 L248,600 L375,178 L384,161Z", + viewBox: "0 0 877 768", + }, + zigbee: { + path: "M11.1000 5.0000L11.1000 8.0000A.9 .9 0 0 0 12.9000 8.0000L12.9000 5.0000A.9 .9 0 0 0 11.1000 5.0000Z M5.1364 19.6364L8.1364 16.6364A.9 .9 0 0 0 6.8636 15.3636L3.8636 18.3636A.9 .9 0 0 0 5.1364 19.6364Z M20.1364 18.3636L17.1364 15.3636A.9 .9 0 0 0 15.8636 16.6364L18.8636 19.6364A.9 .9 0 0 0 20.1364 18.3636Z M7.0699 12.6966L12.5699 8.1966A.9 .9 0 0 0 11.4301 6.8034L5.9301 11.3034A.9 .9 0 0 0 7.0699 12.6966Z M11.4301 8.1966L16.9301 12.6966A.9 .9 0 0 0 18.0699 11.3034L12.5699 6.8034A.9 .9 0 0 0 11.4301 8.1966Z M7.1000 11.0000L7.1000 17.0000A.9 .9 0 0 0 8.9000 17.0000L8.9000 11.0000A.9 .9 0 0 0 7.1000 11.0000Z M8.0000 17.9000L16.0000 17.9000A.9 .9 0 0 0 16.0000 16.1000L8.0000 16.1000A.9 .9 0 0 0 8.0000 17.9000Z M16.9000 17.0000L16.9000 11.0000A.9 .9 0 0 0 15.1000 11.0000L15.1000 17.0000A.9 .9 0 0 0 16.9000 17.0000Z M9.75 3.25a2.25 2.25 0 1 0 4.5 0a2.25 2.25 0 1 0-4.5 0Z M1.0 20.25a2.25 2.25 0 1 0 4.5 0a2.25 2.25 0 1 0-4.5 0Z M18.5 20.25a2.25 2.25 0 1 0 4.5 0a2.25 2.25 0 1 0-4.5 0Z", + viewBox: "0 0 24 24", + }, + }; + window.customIcons = window.customIcons || {}; + window.customIcons["wiser"] = { + getIcon: async (name) => icons[name] || {}, + getIconList: async () => [ + { name: "wiser", keywords: ["wiser", "logo", "brand"] }, + { name: "zigbee", keywords: ["wiser", "home", "network"] }, + ], + }; +})(); diff --git a/custom_components/wiser/frontend/wiser-logo.png b/custom_components/wiser/frontend/wiser-logo.png new file mode 100644 index 00000000..a696c59d Binary files /dev/null and b/custom_components/wiser/frontend/wiser-logo.png differ diff --git a/custom_components/wiser/frontend/wiser-panel.js b/custom_components/wiser/frontend/wiser-panel.js new file mode 100644 index 00000000..e1892ba5 --- /dev/null +++ b/custom_components/wiser/frontend/wiser-panel.js @@ -0,0 +1,369 @@ +/*! WISER-CARD-VERSION wiser-panel 1.0.5 */ + +const WISER_ACTIVE_PANEL_KEY = "wiser-active-panel"; + +class WiserPanel extends HTMLElement { + constructor() { + super(); + this.attachShadow({ mode: "open" }); + this.shadowRoot.innerHTML = ` + +
+ + + + Wiser + + +
+

Loading Wiser…

`; + this.shadowRoot.getElementById("menu").addEventListener("click", () => { + this.dispatchEvent(new CustomEvent("hass-toggle-menu", { bubbles:true, composed:true })); + }); + this.shadowRoot.getElementById("settings").addEventListener("click", () => { + const view = this._views.find((candidate) => !candidate.hidden); + if (typeof view?._openEditor === "function") void view._openEditor(); + }); + this._views = []; + this._tabs = []; + this._generation = 0; + this._draggedPanelId = undefined; + try { + this._activePanel = window.sessionStorage.getItem(WISER_ACTIVE_PANEL_KEY) || undefined; + } catch (_error) { + this._activePanel = undefined; + } + } + + set hass(hass) { + this._hass = hass; + this._syncSettings(); + this._syncTabEditing(); + for (const view of this._views) { + if (!view.hidden) view.hass = hass; + } + } + + set panel(panel) { + if (!this._tabPreferences) { + this._tabPreferences = panel.config.panels.map(({ id, title }) => ({ + id, + title, + })); + } + const config = this._mergeTabPreferences(panel.config); + if (JSON.stringify(config) === JSON.stringify(this._config)) return; + this._config = config; + void this._loadPanels(); + } + + _mergeTabPreferences(config) { + const preferences = new Map( + this._tabPreferences.map((tab, index) => [tab.id, { ...tab, index }]) + ); + const discoveredOrder = new Map( + config.panels.map((panel, index) => [panel.id, index]) + ); + const panels = config.panels + .map((panel) => ({ + ...panel, + title: preferences.get(panel.id)?.title || panel.title, + })) + .sort((left, right) => { + const leftOrder = preferences.get(left.id)?.index; + const rightOrder = preferences.get(right.id)?.index; + return ( + (leftOrder ?? preferences.size + discoveredOrder.get(left.id)) - + (rightOrder ?? preferences.size + discoveredOrder.get(right.id)) + ); + }); + return { ...config, panels }; + } + + _selectPanel(id) { + this._activePanel = id; + try { + window.sessionStorage.setItem(WISER_ACTIVE_PANEL_KEY, id); + } catch (_error) { + // Session storage can be unavailable in restricted browser contexts. + } + this._views.forEach((view, index) => { + const selected = this._config.panels[index].id === id; + view.hidden = !selected; + if (selected) view.hass = this._hass; + this._tabs[index].setAttribute("aria-selected", String(selected)); + this._tabs[index].tabIndex = selected ? 0 : -1; + }); + this._syncSettings(); + } + + _syncSettings() { + const view = this._views.find((candidate) => !candidate.hidden); + this.shadowRoot.getElementById("settings").hidden = !( + this._hass?.user?.is_admin && typeof view?._openEditor === "function" + ); + } + + _syncTabEditing() { + const editable = Boolean(this._hass?.user?.is_admin); + for (const tab of this._tabs) { + tab.draggable = editable; + tab.title = editable + ? "Drag to reorder. Double-click to rename." + : ""; + } + } + + _clearDropIndicators() { + for (const tab of this._tabs) { + tab.classList.remove("drop-before", "drop-after"); + } + } + + _applyPanels(panels) { + const viewsById = new Map( + this._config.panels.map((panel, index) => [panel.id, this._views[index]]) + ); + this._tabPreferences = panels.map(({ id, title }) => ({ id, title })); + this._config = { ...this._config, panels }; + this._views = panels.map((panel) => viewsById.get(panel.id)); + this.shadowRoot.querySelector("main").replaceChildren(...this._views); + this._renderTabs(); + } + + async _persistTabs(previousPanels) { + try { + await this._hass.callWS({ + type: "wiser/panel/configure_tabs", + tabs: this._config.panels.map(({ id, title }) => ({ id, title })), + }); + } catch (error) { + this._applyPanels(previousPanels); + this.dispatchEvent( + new CustomEvent("hass-notification", { + bubbles: true, + composed: true, + detail: { + message: "Unable to save the Wiser panel tab layout.", + }, + }) + ); + console.error("Unable to save Wiser panel tabs", error); + } + } + + _movePanel(panelId, targetId, after) { + if (!this._hass?.user?.is_admin || panelId === targetId) return; + const previousPanels = [...this._config.panels]; + const panels = [...previousPanels]; + const sourceIndex = panels.findIndex((panel) => panel.id === panelId); + if (sourceIndex < 0) return; + const [panel] = panels.splice(sourceIndex, 1); + const targetIndex = panels.findIndex((candidate) => candidate.id === targetId); + if (targetIndex < 0) return; + panels.splice(targetIndex + (after ? 1 : 0), 0, panel); + if (panels.every((candidate, index) => candidate === previousPanels[index])) return; + this._applyPanels(panels); + void this._persistTabs(previousPanels); + } + + _startRename(panelId, tab) { + if (!this._hass?.user?.is_admin || tab.querySelector("input")) return; + const panel = this._config.panels.find((candidate) => candidate.id === panelId); + if (!panel) return; + + const input = document.createElement("input"); + input.className = "tab-name-input"; + input.value = panel.title; + input.maxLength = 64; + input.setAttribute("aria-label", `Rename ${panel.title} tab`); + input.addEventListener("click", (event) => event.stopPropagation()); + input.addEventListener("dblclick", (event) => event.stopPropagation()); + + let finished = false; + const finish = (save) => { + if (finished) return; + finished = true; + const title = input.value.trim() || panel.default_title || panel.title; + if (!save || title === panel.title) { + tab.textContent = panel.title; + this._syncTabEditing(); + return; + } + const previousPanels = this._config.panels.map((item) => ({ ...item })); + const panels = this._config.panels.map((item) => + item.id === panelId ? { ...item, title } : item + ); + this._applyPanels(panels); + void this._persistTabs(previousPanels); + }; + + input.addEventListener("keydown", (event) => { + event.stopPropagation(); + if (event.key === "Enter") { + event.preventDefault(); + finish(true); + } else if (event.key === "Escape") { + event.preventDefault(); + finish(false); + } + }); + input.addEventListener("blur", () => finish(true)); + tab.draggable = false; + tab.replaceChildren(input); + input.focus(); + input.select(); + } + + _renderTabs() { + const panels = this._config.panels; + const container = this.shadowRoot.getElementById("panel-tabs"); + container.hidden = panels.length === 0; + this._tabs = panels.map((panel, index) => { + const tab = document.createElement("button"); + tab.type = "button"; + tab.className = "panel-tab"; + tab.textContent = panel.title; + tab.id = `wiser-panel-tab-${index}`; + tab.setAttribute("role", "tab"); + tab.setAttribute("aria-controls", `wiser-panel-view-${index}`); + tab.addEventListener("click", () => this._selectPanel(panel.id)); + tab.addEventListener("dblclick", (event) => { + event.preventDefault(); + this._startRename(panel.id, tab); + }); + tab.addEventListener("dragstart", (event) => { + if (!this._hass?.user?.is_admin) { + event.preventDefault(); + return; + } + this._draggedPanelId = panel.id; + tab.classList.add("dragging"); + event.dataTransfer.effectAllowed = "move"; + event.dataTransfer.setData("text/plain", panel.id); + }); + tab.addEventListener("dragover", (event) => { + if (!this._draggedPanelId || this._draggedPanelId === panel.id) return; + event.preventDefault(); + event.dataTransfer.dropEffect = "move"; + this._clearDropIndicators(); + const bounds = tab.getBoundingClientRect(); + const after = event.clientX >= bounds.left + bounds.width / 2; + tab.classList.add(after ? "drop-after" : "drop-before"); + }); + tab.addEventListener("drop", (event) => { + if (!this._draggedPanelId) return; + event.preventDefault(); + const bounds = tab.getBoundingClientRect(); + const after = event.clientX >= bounds.left + bounds.width / 2; + const draggedPanelId = this._draggedPanelId; + this._draggedPanelId = undefined; + this._clearDropIndicators(); + this._movePanel(draggedPanelId, panel.id, after); + }); + tab.addEventListener("dragend", () => { + this._draggedPanelId = undefined; + this._clearDropIndicators(); + tab.classList.remove("dragging"); + }); + tab.addEventListener("keydown", (event) => { + let next; + if (event.key === "ArrowRight") next = (index + 1) % panels.length; + else if (event.key === "ArrowLeft") next = (index + panels.length - 1) % panels.length; + else if (event.key === "Home") next = 0; + else if (event.key === "End") next = panels.length - 1; + else return; + event.preventDefault(); + this._selectPanel(panels[next].id); + this._tabs[next].focus(); + }); + const view = this._views[index]; + view.id = `wiser-panel-view-${index}`; + view.setAttribute("role", "tabpanel"); + view.setAttribute("aria-labelledby", tab.id); + return tab; + }); + container.replaceChildren(...this._tabs); + this._syncTabEditing(); + const selected = panels.some((panel) => panel.id === this._activePanel) + ? this._activePanel : panels[0]?.id; + if (selected) this._selectPanel(selected); + } + + async _loadPanels() { + const generation = ++this._generation; + const main = this.shadowRoot.querySelector("main"); + try { + for (const panel of this._config.panels) { + if (!customElements.get(panel.component)) await import(panel.module_url); + await customElements.whenDefined(panel.component); + } + if (generation !== this._generation) return; + this._views = this._config.panels.map((panel) => { + const view = document.createElement(panel.component); + view.className = "panel-view"; + view.setAttribute("nested", ""); + view.hass = this._hass; + view.panel = { config: panel.config }; + return view; + }); + main.replaceChildren(...this._views); + this._renderTabs(); + } catch (error) { + if (generation !== this._generation) return; + this._views = []; + this.shadowRoot.getElementById("panel-tabs").hidden = true; + const message = document.createElement("p"); + message.className = "message"; + message.setAttribute("role", "alert"); + message.textContent = error?.message || "Unable to load Wiser panels."; + main.replaceChildren(message); + console.error("Unable to load Wiser panels", error); + } + } +} + +if (!customElements.get("wiser-panel")) { + customElements.define("wiser-panel", WiserPanel); +} diff --git a/custom_components/wiser/frontend/wiser-schedule-card.js b/custom_components/wiser/frontend/wiser-schedule-card.js deleted file mode 100644 index 86522707..00000000 --- a/custom_components/wiser/frontend/wiser-schedule-card.js +++ /dev/null @@ -1,3340 +0,0 @@ -var wiserschedulecard=function(t){"use strict";var e=function(t,i){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i])},e(t,i)};function i(t,i){if("function"!=typeof i&&null!==i)throw new TypeError("Class extends value "+String(i)+" is not a constructor or null");function s(){this.constructor=t}e(t,i),t.prototype=null===i?Object.create(i):(s.prototype=i.prototype,new s)}var s=function(){return s=Object.assign||function(t){for(var e,i=1,s=arguments.length;i=0;a--)(n=t[a])&&(o=(r<3?n(o):r>3?n(e,i,o):n(e,i))||o);return r>3&&o&&Object.defineProperty(e,i,o),o}function r(t){var e="function"==typeof Symbol&&Symbol.iterator,i=e&&t[e],s=0;if(i)return i.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&s>=t.length&&(t=void 0),{value:t&&t[s++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}"function"==typeof SuppressedError&&SuppressedError; -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const o=globalThis,a=o.ShadowRoot&&(void 0===o.ShadyCSS||o.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,l=Symbol(),d=new WeakMap;let c=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==l)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(a&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=d.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&d.set(e,t))}return t}toString(){return this.cssText}};const h=(t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new c(i,t,l)},p=(t,e)=>{if(a)t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet);else for(const i of e){const e=document.createElement("style"),s=o.litNonce;void 0!==s&&e.setAttribute("nonce",s),e.textContent=i.cssText,t.appendChild(e)}},u=a?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new c("string"==typeof t?t:t+"",void 0,l))(e)})(t):t,{is:m,defineProperty:f,getOwnPropertyDescriptor:g,getOwnPropertyNames:v,getOwnPropertySymbols:_,getPrototypeOf:b}=Object,y=globalThis,x=y.trustedTypes,$=x?x.emptyScript:"",w=y.reactiveElementPolyfillSupport,A=(t,e)=>t,E={toAttribute(t,e){switch(e){case Boolean:t=t?$:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},S=(t,e)=>!m(t,e),C={attribute:!0,type:String,converter:E,reflect:!1,useDefault:!1,hasChanged:S}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */Symbol.metadata??=Symbol("metadata"),y.litPropertyMetadata??=new WeakMap;let T=class extends HTMLElement{static addInitializer(t){this._$Ei(),(this.l??=[]).push(t)}static get observedAttributes(){return this.finalize(),this._$Eh&&[...this._$Eh.keys()]}static createProperty(t,e=C){if(e.state&&(e.attribute=!1),this._$Ei(),this.prototype.hasOwnProperty(t)&&((e=Object.create(e)).wrapped=!0),this.elementProperties.set(t,e),!e.noAccessor){const i=Symbol(),s=this.getPropertyDescriptor(t,i,e);void 0!==s&&f(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){const{get:s,set:n}=g(this.prototype,t)??{get(){return this[e]},set(t){this[e]=t}};return{get:s,set(e){const r=s?.call(this);n?.call(this,e),this.requestUpdate(t,r,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)??C}static _$Ei(){if(this.hasOwnProperty(A("elementProperties")))return;const t=b(this);t.finalize(),void 0!==t.l&&(this.l=[...t.l]),this.elementProperties=new Map(t.elementProperties)}static finalize(){if(this.hasOwnProperty(A("finalized")))return;if(this.finalized=!0,this._$Ei(),this.hasOwnProperty(A("properties"))){const t=this.properties,e=[...v(t),..._(t)];for(const i of e)this.createProperty(i,t[i])}const t=this[Symbol.metadata];if(null!==t){const e=litPropertyMetadata.get(t);if(void 0!==e)for(const[t,i]of e)this.elementProperties.set(t,i)}this._$Eh=new Map;for(const[t,e]of this.elementProperties){const i=this._$Eu(t,e);void 0!==i&&this._$Eh.set(i,t)}this.elementStyles=this.finalizeStyles(this.styles)}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(u(t))}else void 0!==t&&e.push(u(t));return e}static _$Eu(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}constructor(){super(),this._$Ep=void 0,this.isUpdatePending=!1,this.hasUpdated=!1,this._$Em=null,this._$Ev()}_$Ev(){this._$ES=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$E_(),this.requestUpdate(),this.constructor.l?.forEach(t=>t(this))}addController(t){(this._$EO??=new Set).add(t),void 0!==this.renderRoot&&this.isConnected&&t.hostConnected?.()}removeController(t){this._$EO?.delete(t)}_$E_(){const t=new Map,e=this.constructor.elementProperties;for(const i of e.keys())this.hasOwnProperty(i)&&(t.set(i,this[i]),delete this[i]);t.size>0&&(this._$Ep=t)}createRenderRoot(){const t=this.shadowRoot??this.attachShadow(this.constructor.shadowRootOptions);return p(t,this.constructor.elementStyles),t}connectedCallback(){this.renderRoot??=this.createRenderRoot(),this.enableUpdating(!0),this._$EO?.forEach(t=>t.hostConnected?.())}enableUpdating(t){}disconnectedCallback(){this._$EO?.forEach(t=>t.hostDisconnected?.())}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$ET(t,e){const i=this.constructor.elementProperties.get(t),s=this.constructor._$Eu(t,i);if(void 0!==s&&!0===i.reflect){const n=(void 0!==i.converter?.toAttribute?i.converter:E).toAttribute(e,i.type);this._$Em=t,null==n?this.removeAttribute(s):this.setAttribute(s,n),this._$Em=null}}_$AK(t,e){const i=this.constructor,s=i._$Eh.get(t);if(void 0!==s&&this._$Em!==s){const t=i.getPropertyOptions(s),n="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==t.converter?.fromAttribute?t.converter:E;this._$Em=s;const r=n.fromAttribute(e,t.type);this[s]=r??this._$Ej?.get(s)??r,this._$Em=null}}requestUpdate(t,e,i){if(void 0!==t){const s=this.constructor,n=this[t];if(i??=s.getPropertyOptions(t),!((i.hasChanged??S)(n,e)||i.useDefault&&i.reflect&&n===this._$Ej?.get(t)&&!this.hasAttribute(s._$Eu(t,i))))return;this.C(t,e,i)}!1===this.isUpdatePending&&(this._$ES=this._$EP())}C(t,e,{useDefault:i,reflect:s,wrapped:n},r){i&&!(this._$Ej??=new Map).has(t)&&(this._$Ej.set(t,r??e??this[t]),!0!==n||void 0!==r)||(this._$AL.has(t)||(this.hasUpdated||i||(e=void 0),this._$AL.set(t,e)),!0===s&&this._$Em!==t&&(this._$Eq??=new Set).add(t))}async _$EP(){this.isUpdatePending=!0;try{await this._$ES}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){if(!this.isUpdatePending)return;if(!this.hasUpdated){if(this.renderRoot??=this.createRenderRoot(),this._$Ep){for(const[t,e]of this._$Ep)this[t]=e;this._$Ep=void 0}const t=this.constructor.elementProperties;if(t.size>0)for(const[e,i]of t){const{wrapped:t}=i,s=this[e];!0!==t||this._$AL.has(e)||void 0===s||this.C(e,void 0,i,s)}}let t=!1;const e=this._$AL;try{t=this.shouldUpdate(e),t?(this.willUpdate(e),this._$EO?.forEach(t=>t.hostUpdate?.()),this.update(e)):this._$EM()}catch(e){throw t=!1,this._$EM(),e}t&&this._$AE(e)}willUpdate(t){}_$AE(t){this._$EO?.forEach(t=>t.hostUpdated?.()),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$EM(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$ES}shouldUpdate(t){return!0}update(t){this._$Eq&&=this._$Eq.forEach(t=>this._$ET(t,this[t])),this._$EM()}updated(t){}firstUpdated(t){}};T.elementStyles=[],T.shadowRootOptions={mode:"open"},T[A("elementProperties")]=new Map,T[A("finalized")]=new Map,w?.({ReactiveElement:T}),(y.reactiveElementVersions??=[]).push("2.1.1"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const I=globalThis,O=I.trustedTypes,k=O?O.createPolicy("lit-html",{createHTML:t=>t}):void 0,R="$lit$",P=`lit$${Math.random().toFixed(9).slice(2)}$`,L="?"+P,M=`<${L}>`,N=document,D=()=>N.createComment(""),H=t=>null===t||"object"!=typeof t&&"function"!=typeof t,U=Array.isArray,z="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,B=/-->/g,V=/>/g,j=RegExp(`>|${z}(?:([^\\s"'>=/]+)(${z}*=${z}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),W=/'/g,q=/"/g,Y=/^(?:script|style|textarea|title)$/i,X=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),G=Symbol.for("lit-noChange"),K=Symbol.for("lit-nothing"),J=new WeakMap,Z=N.createTreeWalker(N,129);function Q(t,e){if(!U(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==k?k.createHTML(e):e}let tt=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":3===e?"":"",o=F;for(let e=0;e"===l[0]?(o=n??F,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?j:'"'===l[3]?q:W):o===q||o===W?o=j:o===B||o===V?o=F:(o=j,n=void 0);const h=o===j&&t[e+1].startsWith("/>")?" ":"";r+=o===F?i+M:d>=0?(s.push(a),i.slice(0,d)+R+i.slice(d)+P+h):i+P+(-2===d?e:h)}return[Q(t,r+(t[i]||"")+(2===e?"":3===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),Z.currentNode=this.el.content,2===i||3===i){const t=this.el.content.firstChild;t.replaceWith(...t.childNodes)}for(;null!==(n=Z.nextNode())&&l.length0){n.textContent=O?O.emptyScript:"";for(let i=0;iU(t)||"function"==typeof t?.[Symbol.iterator])(t)?this.k(t):this._(t)}O(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}T(t){this._$AH!==t&&(this._$AR(),this._$AH=this.O(t))}_(t){this._$AH!==K&&H(this._$AH)?this._$AA.nextSibling.data=t:this.T(N.createTextNode(t)),this._$AH=t}$(t){const{values:e,_$litType$:i}=t,s="number"==typeof i?this._$AC(t):(void 0===i.el&&(i.el=tt.createElement(Q(i.h,i.h[0]),this.options)),i);if(this._$AH?._$AD===s)this._$AH.p(e);else{const t=new it(s,this),i=t.u(this.options);t.p(e),this.T(i),this._$AH=t}}_$AC(t){let e=J.get(t.strings);return void 0===e&&J.set(t.strings,e=new tt(t)),e}k(e){U(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.O(D()),this.O(D()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=K}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=et(this,t,e,0),r=!H(t)||t!==this._$AH&&t!==G,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{const s=i?.renderBefore??e;let n=s._$litPart$;if(void 0===n){const t=i?.renderBefore??null;s._$litPart$=n=new st(e.insertBefore(D(),t),t,void 0,i??{})}return n._$AI(t),n})(e,this.renderRoot,this.renderOptions)}connectedCallback(){super.connectedCallback(),this._$Do?.setConnected(!0)}disconnectedCallback(){super.disconnectedCallback(),this._$Do?.setConnected(!1)}render(){return G}};ht._$litElement$=!0,ht.finalized=!0,ct.litElementHydrateSupport?.({LitElement:ht});const pt=ct.litElementPolyfillSupport;pt?.({LitElement:ht}),(ct.litElementVersions??=[]).push("4.2.1"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const ut=t=>(e,i)=>{void 0!==i?i.addInitializer(()=>{customElements.define(t,e)}):customElements.define(t,e)},mt={attribute:!0,type:String,converter:E,reflect:!1,hasChanged:S},ft=(t=mt,e,i)=>{const{kind:s,metadata:n}=i;let r=globalThis.litPropertyMetadata.get(n);if(void 0===r&&globalThis.litPropertyMetadata.set(n,r=new Map),"setter"===s&&((t=Object.create(t)).wrapped=!0),r.set(i.name,t),"accessor"===s){const{name:s}=i;return{set(i){const n=e.get.call(this);e.set.call(this,i),this.requestUpdate(s,n,t)},init(e){return void 0!==e&&this.C(s,void 0,t,e),e}}}if("setter"===s){const{name:s}=i;return function(i){const n=this[s];e.call(this,i),this.requestUpdate(s,n,t)}}throw Error("Unsupported decorator location: "+s)}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function gt(t){return(e,i)=>"object"==typeof i?ft(t,e,i):((t,e,i)=>{const s=e.hasOwnProperty(i);return e.constructor.createProperty(i,t),s?Object.getOwnPropertyDescriptor(e,i):void 0})(t,e,i)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function vt(t){return gt({...t,state:!0,attribute:!1})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var _t,bt;!function(t){t.language="language",t.system="system",t.comma_decimal="comma_decimal",t.decimal_comma="decimal_comma",t.space_comma="space_comma",t.none="none"}(_t||(_t={})),function(t){t.language="language",t.system="system",t.am_pm="12",t.twenty_four="24"}(bt||(bt={}));var yt=function(t,e,i,s){s=s||{},i=null==i?{}:i;var n=new Event(e,{bubbles:void 0===s.bubbles||s.bubbles,cancelable:Boolean(s.cancelable),composed:void 0===s.composed||s.composed});return n.detail=i,t.dispatchEvent(n),n};function xt(t,e,i){if(e.has("config")||i)return!0;if(t.config.entity){var s=e.get("hass");return!s||s.states[t.config.entity]!==t.hass.states[t.config.entity]}return!1}const $t="1.5.6",wt=86400;var At,Et,St,Ct;!function(t){t.Heating="mdi:radiator",t.OnOff="mdi:power-socket-uk",t.Shutters="mdi:blinds",t.Lighting="mdi:lightbulb-outline"}(At||(At={})),function(t){t.Overview="OVERVIEW",t.ScheduleEdit="SCHEDULE_EDIT",t.ScheduleCopy="SCHEDULE_COPY",t.ScheduleAdd="SCHEDULE_ADD",t.ScheduleRename="SCHEDULE_RENAME"}(Et||(Et={})),function(t){t.Heating="19",t.OnOff="Off",t.Lighting="0",t.Shutters="100"}(St||(St={})),function(t){t.Heating="°C",t.OnOff="",t.Lighting="%",t.Shutters="%"}(Ct||(Ct={}));const Tt=["Heating","OnOff","Lighting","Shutters"],It=["Lighting","Shutters"],Ot=["Weekdays","Weekend"],kt=["Monday","Tuesday","Wednesday","Thursday","Friday"],Rt=["Saturday","Sunday"],Pt=kt.concat(Rt),Lt=["Sunrise","Sunset"];var Mt;!function(t){t.Sunrise="3000",t.Sunset="4000"}(Mt||(Mt={}));var Nt={version:"Version",invalid_configuration:"Invalid configuration",no_schedules:"No Schedules Found",name_required:"Name is required"},Dt={actions:{copy:"Copy",files:"Files",rename:"Rename",add:"Add",view:"View",add_schedule:"Add Schedule"},labels:{setting:"Setting",name:"Name",assigns:"Assigns",start:"Start",end:"End",to:"to"},days:{monday:"Monday",tuesday:"Tuesday",wednesday:"Wednesday",thursday:"Thursday",friday:"Friday",saturday:"Saturday",sunday:"Sunday",weekdays:"Weekdays",weekend:"Weekend",all:"All",short:{monday:"Mon",tuesday:"Tue",wednesday:"Wed",thursday:"Thu",friday:"Fri",saturday:"Sat",sunday:"Sun"}},headings:{schedule_actions:"Schedule Actions",schedule_type:"Schedule Type",schedule_id:"Schedule Id",schedule_name:"Schedule Name",schedule_assignment:"Room/Device Assignment",not_assigned:"(Not Assigned)",rename_schedule:"Rename Schedule",copy_schedule:"Copy Schedule",delete_schedule:"Delete Schedule"},helpers:{enter_new_name:"Enter the new name for the Schedule",select_copy_schedule:"Select the schedule to copy to",delete_schedule_confirm:"Are you sure you wish to delete the schedule",select_a_schedule:"Select a schedule to view",add_schedule:"Select the schedule type and enter a name for the schedule to create"}},Ht={common:Nt,wiser:Dt},Ut={version:"Déclinaison",invalid_configuration:"Configuration Invalide",no_schedules:"Aucun Programme Trouvé",name_required:"Nom est obligatoire"},zt={actions:{copy:"Copie",files:"Fichier",rename:"Renommer",add:"Ajouter",view:"Voir",add_schedule:"Ajouter un Programme"},labels:{setting:"Paramètre",name:"Nom",assigns:"Attribuers",start:"Début",end:"Fin",to:"à"},days:{monday:"Lundi",tuesday:"Mardi",wednesday:"Mercredi",thursday:"Jeudi",friday:"Vendredi",saturday:"Samedi",sunday:"Dimanche",weekdays:"Lun à Ven",weekend:"Sam et Dim",all:"Toute",short:{monday:"Lun",tuesday:"Mar",wednesday:"Mer",thursday:"Jeu",friday:"Ven",saturday:"Sam",sunday:"Dim"}},headings:{schedule_actions:"Programmer des Actions",schedule_type:"Type de Programme",schedule_id:"Numéro de Programme",schedule_name:"Nom de Programme",schedule_assignment:"Attribuer de Programme",not_assigned:"(Non Attribué)",rename_schedule:"Renommer le Programme",copy_schedule:"Copier le Programme",delete_schedule:"Supprimer le Programme"},helpers:{enter_new_name:"Entrez un nom pour le Programme",select_copy_schedule:"Sélectionnez le calendrier ci-dessous pour le copier",delete_schedule_confirm:"Êtes-vous sûr de vouloir effacer ce programme",select_a_schedule:"Sélectionner un programme à afficher",add_schedule:"Sélectionnez le type de programme et entrez un nom pour la programme à créer"}},Ft={common:Ut,wiser:zt};const Bt={en:Object.freeze({__proto__:null,common:Nt,default:Ht,wiser:Dt}),fr:Object.freeze({__proto__:null,common:Ut,default:Ft,wiser:zt})};function Vt(t,e="",i=""){const s=(localStorage.getItem("selectedLanguage")||"en").replace(/['"]+/g,"").replace("-","_");let n;try{n=t.split(".").reduce((t,e)=>t[e],Bt[s]),n||(n=t.split(".").reduce((t,e)=>t[e],Bt.en))}catch(e){try{n=t.split(".").reduce((t,e)=>t[e],Bt.en)}catch(t){n=""}}return void 0===n&&(n=t.split(".").reduce((t,e)=>t[e],Bt.en)),""!==e&&""!==i&&(n=n.replace(e,i)),n}const jt=h` - .card-header { - display: flex; - justify-content: space-between; - } - .card-header .name { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - display: flex; - } - .card-header ha-switch { - padding: 5px; - } - .card-header ha-icon-button { - position: absolute; - right: 6px; - top: 6px; - } - .card-content { - flex: 1; - } - .card-content > *:first-child { - margin-top: 0; - } - .card-content > *:last-child { - margin-bottom: 0; - } - div.text-field, - div.secondary { - color: var(--secondary-text-color); - } - .disabled { - color: var(--disabled-text-color); - } - div.header { - color: var(--secondary-text-color); - text-transform: uppercase; - font-weight: 500; - font-size: 12px; - margin: 20px 0px 0px 0px; - display: flex; - flex-direction: row; - } - div.header .switch { - text-transform: none; - font-weight: normal; - font-size: 14px; - display: flex; - flex-grow: 1; - justify-content: flex-end; - } - div.header ha-switch { - display: flex; - align-self: center; - margin: 0px 8px; - line-height: 24px; - } - mwc-button { - margin: 2px 0px; - } - mwc-button.active { - background: var(--primary-color); - --mdc-theme-primary: var(--text-primary-color); - border-radius: 4px; - } - mwc-button ha-icon { - margin-right: 11px; - } - mwc-button.warning { - --mdc-theme-primary: var(--error-color); - } - div.checkbox-container { - display: grid; - grid-template-columns: max-content 1fr max-content; - grid-template-rows: min-content; - grid-template-areas: 'checkbox slider value'; - grid-gap: 0px 10px; - } - div.checkbox-container div.checkbox { - grid-area: checkbox; - display: flex; - align-items: center; - } - div.checkbox-container div.slider { - grid-area: slider; - display: flex; - align-items: center; - } - div.checkbox-container div.value { - grid-area: value; - min-width: 40px; - display: flex; - align-items: center; - } - a { - color: var(--primary-color); - } - a:visited { - color: var(--accent-color); - } -`,Wt=(t,e)=>t.callWS({type:"wiser/schedules/types",hub:e}),qt=(t,e,i="")=>t.callWS({type:"wiser/schedules",hub:e,schedule_type:i}),Yt=(t,e,i,s)=>t.callWS({type:"wiser/schedule/id",hub:e,schedule_type:i,schedule_id:s});const Xt="a-f\\d",Gt=`#?[${Xt}]{3}[${Xt}]?`,Kt=`#?[${Xt}]{6}([${Xt}]{2})?`,Jt=new RegExp(`[^#${Xt}]`,"gi"),Zt=new RegExp(`^${Gt}$|^${Kt}$`,"i");const Qt=Math.trunc;function te(t){return null!=t}function ee(t){if(function(t){return 6===(t=String(t).replace("#","")).length&&!isNaN(Number("0x"+t))}(t)){const e=function(t,e={}){if("string"!=typeof t||Jt.test(t)||!Zt.test(t))throw new TypeError("Expected a valid hex string");let i=1;8===(t=t.replace(/^#/,"")).length&&(i=Number.parseInt(t.slice(6,8),16)/255,t=t.slice(0,6)),4===t.length&&(i=Number.parseInt(t.slice(3,4).repeat(2),16)/255,t=t.slice(0,3)),3===t.length&&(t=t[0]+t[0]+t[1]+t[1]+t[2]+t[2]);const s=Number.parseInt(t,16),n=s>>16,r=s>>8&255,o=255&s,a="number"==typeof e.alpha?e.alpha:i;if("array"===e.format)return[n,r,o,a];if("css"===e.format)return`rgb(${n} ${r} ${o}${1===a?"":` / ${Number((100*a).toFixed(2))}%`})`;return{red:n,green:r,blue:o,alpha:a}}(t);return String(e.red+","+e.green+","+e.blue+","+e.alpha)}return"100,100,100"}function ie(t,e){return getComputedStyle(t).getPropertyValue(e).trim()}function se(t,e,i){if("Unknown"==i)return"100,100,100";if("onoff"===e.toLowerCase())return ee(ie(t,"On"==i?"--green-color":"--red-color"));if(["lighting","shutters"].includes(e.toLowerCase()))return(0==(s=parseInt(i))?"50,50,50":Qt(50+1.85*s)+","+Qt(50+1.5*s)+",0")+",1";{if(-20==parseFloat(i))return"138, 138, 138";const t=45,e=-10,s=(parseFloat(i)-e)/(t-e);return 235+","+Math.floor(255*(1-s))+","+0+",1"}var s}function ne(t,e){return!e.display_only&&!!(e.admin_only&&t.user.is_admin||!e.admin_only)}function re(t,e){return 0==t.slots.length||t.slots.length-1==e?"23:59":t.slots[e+1].Time}function oe(t,e,i){return-1==e?function(t,e){const i=[...Pt.slice(Pt.indexOf(t.day)),...Pt.slice(0,Pt.indexOf(t.day))].reverse();let s;for(s of i){const t=e.ScheduleData.filter(t=>t.day==s)[0];if(t&&t.slots.length>0)return t.slots[t.slots.length-1].Setpoint}return"Unknown"}(t,i):t.slots[e].Setpoint}function ae(t){const[e,i]=t.split(":");return 3600*+e+60*+i}const le=t=>t.locale||{language:t.selectedLanguage,number_format:_t.system,time_format:bt.system},de=t=>{class e extends t{connectedCallback(){super.connectedCallback(),this.__checkSubscribed()}disconnectedCallback(){if(super.disconnectedCallback(),this.__unsubs){for(;this.__unsubs.length;){const t=this.__unsubs.pop();t instanceof Promise?t.then(t=>t()):te(t)&&t()}this.__unsubs=void 0}}updated(t){super.updated(t),t.has("hass")&&this.__checkSubscribed()}hassSubscribe(){return[]}__checkSubscribed(){void 0===this.__unsubs&&this.isConnected&&void 0!==this.hass&&(this.__unsubs=this.hassSubscribe())}}return n([gt({attribute:!1})],e.prototype,"hass",void 0),e};let ce=class extends(de(ht)){constructor(){super(...arguments),this.component_loaded=!1,this.connectionError=!1}async initialise(){return await this.isComponentLoaded()&&(this.component_loaded=!0,await this.loadData()),!0}async isComponentLoaded(){for(;!this.hass||!this.hass.config.components.includes("wiser");)await new Promise(t=>setTimeout(t,100));return!0}hassSubscribe(){return this.initialise(),[this.hass.connection.subscribeMessage(t=>this.handleUpdate(t),{type:"wiser_updated"})]}async handleUpdate(t){this.config.hub&&t.hub!=this.config.hub||"wiser_updated"!=t.event||await this.loadData()}async loadData(){this.error=void 0,await Wt(this.hass,this.config.hub).then(t=>{this.supported_schedule_types=t}).catch(t=>{this.error=t}),this.supported_schedule_types&&await qt(this.hass,this.config.hub).then(t=>{this.schedule_list=t}).catch(t=>{this.error=t})}shouldUpdate(t){return!!t.has("schedule_list")||!!xt(this,t,!1)&&(this.loadData(),!0)}render(){return this.hass&&this.config?this.schedule_list&&this.schedule_list.length>0?X` -
-
${Vt("wiser.helpers.select_a_schedule")}
- ${this.supported_schedule_types.map(t=>this.renderScheduleItemsByType(t))} -
- ${this.renderAddScheduleButton()} - `:X` ${this._showWarning(Vt("wiser.common.no_schedules"))} `:X``}_showWarning(t){return X` ${t} `}renderScheduleItemsByType(t){var e,i,s=this.schedule_list.filter(e=>e.Type===t);return this.config?.hide_hw_schedule&&(s=s.filter(e=>1e3!=e.Id)),s.length>0?X` -
-
- ${t} -
- ${"list"==(null===(e=this.config)||void 0===e?void 0:e.view_type)?this.renderScheduleList(s):(null===(i=this.config)||void 0===i?void 0:i.show_schedule_id)?s.sort((t,e)=>t.Id-e.Id).map(t=>this.renderScheduleItem(t)):s.map(t=>this.renderScheduleItem(t))} -
-
-
- `:X``}renderScheduleList(t){return t.length>0?X` - - - - - - - - - - - ${t.sort((t,e)=>t.Id-e.Id).map(t=>this.renderScheduleListItem(t))} - -
ID${Vt("wiser.labels.name")}${Vt("wiser.labels.assigns")} 
- `:X``}renderScheduleItem(t){var e,i;const s=At[t.Type];return X` - this._scheduleClick(t.Type,t.Id)} - > - - ${(null===(e=this.config)||void 0===e?void 0:e.show_schedule_id)?t.Id+" - ":null}${t.Name} - ${(null===(i=this.config)||void 0===i?void 0:i.show_badges)?X`${t.Assignments}`:null} - - `}renderScheduleListItem(t){return X` - - ${t.Id} - ${t.Name} - ${t.Assignments} - - this._scheduleClick(t.Type,t.Id)} - > - ${Vt("wiser.actions.view")} - - - - `}renderAddScheduleButton(){if(ne(this.hass,this.config))return X` -
- ${Vt("wiser.actions.add_schedule")} - -
- `}async _addScheduleClick(){const t=new CustomEvent("addScheduleClick");this.dispatchEvent(t)}_scheduleClick(t,e){const i=new CustomEvent("scheduleClick",{detail:{schedule_type:t,schedule_id:e}});this.dispatchEvent(i)}};ce.styles=h` - ${jt} - div.info-text { - margin-bottom: 10px; - } - span.button-label { - padding-left: 5px; - text-transform: uppercase; - font-weight: 500; - } - div.wrapper { - white-space: nowrap; - transition: - width 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67), - margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - overflow: auto; - display: flex; - flex-wrap: wrap; - flex-direction: row; - justify-content: flex-start; - } - div.sub-heading { - display: block; - margin: 5px 0; - } - div.sub-heading fieldset { - border: 1px solid var(--divider-color, #e8e8e8); - border-radius: 5px; - font-size: small; - } - .schedule-id { - width: 30px; - text-align: right; - } - .schedule-name { - padding-left: 5px; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - } - .schedule-assigns { - width: 15%; - text-align: center; - padding-left: 5px; - } - .schedule-action { - width: 75px; - text-align: center; - padding-left: 5px; - } - div.table-header { - display: flex; - font-weight: 500; - } - div.table-body-item { - display: flex; - align-items: center; - } - .schedule-table { - width: 100%; - font-size: 14px; - table-layout: fixed; - } - .view-button { - line-height: 32px; - cursor: pointer; - white-space: nowrap; - text-overflow: ellipsis; - margin: 3px 10px 3px 5px; - display: flex; - padding: 0px 15px 0px 10px; - color: var(--mdc-theme-primary, #6200ee); - background: var(--primary-color); - --mdc-theme-primary: var(--text-primary-color); - border-radius: 4px; - font-size: var(--material-button-font-size); - position: relative; - } - .schedule-item { - white-space: nowrap; - text-overflow: ellipsis; - padding: 5px 5px; - position: relative; - } - .badge { - font-size: 13px; - font-weight: 400; - position: absolute; - min-width: 20px; - box-sizing: border-box; - top: 0px; - right: 0px; - background-color: var(--accent-color); - border-radius: 50%; - line-height: 20px; - text-align: center; - padding: 0px 6px; - color: var(--text-accent-color, var(--text-primary-color)); - } - `,n([gt({attribute:!1})],ce.prototype,"config",void 0),n([gt({attribute:!1})],ce.prototype,"schedule_list",void 0),n([gt({attribute:!1})],ce.prototype,"component_loaded",void 0),ce=n([ut("wiser-schedule-list-card")],ce);let he=class extends ht{async showDialog(t){this._params=t,await this.updateComplete}async closeDialog(){this._params&&this._params.cancel(),this._params=void 0}render(){return this._params?X` - -
- ${Vt("wiser.helpers.delete_schedule_confirm")+" "+this._params.name+"?"} -
Note : UI can take 5-10 seconds to reflect change
-
- - - ${this.hass.localize("ui.common.cancel")} - - - ${this.hass.localize("ui.common.delete")} - - -
- `:X``}confirmClick(){this._params.confirm()}cancelClick(){this._params.cancel()}static get styles(){return h` - div.wrapper { - color: var(--primary-text-color); - } - div.notice { - margin-top: 12px; - font-size: 0.9em; - } - `}};n([gt({attribute:!1})],he.prototype,"hass",void 0),n([vt()],he.prototype,"_params",void 0),he=n([ut("wiser-dialog-delete-confirm")],he);var pe=Object.freeze({__proto__:null,get DialogDeleteConfirm(){return he}});function ue(t){if(t.match(/^([0-9:]+)$/)){const e=t.split(":").map(Number);return 3600*e[0]+60*e[1]+(e[2]||0)}const e=new Date(t);return 3600*e.getHours()+60*e.getMinutes()+e.getSeconds()}function me(t){const e=Math.floor(t/3600);t-=3600*e;const i=Math.floor(t/60);t-=60*i;const s=Math.round(t);return String(e%24).padStart(2,"0")+":"+String(i).padStart(2,"0")+":"+String(s).padStart(2,"0")}function fe(t){const e=Math.floor(t/3600);t-=3600*e;const i=Math.floor(t/60);return String(e%24).padStart(2,"0")+":"+String(i).padStart(2,"0")}function ge(t,e,i={wrapAround:!0}){let s=t>=0?Math.floor(t/3600):Math.ceil(t/3600),n=Math.floor((t-3600*s)/60);n%e!=0&&(n=Math.round(n/e)*e),n>=60?(s++,n-=60):n<0&&(s--,n+=60),i.wrapAround&&(s>=24?s-=24:s<0&&(s+=24));const r=3600*s+60*n;if(i.maxHours){if(r>3600*i.maxHours)return 3600*i.maxHours;if(r<3600*-i.maxHours)return 3600*-i.maxHours}return r}var ve=/d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|Z|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g,_e=/\[([^]*?)\]/gm;function be(t,e){for(var i=[],s=0,n=t.length;s3?0:(t-t%10!=10?1:0)*t%10]}}),Ee=function(t,e){for(void 0===e&&(e=2),t=String(t);t.length0?"-":"+")+Ee(100*Math.floor(Math.abs(e)/60)+Math.abs(e)%60,4)},Z:function(t){var e=t.getTimezoneOffset();return(e>0?"-":"+")+Ee(Math.floor(Math.abs(e)/60),2)+":"+Ee(Math.abs(e)%60,2)}},Ce={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"},Te=function(t,e,i){if(void 0===e&&(e=Ce.default),void 0===i&&(i={}),"number"==typeof t&&(t=new Date(t)),"[object Date]"!==Object.prototype.toString.call(t)||isNaN(t.getTime()))throw new Error("Invalid Date pass to format");var s=[];e=(e=Ce[e]||e).replace(_e,function(t,e){return s.push(e),"@@@"});var n=ye(ye({},Ae),i);return(e=e.replace(ve,function(e){return Se[e](t,n)})).replace(/@@@/g,function(){return s.shift()})};const Ie=t=>{if(t.time_format===bt.language||t.time_format===bt.system){const e=t.time_format===bt.language?t.language:void 0,i=(new Date).toLocaleString(e);return i.includes("AM")||i.includes("PM")}return t.time_format===bt.am_pm};function Oe(t,e,i){return i===bt.am_pm||!i&&e.time_format===bt.am_pm?Te(t,"h:mm A"):i===bt.twenty_four||!i&&e.time_format===bt.twenty_four?Te(t,"shortTime"):(()=>{try{(new Date).toLocaleTimeString("i")}catch(t){return"RangeError"===t.name}return!1})()?t.toLocaleTimeString(e.language,{hour:"numeric",minute:"2-digit",hour12:Ie(e)}):Ie(e)?Oe(t,e,bt.am_pm):Oe(t,e,bt.twenty_four)}function ke(t){const e=new Date,i=(t||"").match(/^([0-9]{4})-([0-9]{2})-([0-9]{2})/);null!==i&&e.setFullYear(Number(i[1]),Number(i[2])-1,Number(i[3]));const s=(t||"").match(/([0-9]{2}):([0-9]{2})(:([0-9]{2}))?$/);return null!==s&&e.setHours(Number(s[1]),Number(s[2]),s.length>4?Number(s[4]):e.getSeconds()),e}let Re=class extends ht{constructor(){super(...arguments),this.min=0,this.max=255,this.step=1,this.scaleFactor=1,this.unit="",this.optional=!1,this.disabled=!1,this._displayedValue=0}set value(t){t=isNaN(t)?this.min:this._roundedValue(t/this.scaleFactor),this._displayedValue=t}render(){return X` -
-
${this.getCheckbox()}
-
${this.getSlider()}
-
${this._displayedValue}${this.unit}
-
- `}getSlider(){return this.disabled?X` - - `:X` - - `}getCheckbox(){return this.optional?X` `:X``}_toggleChecked(t){const e=t.target.checked;this.disabled=!e;const i=this.disabled?null:this._scaledValue(this._displayedValue);yt(this,"value-changed",{value:i})}_updateValue(t){let e=Number(t.target.value);this._displayedValue=e,e=this._scaledValue(this._displayedValue),yt(this,"value-changed",{value:e})}_roundedValue(t){return t=Math.round(t/this.step)*this.step,(t=parseFloat(t.toPrecision(12)))>this.max?t=this.max:t${this.renderTimes()} `}renderTimes(){if(this.hass){const t=parseFloat(getComputedStyle(this).getPropertyValue("width"))||460,e=[1,2,3,4,6,8,12],i=Ie(le(this.hass))?55:40;let s=Math.ceil(24/(t/i));for(;!e.includes(s);)s++;const n=[0,...Array.from(Array(24/s-1).keys()).map(t=>(t+1)*s),24];return n.map(t=>{const e=0==t||24==t,i=e?s/48*100:s/24*100;return X` -
- ${e?"":Oe(ke(me(3600*t)),le(this.hass))} -
- `})}return X``}static get styles(){return h` - :host { - display: block; - max-width: 100%; - overflow: hidden; - } - div.outer { - width: 100%; - overflow-x: hidden; - overflow-y: hidden; - border-radius: 5px; - } - div.time-wrapper { - white-space: nowrap; - transition: - width 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67), - margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - overflow: auto; - } - div.time-wrapper div { - float: left; - display: flex; - position: relative; - height: 25px; - line-height: 25px; - font-size: 12px; - text-align: center; - align-content: center; - align-items: center; - justify-content: center; - } - div.time-wrapper div.time:before { - content: ' '; - background: var(--disabled-text-color); - position: absolute; - left: 0px; - top: 0px; - width: 1px; - height: 5px; - margin-left: 50%; - margin-top: 0px; - } - @keyframes fadeIn { - 99% { - visibility: hidden; - } - 100% { - visibility: visible; - } - } - `}};n([gt({attribute:!1})],Pe.prototype,"hass",void 0),Pe=n([ut("wiser-time-bar")],Pe);let Le=class extends ht{constructor(){super(),this.editMode=!1,this._activeSlot=-99,this._activeDay="",this._show_short_days=!1,this.schedule_type=Tt[0],this.activeMarker=0,this.isDragging=!1,this.currentTime=0,this.timer=0,this.timeout=0,this.zoomFactor=1,this.rangeMin=0,this.rangeMax=wt,this.stepSize=5,this.initialise()}async initialise(){return this.schedule&&(this.schedule_type=this.schedule.Type),!0}shouldUpdate(){return this.editMode||(this._activeSlot=-99,this._activeDay=""),!0}render(){const t=parseFloat(getComputedStyle(this).getPropertyValue("width"));return this._show_short_days=t<500,this.hass&&this.config&&this.suntimes&&this.schedule?X` -
- ${Pt.map(t=>this.renderDay(this.schedule.ScheduleData.filter(e=>e.day==t)[0]?this.schedule.ScheduleData.filter(e=>e.day==t)[0]:{day:t,slots:[]}))} -
-
 
- -
-
- - ${this.editMode&&It.includes(this.schedule_type)?this.renderSpecialTimeButtons():null} - ${this.editMode?this.renderAddDeleteButtons():null} - ${this.editMode?this.renderSetPointControl():null} - ${this.editMode?this.renderCopyDay():null} - `:X``}renderDay(t){return X` -
- ${this.computeDayLabel(t.day)} -
-
- ${t.slots.length>0?t.slots.map((e,i)=>this.renderSlot(e,i,t)):this.renderEmptySlot({Time:"23:59",Setpoint:"0",SpecialTime:""},-1,t,!0)} -
-
-
- `}renderEmptySlot(t,e,i,s=!1){const n="00:00",r=t.Time,o=oe(i,e,this.schedule),a=parseFloat(getComputedStyle(this).getPropertyValue("width")),l=this.config.theme_colors?"rgba(var(--rgb-primary-color), 0.7)":"rgba("+se(this,this.schedule_type,o)+")",d=(ae(r)-ae(n))/wt*100,c=Vt("wiser.labels.start")+" - "+n+"\n"+Vt("wiser.labels.end")+" - "+r+"\n"+Vt("wiser.labels.setting")+" - "+this.computeSetpointLabel(o),h=d/100*a<35?"setpoint rotate":"setpoint";return X` - - `}renderSlot(t,e,i){const s=t.Time,n=re(i,e),r=t.Setpoint,o=(ae(n)-ae(s))/wt*100,a=this.config.theme_colors?"rgba(var(--rgb-primary-color), 0.7)":"rgba("+se(this,this.schedule_type,r)+")",l=o/100*parseFloat(getComputedStyle(this).getPropertyValue("width"))<35?"setpoint rotate":"setpoint",d=Vt("wiser.labels.start")+" - "+(t.SpecialTime?t.SpecialTime+" ("+s+")":s)+"\n"+Vt("wiser.labels.end")+" - "+n+"\n"+Vt("wiser.labels.setting")+" - "+this.computeSetpointLabel(r);return X` - ${0==e&&"00:00"!=s&&"0:00"!=s?this.renderEmptySlot(t,-1,i,!1):""} -
-
- ${this.computeSetpointLabel(r)} -
- ${this._activeSlot==e&&this._activeDay==i.day?X` -
-
- - -
-
- `:""} - ${this._activeSlot==e&&this._activeDay==i.day?this.renderTooltip(i,e):""} -
- `}renderSpecialTimeButtons(){const t=this._activeDay?this.schedule.ScheduleData.filter(t=>t.day==this._activeDay)[0].slots[this._activeSlot]:null;return X` -
-
 
-
-
Set Special Time
- - - Sunrise - - - - Sunset - -
-
- `}renderAddDeleteButtons(){let t=0;return this.schedule.ScheduleData.filter(t=>t.day==this._activeDay).length>0&&(t=this._activeDay?this.schedule.ScheduleData.filter(t=>t.day==this._activeDay)[0].slots.length:0),X` -
-
 
-
- =24} - > - - ${Vt("wiser.actions.add")} - - - - ${this.hass.localize("ui.common.delete")} - -
-
- `}renderSetPointControl(){let t={};return this.editMode?(this.schedule.ScheduleData.filter(t=>t.day==this._activeDay).length>0&&(t=this._activeDay?this.schedule.ScheduleData.filter(t=>t.day==this._activeDay)[0].slots:{}),"Heating"==this.schedule_type?X` -
-
 
-
-
${this._show_short_days?"Temp":"Temperature"}
-
-
- this._updateSetPoint("-20")} - > - - =0?parseFloat(t[this._activeSlot].Setpoint):0} - unit="°C" - .optional=${!1} - .disabled=${this._activeSlot<0} - @value-changed=${t=>{this._updateSetPoint(Number(t.detail.value))}} - > - -
-
-
- `:"OnOff"==this.schedule_type?X` -
-
 
-
-
-
State
-
- Off - =0&&"On"==t[this._activeSlot].Setpoint} - .disabled=${this._activeSlot<0} - @change=${()=>"On"==t[this._activeSlot].Setpoint?this._updateSetPoint("Off"):this._updateSetPoint("On")} - > - - On -
-
-
-
- `:["Lighting","Shutters"].includes(this.schedule_type)?X` -
-
 
-
-
Level
-
- =0?parseFloat(t[this._activeSlot].Setpoint):0} - unit="%" - .optional=${!1} - .disabled=${this._activeSlot<0} - @value-changed=${t=>{this._updateSetPoint(Number(t.detail.value))}} - > - -
-
-
- `:X``):X``}renderCopyDay(){return X` -
-
 
-
-
- ${this._activeDay?Vt("wiser.actions.copy")+" "+Vt("wiser.days."+this._activeDay.toLowerCase())+" "+Vt("wiser.labels.to"):Vt("wiser.actions.copy")+" "+Vt("wiser.labels.to")} -
-
- ${Pt.concat(Ot).concat("All").map(t=>this.renderCopyToButton(t))} -
-
-
- `}renderCopyToButton(t){return X` - - ${Pt.includes(t)&&this._show_short_days?Vt("wiser.days.short."+t.toLowerCase()):Vt("wiser.days."+t.toLowerCase())} - - `}renderTooltip(t,e){const i=t.slots,s=Lt.includes(i[e].SpecialTime);return X` -
-
- ${s?X` - - ${i[e].SpecialTime} - `:Oe(ke(me(ue(i[e].Time))),le(this.hass))} -
-
- `}_slotClick(t){const e=t.target.parentElement.parentElement;if(e.id){const t=e.id.split("|")[0],i=e.id.split("|")[1];i!=this._activeSlot||t!=this._activeDay?(this._activeSlot=parseInt(i),this._activeDay=t):(this._activeSlot=-99,this._activeDay="");const s=new CustomEvent("slotClicked",{detail:{day:this._activeDay,slot:this._activeSlot}});this.dispatchEvent(s)}}_copyDay(t){const e=t.target,i=JSON.stringify(this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots);Pt.includes(e.id)?this.schedule.ScheduleData[Pt.indexOf(e.id)].slots=JSON.parse(i):e.id==Ot[0]?kt.map(t=>{this.schedule.ScheduleData[Pt.indexOf(t)].slots=JSON.parse(i)}):e.id==Ot[1]?Rt.map(t=>{this.schedule.ScheduleData[Pt.indexOf(t)].slots=JSON.parse(i)}):"All"==e.id&&Pt.map(t=>{this.schedule.ScheduleData[Pt.indexOf(t)].slots=JSON.parse(i)}),this.requestUpdate()}_updateSetPoint(t){this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots=Object.assign(this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots,{[this._activeSlot]:Object.assign(Object.assign({},this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots[this._activeSlot]),{Setpoint:t})});const e=new CustomEvent("scheduleChanged",{detail:{schedule:this.schedule}});this.dispatchEvent(e),this.requestUpdate()}getSunTime(t,e){return e==Lt[0]?this.suntimes.Sunrises[Pt.indexOf(t)].time:this.suntimes.Sunsets[Pt.indexOf(t)].time}convertScheduleDay(t){const e=t.slots.map(e=>Lt.includes(e.SpecialTime)?{Time:this.getSunTime(t.day,e.SpecialTime),Setpoint:e.Setpoint,SpecialTime:e.SpecialTime}:{Time:e.Time,Setpoint:e.Setpoint,SpecialTime:e.SpecialTime}).sort((t,e)=>parseInt(t.Time.replace(":",""))JSON.stringify(t))),s=Array.from(i).map(t=>JSON.parse(t));return{day:t.day,slots:s}}_setSpecialTime(t){const e=t.target.id.replace(/\w\S*/g,t=>t.charAt(0).toUpperCase()+t.substr(1).toLowerCase());this._activeDay&&this._activeSlot>=0&&(this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots[this._activeSlot].SpecialTime!=e&&(this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots=Object.assign(this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots,{[this._activeSlot]:Object.assign(Object.assign({},this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots[this._activeSlot]),{SpecialTime:e,Time:this.getSunTime(this._activeDay,e)})})),this.schedule.ScheduleData[Pt.indexOf(this._activeDay)]=this.convertScheduleDay(this.schedule.ScheduleData[Pt.indexOf(this._activeDay)]),this.schedule.ScheduleData[Pt.indexOf(this._activeDay)].slots.forEach((t,i)=>{t.SpecialTime==e&&(this._activeSlot=i)}),this.requestUpdate())}_addSlot(){if(this._activeSlot<-1)return;const t=Pt.indexOf(this._activeDay);if(this._activeSlot<0)this.schedule.ScheduleData[t].slots=[{Time:fe(ue("06:00")),Setpoint:St[this.schedule_type],SpecialTime:""}],this._activeSlot=0;else{const e=this.schedule.ScheduleData[t].slots[this._activeSlot];let i=ue(e.Time),s=ue(re(this.schedule.ScheduleData[t],this._activeSlot));st.day==this._activeDay)[0].slots;const s=t.target;let n=s;for(;!n.classList.contains("outer");)n=n.parentElement;const r=parseFloat(getComputedStyle(n).getPropertyValue("width")),o=wt/(this.rangeMax-this.rangeMin)*r,a=-(-this.rangeMin/(this.rangeMax-this.rangeMin)*r)/o*wt;let l=s;for(;!l.classList.contains("slot");)l=l.parentElement;const d=l,c=Number(d.getAttribute("slot")),h=c>0?ue(i[c-1].Time)+60*this.stepSize:0,p=c{let s;s="undefined"!=typeof TouchEvent&&t instanceof TouchEvent?t.changedTouches[0].pageX:t.pageX;let n=s-u.left;n>r-1&&(n=r-1),n<-18&&(n=-18);let l=Math.round(n/o*wt+a);lp&&(l=p),this.currentTime=l,l=Math.round(l)>=wt?wt:ge(l,this.stepSize);const d=fe(l);d!=re(this.schedule.ScheduleData[e],c)&&(i=Object.assign(i,{[c]:Object.assign(Object.assign({},i[c]),{Time:d,SpecialTime:""})}),this.requestUpdate())};const f=()=>{window.removeEventListener("mousemove",m),window.removeEventListener("touchmove",m),window.removeEventListener("mouseup",f),window.removeEventListener("touchend",f),window.removeEventListener("blur",f),m=()=>{},setTimeout(()=>{this.isDragging=!1},100),s.blur();const t=new CustomEvent("scheduleChanged",{detail:{schedule:this.schedule}});this.dispatchEvent(t)};window.addEventListener("mouseup",f),window.addEventListener("touchend",f),window.addEventListener("blur",f),window.addEventListener("mousemove",m),window.addEventListener("touchmove",m)}computeDayLabel(t){return X` -
- ${this._show_short_days?Vt("wiser.days.short."+t.toLowerCase()):Vt("wiser.days."+t.toLowerCase())} -
- `}computeSetpointLabel(t){return"Unknown"==t?t:"Heating"==this.schedule_type&&-20==t?"Off":t+Ct[this.schedule_type]}static get styles(){return h` - :host { - display: block; - max-width: 100%; - } - div.outer { - width: 100%; - overflow: visible; - } - div.wrapper, - div.time-wrapper { - white-space: nowrap; - transition: - width 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67), - margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - display: flex; - } - div.sub-section { - display: flex; - justify-content: center; - width: 100%; - } - .special-times { - justify-content: flex-end; - line-height: 40px; - padding: 0 5px; - text-transform: uppercase; - font-size: small; - } - .section-header { - color: var(--material-body-text-color, #000); - text-transform: uppercase; - font-weight: 500; - font-size: var(--material-small-font-size); - padding: 5px 10px; - } - .slot { - float: left; - background: rgba(var(--rgb-primary-color), 0.7); - height: 60px; - box-sizing: border-box; - transition: background 0.1s cubic-bezier(0.17, 0.67, 0.83, 0.67); - position: relative; - height: 40px; - line-height: 40px; - font-size: 10px; - text-align: center; - } - .slot:first-child { - border-radius: 5px 0 0 5px; - } - .slot:last-child { - border-radius: 0 5px 5px 0; - } - .slot:only-child { - border-radius: 5px; - } - .slot.previous { - cursor: default; - } - .slot.selected { - background: rgba(52, 143, 255, 1); - } - .setpoint { - z-index: 3; - position: relative; - text-align: center; - } - .slotoverlay { - position: absolute; - display: hidden; - width: 100%; - height: 100%; - top: 0; - left: 0; - right: 0; - bottom: 0; - /*background-color: rgba(0,0,0,0.5);*/ - z-index: 2; - } - div.slot.selectable { - cursor: pointer; - } - - .previous { - display: block; - background: repeating-linear-gradient( - 135deg, - rgba(0, 0, 0, 0), - rgba(0, 0, 0, 0) 5px, - rgba(255, 255, 255, 0.2) 5px, - rgba(255, 255, 255, 0.2) 10px - ); - border-radius: 5px 0 0 5px; - } - .previous.selected { - border: 2px solid var(--primary-color); - } - .previous.selected.theme-colors { - border: 2px solid var(--warning-color); - } - .wrapper.selectable .slot:hover { - background: rgba(var(--rgb-primary-color), 0.85); - } - .slot:not(:first-child) { - border-left: 1px solid var(--card-background-color); - } - .slot.active { - background: rgba(var(--rgb-accent-color), 0.7); - } - .slot.noborder { - border: none; - } - .wrapper.selectable .slot.active:hover { - background: rgba(var(--rgb-accent-color), 0.85); - } - .wrapper .day.short { - max-width: 50px; - } - .wrapper .day { - line-height: 42px; - float: left; - width: 20%; - max-width: 100px; - } - .wrapper .schedule { - position: relative; - width: 100%; - height: 40px; - border-radius: 5px; - overflow: auto; - margin-bottom: 2px; - display: flex; - } - .setpoint.rotate { - z-index: 3; - transform: rotate(-90deg); - position: absolute; - top: 20px; - height: 0px !important; - width: 100%; - overflow: visible !important; - } - div.time-wrapper div { - float: left; - display: flex; - position: relative; - height: 25px; - line-height: 25px; - font-size: 12px; - text-align: center; - align-content: center; - align-items: center; - justify-content: center; - } - div.time-wrapper div.time:before { - content: ' '; - background: var(--disabled-text-color); - position: absolute; - left: 0px; - top: 0px; - width: 1px; - height: 5px; - margin-left: 50%; - margin-top: 0px; - } - .slot span { - font-size: 10px; - color: var(--text-primary-color); - height: 100%; - display: flex; - align-content: center; - align-items: center; - justify-content: center; - transition: margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - word-break: nowrap; - white-space: normal; - overflow: hidden; - line-height: 1em; - } - div.handle { - display: flex; - height: 100%; - width: 36px; - margin-left: -19px; - margin-bottom: -60px; - align-content: center; - align-items: center; - justify-content: center; - } - div.button-holder { - background: var(--card-background-color); - border-radius: 50%; - width: 24px; - height: 24px; - display: flex; - visibility: hidden; - animation: 0.2s fadeIn; - animation-fill-mode: forwards; - z-index: 5; - } - div.tooltip-container { - position: absolute; - margin-top: -12px; - margin-left: -22px; - width: 40px; - height: 0px; - text-align: center; - line-height: 35px; - z-index: 3; - top: -26px; - } - - div.tooltip-container.visible { - display: block; - } - div.tooltip-container.left { - margin-left: -80px; - text-align: right; - } - div.tooltip-container.right { - margin-left: 0px; - text-align: left; - } - div.tooltip { - display: inline-flex; - margin: 0px auto; - border-radius: 5px; - color: var(--text-primary-color); - font-size: 1em; - padding: 0px 5px; - text-align: center; - line-height: 20px; - z-index: 5; - transition: all 0.1s ease-in; - transform-origin: center bottom; - --tooltip-color: var(--primary-color); - background: var(--primary-color); - } - div.tooltip.active { - --tooltip-color: rgba(var(--rgb-accent-color), 0.7); - } - div.tooltip-container.left div.tooltip { - transform-origin: right bottom; - } - div.tooltip-container.right div.tooltip { - transform-origin: left bottom; - } - div.tooltip-container.center div.tooltip:before { - content: ' '; - width: 0px; - height: 0px; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-top: 10px solid var(--primary-color); - position: absolute; - margin-top: 25px; - margin-left: calc(50% - 6px); - top: 0px; - left: 0px; - } - div.tooltip-container.left div.tooltip:before { - content: ' '; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - border-right: 8px solid var(--tooltip-color); - opacity: 1; - position: absolute; - margin-top: 15px; - margin-left: calc(100% - 8px); - left: 0px; - top: 0px; - width: 0px; - height: 0px; - } - div.tooltip-container.right div.tooltip:before { - content: ' '; - border-top: 10px solid transparent; - border-bottom: 10px solid transparent; - border-left: 8px solid var(--tooltip-color); - opacity: 1; - position: absolute; - margin-top: 15px; - margin-left: 0px; - left: 0px; - top: 0px; - width: 0px; - height: 0px; - } - div.tooltip ha-icon { - --mdc-icon-size: 18px; - } - - mwc-button.state-button { - padding: 0px 10px; - margin: 0 2px; - max-width: 100px; - } - - mwc-button.warning { - --mdc-theme-primary: var(--error-color); - } - mwc-button.warning .mdc-button .mdc-button__label { - color: var(--primary-text-color); - } - mwc-button.right { - float: right; - } - ha-icon-button { - --mdc-icon-button-size: 36px; - margin-top: -6px; - margin-left: -6px; - } - @keyframes fadeIn { - 99% { - visibility: hidden; - } - 100% { - visibility: visible; - } - } - - mwc-button ha-icon { - margin-right: 2px; - } - mwc-button.active { - background: var(--primary-color); - --mdc-theme-primary: var(--text-primary-color); - border-radius: 4px; - } - ha-icon-button.set-off-button { - margin-left: 0px; - } - .sub-heading { - padding: 0px 10px 0px 10px; - font-weight: 500; - } - `}};n([gt({attribute:!1})],Le.prototype,"hass",void 0),n([gt({attribute:!1})],Le.prototype,"config",void 0),n([gt({attribute:!1})],Le.prototype,"schedule",void 0),n([gt({attribute:!1})],Le.prototype,"suntimes",void 0),n([gt({attribute:!1})],Le.prototype,"editMode",void 0),n([vt()],Le.prototype,"_activeSlot",void 0),n([vt()],Le.prototype,"_activeDay",void 0),n([vt()],Le.prototype,"_show_short_days",void 0),n([vt()],Le.prototype,"rangeMin",void 0),n([vt()],Le.prototype,"rangeMax",void 0),n([vt()],Le.prototype,"stepSize",void 0),n([function(t){return(e,i)=>{const s="function"==typeof e?e:e[i];Object.assign(s,t)}}({passive:!0})],Le.prototype,"_handleTouchStart",null),Le=n([ut("wiser-schedule-slot-editor")],Le);let Me=class extends ht{async showDialog(t){this._params=t,await this.updateComplete}async closeDialog(){this._params=void 0}render(){return this._params?X` - -
${this._params.error||""}
- - ${this.hass.localize("ui.dialogs.generic.ok")} - -
- `:X``}static get styles(){return h` - div.wrapper { - color: var(--primary-text-color); - } - `}};n([gt({attribute:!1})],Me.prototype,"hass",void 0),n([vt()],Me.prototype,"_params",void 0),Me=n([ut("wiser-dialog-error")],Me);var Ne=Object.freeze({__proto__:null,get DialogError(){return Me}});let De=class extends(de(ht)){constructor(){var t;super(...arguments),this.schedule_id=0,this.use_heat_colors=!0,this.rooms=[],this.entities=[],this._activeSlot=null,this._activeDay=null,this.editMode=!1,this._current_user=null===(t=this.hass)||void 0===t?void 0:t.user,this._assigning_in_progress=0,this._save_in_progress=!1,this.stepSize=5}async initialise(){return await this._isComponentLoaded()&&(this.component_loaded=!0,await this.loadData()),!0}hassSubscribe(){return this.initialise(),[this.hass.connection.subscribeMessage(t=>this.handleUpdate(t),{type:"wiser_updated"})]}async handleUpdate(t){this.config.hub&&t.hub!=this.config.hub||"wiser_updated"!=t.event||await this.loadData()}async _isComponentLoaded(){for(;!this.hass&&!this.config&&!this.hass.config.components.includes("wiser");)await new Promise(t=>setTimeout(t,100));return!0}getSunTime(t,e){return e==Lt[0]?this.suntimes.Sunrises[Pt.indexOf(t)].time:this.suntimes.Sunsets[Pt.indexOf(t)].time}convertLoadedSchedule(t){const e=t.ScheduleData.map(t=>this.convertLoadedScheduleDay(t));return t.ScheduleData=e,t}convertLoadedScheduleDay(t){const e=t.slots.map(e=>Lt.includes(e.Time)?{Time:this.getSunTime(t.day,e.Time),Setpoint:e.Setpoint,SpecialTime:e.Time}:{Time:e.Time,Setpoint:e.Setpoint,SpecialTime:""}).sort((t,e)=>parseInt(t.Time.replace(":",""))JSON.stringify(t))),s=Array.from(i).map(t=>JSON.parse(t));return{day:t.day,slots:s}}convertScheduleForSaving(t){const e=t.ScheduleData.map(t=>this.convertScheduleDayForSaving(t));return t.ScheduleData=e,t}convertScheduleDayForSaving(t){const e=t.slots.map(t=>Lt.includes(t.SpecialTime)?{Time:t.SpecialTime,Setpoint:t.Setpoint,SpecialTime:t.SpecialTime}:{Time:t.Time,Setpoint:t.Setpoint,SpecialTime:""}).sort((t,e)=>t.Time.replace(":","")JSON.stringify(t))),s=Array.from(i).map(t=>JSON.parse(t));return{day:t.day,slots:s}}async loadData(){var t,e;this.error=void 0,this.schedule_type&&this.schedule_id&&!this.editMode&&(await(t=this.hass,e=this.config.hub,t.callWS({type:"wiser/suntimes",hub:e})).then(t=>{this.suntimes=t}).catch(t=>{this.error=t}),await Yt(this.hass,this.config.hub,this.schedule_type,this.schedule_id).then(t=>{this.schedule=this.convertLoadedSchedule(t)}).catch(t=>{this.schedule=void 0,this.error=t}),this.schedule&&await this.get_entity_list(this.hass,this.config.hub).then(t=>{this.entities=t}).catch(t=>{this.error=t}))}async get_entity_list(t,e){return"heating"==this.schedule.Type.toLowerCase()?await((t,e)=>t.callWS({type:"wiser/rooms",hub:e}))(t,e):await((t,e,i)=>t.callWS({type:"wiser/devices",device_type:i,hub:e}))(t,e,this.schedule.SubType)}shouldUpdate(t){return t.has("schedule_id")||t.has("editMode")?(this.loadData(),!0):!!(t.has("schedule")||t.has("entities")||t.has("editMode")||t.has("_assigning_in_progress")||t.has("_save_in_progress")||t.has("error")&&te(this.error))}render(){return this.hass&&this.config&&this.component_loaded?te(this.error)?X` ${this.error.message} `:this.schedule&&this.entities&&this.suntimes?X` -
-
- ${Vt("wiser.headings.schedule_type")}: ${this.schedule.SubType} -
-
- ${Vt("wiser.headings.schedule_id")}: ${this.schedule.Id} -
-
- ${Vt("wiser.headings.schedule_name")}: ${this.schedule.Name} -
-
${this.editMode?"Edit Mode":null}
-
-
-
- -
-
-
- ${this.renderScheduleAssignment(this.entities,this.schedule.Assignments)} - ${this.renderScheduleActionButtonSection()} -
- ${this.renderCardActions()} - - `:X``:X``}renderScheduleAssignment(t,e){if(this.schedule&&!this.editMode)return ne(this.hass,this.config)?X` -
-
${Vt("wiser.headings.schedule_assignment")}
- ${t.length>0?t.map(t=>this.renderEntityButton(t,e.map(function(t){return t.name}).includes(t.Name))):X`
(No Assignable Devices)
`} -
- `:X` -
-
${Vt("wiser.headings.schedule_assignment")}
- ${e.length>0?t.filter(t=>e.map(function(t){return t.name}).includes(t.Name)).map(t=>this.renderEntityLabel(t)):X`${Vt("wiser.headings.not_assigned")}`} -
- `}renderEntityButton(t,e){return X` - - ${this._assigning_in_progress==t.Id?X``:null} - ${t.Name} - - `}renderScheduleActionButtonSection(){if(this.schedule&&!this.editMode&&ne(this.hass,this.config))return X` -
-
${Vt("wiser.headings.schedule_actions")}
-
- ${this.renderScheduleRenameButton()} ${this.renderEditScheduleButton()} ${this.renderCopyScheduleButton()} - ${this.renderDeleteScheduleButton()} -
-
- `}renderEntityLabel(t){return X` ${t.Name} `}renderCardActions(){if(!this.config.selected_schedule||this.editMode)return X` -
-
- ${this.editMode?null:this.renderBackButton()} ${this.editMode?this.renderCancelButton():null} - ${this.editMode?this.renderSaveScheduleButton():null} -
-
- `}renderBackButton(){return X` - ${this.hass.localize("ui.common.back")} - `}renderCancelButton(){return X` - ${this.hass.localize("ui.common.cancel")} - `}renderScheduleRenameButton(){return X` - - ${Vt("wiser.actions.rename")} - - `}renderDeleteScheduleButton(){return X` - - ${this.hass.localize("ui.common.delete")} - - `}renderCopyScheduleButton(){return X` - - ${Vt("wiser.actions.copy")} - - `}renderEditScheduleButton(){return X` - - ${this.hass.localize("ui.common.edit")} - - `}renderFilesScheduleButton(){return X` - - ${Vt("wiser.actions.files")} - - `}renderSaveScheduleButton(){if(ne(this.hass,this.config))return X` - - ${this._save_in_progress?X``:this.hass.localize("ui.common.save")} - - `}async entityAssignmentClick(t){const e=t.target;this._assigning_in_progress=parseInt(e.id),ne(this.hass,this.config)&&await((t,e,i,s,n,r=!1)=>t.callWS({type:"wiser/schedule/assign",hub:e,schedule_type:i,schedule_id:s,entity_id:n,remove:r}))(this.hass,this.config.hub,this.schedule_type,this.schedule_id,e.id,e.classList.contains("active")),this._assigning_in_progress=0}backClick(){const t=new CustomEvent("backClick");this.dispatchEvent(t)}editClick(){this._tempSchedule=this.schedule,this.editMode=!this.editMode}copyClick(){const t=new CustomEvent("copyClick");this.dispatchEvent(t)}filesClick(){const t=new CustomEvent("filesClick");this.dispatchEvent(t)}async renameScheduleClick(){const t=new CustomEvent("renameClick");this.dispatchEvent(t)}async deleteClick(t){const e=t.target;if(await new Promise(t=>{yt(e,"show-dialog",{dialogTag:"wiser-dialog-delete-confirm",dialogImport:()=>Promise.resolve().then(function(){return pe}),dialogParams:{cancel:()=>{t(!1)},confirm:()=>{t(!0)},name:this.schedule.Name}})})){this.schedule_id=0,await(i=this.hass,s=this.config.hub,n=this.schedule.Type,r=this.schedule.Id,i.callWS({type:"wiser/schedule/delete",hub:s,schedule_type:n,schedule_id:r}));const t=new CustomEvent("schedule-deleted",{bubbles:!0,composed:!0});this.dispatchEvent(t)}var i,s,n,r}cancelClick(){this.editMode=!1}validateSchedule(t){return t.ScheduleData.map(t=>t.slots).map(t=>t.length>0).includes(!0)}async saveClick(){if(this._save_in_progress=!0,this.validateSchedule(this._tempSchedule))It.includes(this.schedule_type)&&(this._tempSchedule=await this.convertScheduleForSaving(this._tempSchedule)),await(t=this.hass,e=this.config.hub,i=this.schedule_type,s=this.schedule_id,n=this._tempSchedule,t.callWS({type:"wiser/schedule/save",hub:e,schedule_type:i,schedule_id:s,schedule:n})),this.editMode=!1;else{const t=X`The schedule you are trying to save has no time slots.`;yt(this,"show-dialog",{dialogTag:"wiser-dialog-error",dialogImport:()=>Promise.resolve().then(function(){return Ne}),dialogParams:{title:"Error Saving Schedule",error:t}})}var t,e,i,s,n;this._save_in_progress=!1}scheduleChanged(t){this._tempSchedule=t.detail.schedule,this.render()}static get styles(){return h` - :host { - display: block; - max-width: 100%; - } - div.outer { - width: 100%; - overflow-x: hidden; - overflow-y: hidden; - border-radius: 5px; - } - div.wrapper, - div.time-wrapper { - white-space: nowrap; - transition: - width 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67), - margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - } - div.assignment-wrapper, - div.actions-wrapper { - border-top: 1px solid var(--divider-color, #e8e8e8); - padding: 5px 0px; - min-height: 40px; - } - div.mode { - position: absolute; - right: 10px; - top: 64px; - background: var(--primary-color); - padding: 2px 10px; - border-radius: 20px; - font-size: smaller; - color: var(--app-header-text-color); - } - div.action-buttons { - display: flow-root; - } - span.assignment-label { - color: var(--primary-color); - text-transform: uppercase; - font-weight: 500; - font-size: var(--material-small-font-size); - padding: 5px 10px; - } - .slot { - float: left; - background: rgba(var(--rgb-primary-color), 0.7); - height: 60px; - cursor: pointer; - box-sizing: border-box; - transition: background 0.1s cubic-bezier(0.17, 0.67, 0.83, 0.67); - position: relative; - height: 40px; - line-height: 40px; - font-size: 10px; - text-align: center; - overflow: hidden; - } - .slot.previous { - cursor: default; - } - .slot.selected { - background: rgba(52, 143, 255, 1); - } - .setpoint { - z-index: 3; - position: relative; - text-align: center; - } - .slotoverlay { - position: absolute; - display: hidden; - width: 100%; - height: 100%; - top: 0; - left: 0; - right: 0; - bottom: 0; - /*background-color: rgba(0,0,0,0.5);*/ - z-index: 2; - cursor: pointer; - } - .previous { - display: block; - background: repeating-linear-gradient( - 135deg, - rgba(0, 0, 0, 0), - rgba(0, 0, 0, 0) 5px, - rgba(255, 255, 255, 0.2) 5px, - rgba(255, 255, 255, 0.2) 10px - ); - } - .wrapper.selectable .slot:hover { - background: rgba(var(--rgb-primary-color), 0.85); - } - .slot:not(:first-child) { - border-left: 1px solid var(--card-background-color); - } - .slot:not(:last-child) { - border-right: 1px solid var(--card-background-color); - } - .slot.active { - background: rgba(var(--rgb-accent-color), 0.7); - } - .slot.noborder { - border: none; - } - .wrapper.selectable .slot.active:hover { - background: rgba(var(--rgb-accent-color), 0.85); - } - .wrapper .days .day { - line-height: 42px; - float: left; - width: 100%; - } - .wrapper .schedules { - position: relative; - padding-top: 30px; - width: 100%; - } - .wrapper .schedules .slots { - height: 40px; - border-radius: 5px; - overflow: auto; - margin-bottom: 2px; - display: flex; - } - - .setpoint.rotate { - z-index: 3; - transform: rotate(-90deg); - position: absolute; - top: 20px; - height: 0px !important; - width: 100%; - overflow: visible !important; - } - div.schedule-action-wrapper { - display: flex; - justify-content: center; - } - div.time-wrapper div { - float: left; - display: flex; - position: relative; - height: 25px; - line-height: 25px; - font-size: 12px; - text-align: center; - align-content: center; - align-items: center; - justify-content: center; - } - div.time-wrapper div.time:before { - content: ' '; - background: var(--disabled-text-color); - position: absolute; - left: 0px; - top: 0px; - width: 1px; - height: 5px; - margin-left: 50%; - margin-top: 0px; - } - .slot span { - font-size: 10px; - color: var(--text-primary-color); - height: 100%; - display: flex; - align-content: center; - align-items: center; - justify-content: center; - transition: margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - word-break: nowrap; - white-space: normal; - overflow: hidden; - line-height: 1em; - } - div.handle { - display: flex; - height: 100%; - width: 36px; - margin-left: -19px; - margin-bottom: -60px; - align-content: center; - align-items: center; - justify-content: center; - } - div.button-holder { - background: var(--card-background-color); - border-radius: 50%; - width: 24px; - height: 24px; - display: flex; - visibility: hidden; - animation: 0.2s fadeIn; - animation-fill-mode: forwards; - } - .schedule-action-button { - width: 20%; - padding: 0 5px; - } - ha-icon-button { - --mdc-icon-button-size: 36px; - margin-top: -6px; - margin-left: -6px; - } - @keyframes fadeIn { - 99% { - visibility: hidden; - } - 100% { - visibility: visible; - } - } - .card-header ha-icon-button { - position: absolute; - right: 6px; - top: 6px; - } - .sub-heading { - padding-bottom: 10px; - font-weight: 500; - } - span.waiting { - position: absolute; - height: 28px; - width: 100%; - margin: 4px; - } - div.schedule-info { - margin: 3px 0; - } - `}};n([gt({attribute:!1})],De.prototype,"config",void 0),n([gt({attribute:!1})],De.prototype,"schedule_id",void 0),n([gt({attribute:!1})],De.prototype,"schedule_type",void 0),n([gt({attribute:!1})],De.prototype,"use_heat_colors",void 0),n([vt()],De.prototype,"schedule",void 0),n([vt()],De.prototype,"rooms",void 0),n([vt()],De.prototype,"entities",void 0),n([vt()],De.prototype,"suntimes",void 0),n([vt()],De.prototype,"component_loaded",void 0),n([vt()],De.prototype,"_activeSlot",void 0),n([vt()],De.prototype,"_activeDay",void 0),n([vt()],De.prototype,"editMode",void 0),n([vt()],De.prototype,"_current_user",void 0),n([vt()],De.prototype,"_assigning_in_progress",void 0),n([vt()],De.prototype,"_save_in_progress",void 0),n([vt()],De.prototype,"error",void 0),De=n([ut("wiser-schedule-edit-card")],De);let He=class extends ht{constructor(){super(),this.component_loaded=!1,this._schedule_types=[],this._schedule_info={Name:"",Type:"Heating"},this.initialise()}async initialise(){return await this.isComponentLoaded()&&(this.component_loaded=!0,await this.loadData()),!0}async isComponentLoaded(){for(;!this.hass||!this.hass.config.components.includes("wiser");)await new Promise(t=>setTimeout(t,100));return!0}async loadData(){this._schedule_types=await Wt(this.hass,this.config.hub)}render(){return this.hass&&this.config?X` -
-
${Vt("wiser.actions.add_schedule")}
-
${Vt("wiser.helpers.add_schedule")}
-
${this._schedule_types.map((t,e)=>this.renderScheduleTypeButtons(t,e))}
- - -
-
- - ${this.hass.localize("ui.common.save")} - - ${this.hass.localize("ui.common.cancel")} -
- `:X``}renderScheduleTypeButtons(t,e){return X` - - ${t} - - `}async confirmClick(){await this.createSchedule()}async createSchedule(){var t,e,i,s;await(t=this.hass,e=this.config.hub,i=this._schedule_info.Type,s=this._schedule_info.Name,t.callWS({type:"wiser/schedule/create",hub:e,schedule_type:i,name:s}));const n=new CustomEvent("scheduleAdded");this.dispatchEvent(n)}cancelClick(){const t=new CustomEvent("backClick");this.dispatchEvent(t)}_valueChanged(t){const e=t.target;e.configValue&&(this._schedule_info=Object.assign(Object.assign({},this._schedule_info),{[e.configValue]:void 0!==e.checked?e.checked:e.value}))}static get styles(){return h` - div.wrapper { - white-space: nowrap; - transition: - width 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67), - margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - overflow: auto; - } - div.wrapper { - color: var(--primary-text-color); - padding: 5px 0; - } - .schedule-type-select { - margin: 20px 0 0 0; - } - .schedule-name { - margin: 20px 0 0 0; - width: 100%; - } - ha-icon-button { - --mdc-icon-button-size: 36px; - margin-top: -6px; - margin-left: -6px; - } - .card-header ha-icon-button { - position: absolute; - right: 6px; - top: 6px; - } - mwc-button.active { - background: var(--primary-color); - --mdc-theme-primary: var(--text-primary-color); - border-radius: 4px; - } - `}};n([gt({attribute:!1})],He.prototype,"hass",void 0),n([gt({attribute:!1})],He.prototype,"config",void 0),n([gt({attribute:!1})],He.prototype,"component_loaded",void 0),n([vt()],He.prototype,"_schedule_types",void 0),n([vt()],He.prototype,"_schedule_info",void 0),He=n([ut("wiser-schedule-add-card")],He);let Ue=class extends ht{constructor(){super(),this.schedule_id=0,this.component_loaded=!1,this._copy_in_progress=0,this._schedule_list=[],this.initialise()}async initialise(){return await this.isComponentLoaded()&&(this.component_loaded=!0,await this.loadData()),!0}async isComponentLoaded(){for(;!this.hass||!this.hass.config.components.includes("wiser");)await new Promise(t=>setTimeout(t,100));return!0}async loadData(){this.schedule=await Yt(this.hass,this.config.hub,this.schedule_type,this.schedule_id),this._schedule_list=await qt(this.hass,this.config.hub,this.schedule_type)}render(){return this.hass&&this.config&&this.schedule?X` -
-
${Vt("wiser.headings.copy_schedule")}
-
- ${Vt("wiser.headings.schedule_type")}: ${this.schedule.Type} -
-
- ${Vt("wiser.headings.schedule_id")}: ${this.schedule.Id} -
-
- ${Vt("wiser.headings.schedule_name")}: ${this.schedule.Name} -
-
${Vt("wiser.helpers.select_copy_schedule")}
-
- ${this._schedule_list.filter(t=>{var e;return t.Id!=(null===(e=this.schedule)||void 0===e?void 0:e.Id)}).map(t=>this.renderScheduleButtons(t))} -
-
-
- - ${this.hass.localize("ui.common.cancel")} - -
- - `:X``}renderScheduleButtons(t){return X` - - ${this._copy_in_progress==t.Id?X``:null} - ${t.Name} - - `}cancelClick(){const t=new CustomEvent("backClick",{detail:Et.ScheduleEdit});this.dispatchEvent(t)}async _copySchedule(t){const e=t.target;if(e.id){this._copy_in_progress=parseInt(e.id),await(i=this.hass,s=this.config.hub,n=this.schedule_type,r=this.schedule_id,o=parseInt(e.id),i.callWS({type:"wiser/schedule/copy",hub:s,schedule_type:n,schedule_id:r,to_schedule_id:o})),this._copy_in_progress=0;const t=new CustomEvent("scheduleCopied");this.dispatchEvent(t)}var i,s,n,r,o}static get styles(){return h` - div.wrapper { - white-space: nowrap; - transition: - width 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67), - margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - overflow: auto; - } - div.card-actions { - border-top: 1px solid var(--divider-color, #e8e8e8); - padding: 5px 0px; - min-height: 40px; - } - div.wrapper { - color: var(--primary-text-color); - padding: 5px 0; - } - .schedule-type-select { - margin: 20px 0 0 0; - } - .schedule-name { - margin: 20px 0 0 0; - width: 100%; - } - .sub-heading { - padding-bottom: 10px; - font-weight: 500; - } - .schedule-button { - padding: 5px; - } - span.waiting { - position: absolute; - height: 28px; - width: 100%; - margin: 4px; - } - div.schedule-info { - margin: 3px 0; - } - `}};n([gt({attribute:!1})],Ue.prototype,"hass",void 0),n([gt({attribute:!1})],Ue.prototype,"config",void 0),n([gt({attribute:!1})],Ue.prototype,"schedule_id",void 0),n([gt({attribute:!1})],Ue.prototype,"schedule_type",void 0),n([vt()],Ue.prototype,"schedule",void 0),n([vt()],Ue.prototype,"component_loaded",void 0),n([vt()],Ue.prototype,"_copy_in_progress",void 0),n([vt()],Ue.prototype,"_schedule_list",void 0),Ue=n([ut("wiser-schedule-copy-card")],Ue);let ze=class extends ht{constructor(){super(),this.component_loaded=!1,this._newScheduleName="",this._rename_in_progress=!1,this.initialise()}async initialise(){return await this.isComponentLoaded()&&(this.component_loaded=!0,await this.loadData()),!0}async isComponentLoaded(){for(;!this.hass||!this.hass.config.components.includes("wiser");)await new Promise(t=>setTimeout(t,100));return!0}async loadData(){this._schedule=await Yt(this.hass,this.config.hub,this.schedule_type,this.schedule_id)}render(){return this.hass&&this.config?X` -
-
${Vt("wiser.headings.rename_schedule")}
-
${Vt("wiser.helpers.enter_new_name")}
- - -
-
- - ${this._rename_in_progress?X``:this.hass.localize("ui.common.save")} - - - ${this.hass.localize("ui.common.cancel")} - -
- `:X``}async confirmClick(){await this.renameSchedule()}async renameSchedule(){var t,e,i,s,n;this._rename_in_progress=!0,await(t=this.hass,e=this.config.hub,i=this.schedule_type,s=this.schedule_id,n=this._newScheduleName,t.callWS({type:"wiser/schedule/rename",hub:e,schedule_type:i,schedule_id:s,schedule_name:n}));const r=new CustomEvent("scheduleRenamed");this.dispatchEvent(r),this._rename_in_progress=!1}cancelClick(){const t=new CustomEvent("backClick",{detail:Et.ScheduleEdit});this.dispatchEvent(t)}_valueChanged(t){const e=t.target;e.configValue&&(this._newScheduleName=e.value)}static get styles(){return h` - div.wrapper { - white-space: nowrap; - transition: - width 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67), - margin 0.2s cubic-bezier(0.17, 0.67, 0.83, 0.67); - overflow: auto; - } - div.wrapper { - color: var(--primary-text-color); - padding: 5px 0; - } - .card-actions { - padding-top: 8px; - } - .schedule-type-select { - margin: 20px 0 0 0; - } - .schedule-name { - margin: 20px 0 0 0; - width: 100%; - } - ha-icon-button { - --mdc-icon-button-size: 36px; - margin-top: -6px; - margin-left: -6px; - } - .card-header ha-icon-button { - position: absolute; - right: 6px; - top: 6px; - } - mwc-button.active { - background: var(--primary-color); - --mdc-theme-primary: var(--text-primary-color); - border-radius: 4px; - } - `}};n([gt({attribute:!1})],ze.prototype,"hass",void 0),n([gt({attribute:!1})],ze.prototype,"config",void 0),n([gt({attribute:!1})],ze.prototype,"component_loaded",void 0),n([gt({attribute:!1})],ze.prototype,"schedule_type",void 0),n([gt({attribute:!1})],ze.prototype,"schedule_id",void 0),n([vt()],ze.prototype,"_newScheduleName",void 0),n([vt()],ze.prototype,"_schedule",void 0),n([vt()],ze.prototype,"_rename_in_progress",void 0),ze=n([ut("wiser-schedule-rename-card")],ze); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Fe=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),Be={ROOT:"mdc-form-field"},Ve={LABEL_SELECTOR:".mdc-form-field > label"},je=function(t){function e(i){var n=t.call(this,s(s({},e.defaultAdapter),i))||this;return n.click=function(){n.handleClick()},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Be},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Ve},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{activateInputRipple:function(){},deactivateInputRipple:function(){},deregisterInteractionHandler:function(){},registerInteractionHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerInteractionHandler("click",this.click)},e.prototype.destroy=function(){this.adapter.deregisterInteractionHandler("click",this.click)},e.prototype.handleClick=function(){var t=this;this.adapter.activateInputRipple(),requestAnimationFrame(function(){t.adapter.deactivateInputRipple()})},e}(Fe); -/** - * @license - * Copyright 2017 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const We=window,qe=We.ShadowRoot&&(void 0===We.ShadyCSS||We.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Ye=Symbol(),Xe=new WeakMap;let Ge=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==Ye)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(qe&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=Xe.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&Xe.set(e,t))}return t}toString(){return this.cssText}};const Ke=qe?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new Ge("string"==typeof t?t:t+"",void 0,Ye))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Je;const Ze=window,Qe=Ze.trustedTypes,ti=Qe?Qe.emptyScript:"",ei=Ze.reactiveElementPolyfillSupport,ii={toAttribute(t,e){switch(e){case Boolean:t=t?ti:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},si=(t,e)=>e!==t&&(e==e||t==t),ni={attribute:!0,type:String,converter:ii,reflect:!1,hasChanged:si},ri="finalized";let oi=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=ni){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||ni}static finalize(){if(this.hasOwnProperty(ri))return!1;this[ri]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(Ke(t))}else void 0!==t&&e.push(Ke(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{qe?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=We.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=ni){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:ii).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:ii;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||si)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var ai;oi[ri]=!0,oi.elementProperties=new Map,oi.elementStyles=[],oi.shadowRootOptions={mode:"open"},null==ei||ei({ReactiveElement:oi}),(null!==(Je=Ze.reactiveElementVersions)&&void 0!==Je?Je:Ze.reactiveElementVersions=[]).push("1.6.3");const li=window,di=li.trustedTypes,ci=di?di.createPolicy("lit-html",{createHTML:t=>t}):void 0,hi="$lit$",pi=`lit$${(Math.random()+"").slice(9)}$`,ui="?"+pi,mi=`<${ui}>`,fi=document,gi=()=>fi.createComment(""),vi=t=>null===t||"object"!=typeof t&&"function"!=typeof t,_i=Array.isArray,bi="[ \t\n\f\r]",yi=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,xi=/-->/g,$i=/>/g,wi=RegExp(`>|${bi}(?:([^\\s"'>=/]+)(${bi}*=${bi}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Ai=/'/g,Ei=/"/g,Si=/^(?:script|style|textarea|title)$/i,Ci=Symbol.for("lit-noChange"),Ti=Symbol.for("lit-nothing"),Ii=new WeakMap,Oi=fi.createTreeWalker(fi,129,null,!1);function ki(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==ci?ci.createHTML(e):e}let Ri=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=yi;for(let e=0;e"===l[0]?(o=null!=n?n:yi,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?wi:'"'===l[3]?Ei:Ai):o===Ei||o===Ai?o=wi:o===xi||o===$i?o=yi:(o=wi,n=void 0);const h=o===wi&&t[e+1].startsWith("/>")?" ":"";r+=o===yi?i+mi:d>=0?(s.push(a),i.slice(0,d)+hi+i.slice(d)+pi+h):i+pi+(-2===d?(s.push(void 0),e):h)}return[ki(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),Oi.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=Oi.nextNode())&&l.length0){n.textContent=di?di.emptyScript:"";for(let i=0;i_i(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==Ti&&vi(this._$AH)?this._$AA.nextSibling.data=t:this.$(fi.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=Ri.createElement(ki(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new Li(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=Ii.get(t.strings);return void 0===e&&Ii.set(t.strings,e=new Ri(t)),e}T(e){_i(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(gi()),this.k(gi()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=Ti}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=Pi(this,t,e,0),r=!vi(t)||t!==this._$AH&&t!==Ci,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new Mi(e.insertBefore(gi(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return Ci}};Wi.finalized=!0,Wi._$litElement$=!0,null===(Vi=globalThis.litElementHydrateSupport)||void 0===Vi||Vi.call(globalThis,{LitElement:Wi});const qi=globalThis.litElementPolyfillSupport;null==qi||qi({LitElement:Wi}),(null!==(ji=globalThis.litElementVersions)&&void 0!==ji?ji:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -const Yi=t=>t.nodeType===Node.ELEMENT_NODE;function Xi(t){return{addClass:e=>{t.classList.add(e)},removeClass:e=>{t.classList.remove(e)},hasClass:e=>t.classList.contains(e)}}const Gi=()=>{},Ki={get passive(){return!1}};document.addEventListener("x",Gi,Ki),document.removeEventListener("x",Gi);const Ji=(t=window.document)=>{let e=t.activeElement;const i=[];if(!e)return i;for(;e&&(i.push(e),e.shadowRoot);)e=e.shadowRoot.activeElement;return i},Zi=t=>{const e=Ji();if(!e.length)return!1;const i=e[e.length-1],s=new Event("check-if-focused",{bubbles:!0,composed:!0});let n=[];const r=t=>{n=t.composedPath()};return document.body.addEventListener("check-if-focused",r),i.dispatchEvent(s),document.body.removeEventListener("check-if-focused",r),-1!==n.indexOf(t)}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class Qi extends Wi{click(){if(this.mdcRoot)return this.mdcRoot.focus(),void this.mdcRoot.click();super.click()}createFoundation(){void 0!==this.mdcFoundation&&this.mdcFoundation.destroy(),this.mdcFoundationClass&&(this.mdcFoundation=new this.mdcFoundationClass(this.createAdapter()),this.mdcFoundation.init())}firstUpdated(){this.createFoundation()}} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const ts=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}}; -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var es,is,ss;null===(es=window.HTMLSlotElement)||void 0===es||es.prototype.assignedElements;const ns=null!==(ss=null===(is=window.ShadyDOM)||void 0===is?void 0:is.inUse)&&void 0!==ss&&ss;class rs extends Qi{constructor(){super(...arguments),this.disabled=!1,this.containingForm=null,this.formDataListener=t=>{this.disabled||this.setFormData(t.formData)}}findFormElement(){if(!this.shadowRoot||ns)return null;const t=this.getRootNode().querySelectorAll("form");for(const e of Array.from(t))if(e.contains(this))return e;return null}connectedCallback(){var t;super.connectedCallback(),this.containingForm=this.findFormElement(),null===(t=this.containingForm)||void 0===t||t.addEventListener("formdata",this.formDataListener)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this.containingForm)||void 0===t||t.removeEventListener("formdata",this.formDataListener),this.containingForm=null}click(){this.formElement&&!this.disabled&&(this.formElement.focus(),this.formElement.click())}firstUpdated(){super.firstUpdated(),this.shadowRoot&&this.mdcRoot.addEventListener("change",t=>{this.dispatchEvent(new Event("change",t))})}}rs.shadowRootOptions={mode:"open",delegatesFocus:!0},n([function(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):ts(t,e)}({type:Boolean})],rs.prototype,"disabled",void 0); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -const os=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnProperty("_observers")){const t=e.constructor._observers;e.constructor._observers=new Map,t.forEach((t,i)=>e.constructor._observers.set(i,t))}}else{e.constructor._observers=new Map;const t=e.updated;e.updated=function(e){t.call(this,e),e.forEach((t,e)=>{const i=this.constructor._observers.get(e);void 0!==i&&i.call(this,this[e],t)})}}e.constructor._observers.set(i,t)},as=window,ls=as.ShadowRoot&&(void 0===as.ShadyCSS||as.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,ds=Symbol(),cs=new WeakMap; -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */let hs=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==ds)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(ls&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=cs.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&cs.set(e,t))}return t}toString(){return this.cssText}};const ps=ls?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new hs("string"==typeof t?t:t+"",void 0,ds))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var us;const ms=window,fs=ms.trustedTypes,gs=fs?fs.emptyScript:"",vs=ms.reactiveElementPolyfillSupport,_s={toAttribute(t,e){switch(e){case Boolean:t=t?gs:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},bs=(t,e)=>e!==t&&(e==e||t==t),ys={attribute:!0,type:String,converter:_s,reflect:!1,hasChanged:bs},xs="finalized";let $s=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=ys){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||ys}static finalize(){if(this.hasOwnProperty(xs))return!1;this[xs]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(ps(t))}else void 0!==t&&e.push(ps(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{ls?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=as.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=ys){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:_s).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:_s;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||bs)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var ws;$s[xs]=!0,$s.elementProperties=new Map,$s.elementStyles=[],$s.shadowRootOptions={mode:"open"},null==vs||vs({ReactiveElement:$s}),(null!==(us=ms.reactiveElementVersions)&&void 0!==us?us:ms.reactiveElementVersions=[]).push("1.6.3");const As=window,Es=As.trustedTypes,Ss=Es?Es.createPolicy("lit-html",{createHTML:t=>t}):void 0,Cs="$lit$",Ts=`lit$${(Math.random()+"").slice(9)}$`,Is="?"+Ts,Os=`<${Is}>`,ks=document,Rs=()=>ks.createComment(""),Ps=t=>null===t||"object"!=typeof t&&"function"!=typeof t,Ls=Array.isArray,Ms="[ \t\n\f\r]",Ns=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ds=/-->/g,Hs=/>/g,Us=RegExp(`>|${Ms}(?:([^\\s"'>=/]+)(${Ms}*=${Ms}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),zs=/'/g,Fs=/"/g,Bs=/^(?:script|style|textarea|title)$/i,Vs=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),js=Symbol.for("lit-noChange"),Ws=Symbol.for("lit-nothing"),qs=new WeakMap,Ys=ks.createTreeWalker(ks,129,null,!1);function Xs(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==Ss?Ss.createHTML(e):e}let Gs=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=Ns;for(let e=0;e"===l[0]?(o=null!=n?n:Ns,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?Us:'"'===l[3]?Fs:zs):o===Fs||o===zs?o=Us:o===Ds||o===Hs?o=Ns:(o=Us,n=void 0);const h=o===Us&&t[e+1].startsWith("/>")?" ":"";r+=o===Ns?i+Os:d>=0?(s.push(a),i.slice(0,d)+Cs+i.slice(d)+Ts+h):i+Ts+(-2===d?(s.push(void 0),e):h)}return[Xs(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),Ys.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=Ys.nextNode())&&l.length0){n.textContent=Es?Es.emptyScript:"";for(let i=0;iLs(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==Ws&&Ps(this._$AH)?this._$AA.nextSibling.data=t:this.$(ks.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=Gs.createElement(Xs(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new Js(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=qs.get(t.strings);return void 0===e&&qs.set(t.strings,e=new Gs(t)),e}T(e){Ls(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(Rs()),this.k(Rs()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=Ws}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=Ks(this,t,e,0),r=!Ps(t)||t!==this._$AH&&t!==js,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new Zs(e.insertBefore(Rs(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return js}};dn.finalized=!0,dn._$litElement$=!0,null===(an=globalThis.litElementHydrateSupport)||void 0===an||an.call(globalThis,{LitElement:dn});const cn=globalThis.litElementPolyfillSupport;null==cn||cn({LitElement:dn}),(null!==(ln=globalThis.litElementVersions)&&void 0!==ln?ln:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const hn=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function pn(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):hn(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const un=({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function mn(t,e){return un({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var fn;const gn=null!=(null===(fn=window.HTMLSlotElement)||void 0===fn?void 0:fn.prototype.assignedElements)?(t,e)=>t.assignedElements(e):(t,e)=>t.assignedNodes(e).filter(t=>t.nodeType===Node.ELEMENT_NODE); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const vn=1;let _n=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const bn=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends _n{constructor(t){var e;if(super(t),t.type!==vn||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter(e=>t[e]).join(" ")+" "}update(t,[e]){var i,s;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach(t=>{t in e||(n.remove(t),this.it.delete(t))});for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(s=this.nt)||void 0===s?void 0:s.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return js}}); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class yn extends Qi{constructor(){super(...arguments),this.alignEnd=!1,this.spaceBetween=!1,this.nowrap=!1,this.label="",this.mdcFoundationClass=je}createAdapter(){return{registerInteractionHandler:(t,e)=>{this.labelEl.addEventListener(t,e)},deregisterInteractionHandler:(t,e)=>{this.labelEl.removeEventListener(t,e)},activateInputRipple:async()=>{const t=this.input;if(t instanceof rs){const e=await t.ripple;e&&e.startPress()}},deactivateInputRipple:async()=>{const t=this.input;if(t instanceof rs){const e=await t.ripple;e&&e.endPress()}}}}get input(){var t,e;return null!==(e=null===(t=this.slottedInputs)||void 0===t?void 0:t[0])&&void 0!==e?e:null}render(){const t={"mdc-form-field--align-end":this.alignEnd,"mdc-form-field--space-between":this.spaceBetween,"mdc-form-field--nowrap":this.nowrap};return Vs` -
- - -
`}click(){this._labelClick()}_labelClick(){const t=this.input;t&&(t.focus(),t.click())}}n([pn({type:Boolean})],yn.prototype,"alignEnd",void 0),n([pn({type:Boolean})],yn.prototype,"spaceBetween",void 0),n([pn({type:Boolean})],yn.prototype,"nowrap",void 0),n([pn({type:String}),os(async function(t){var e;null===(e=this.input)||void 0===e||e.setAttribute("aria-label",t)})],yn.prototype,"label",void 0),n([mn(".mdc-form-field")],yn.prototype,"mdcRoot",void 0),n([ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function(t,e,i){let s;return s={flatten:e},i?function(t){const{slot:e,selector:i}=null!=t?t:{};return un({descriptor:s=>({get(){var s;const n="slot"+(e?`[name=${e}]`:":not([name])"),r=null===(s=this.renderRoot)||void 0===s?void 0:s.querySelector(n),o=null!=r?gn(r,t):[];return i?o.filter(t=>t.matches(i)):o},enumerable:!0,configurable:!0})})}({slot:t,flatten:e,selector:i}):un({descriptor:t=>({get(){var t,e;const i=null===(t=this.renderRoot)||void 0===t?void 0:t.querySelector("slot:not([name])");return null!==(e=null==i?void 0:i.assignedNodes(s))&&void 0!==e?e:[]},enumerable:!0,configurable:!0})})}("",!0,"*")],yn.prototype,"slottedInputs",void 0),n([mn("label")],yn.prototype,"labelEl",void 0); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const xn=((t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new hs(i,t,ds)})`.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}`,$n={"mwc-formfield":class extends yn{static get styles(){return xn}}}; -/** - * @license - * Copyright 2020 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var wn="Unknown",An="Backspace",En="Enter",Sn="Spacebar",Cn="PageUp",Tn="PageDown",In="End",On="Home",kn="ArrowLeft",Rn="ArrowUp",Pn="ArrowRight",Ln="ArrowDown",Mn="Delete",Nn="Escape",Dn="Tab",Hn=new Set;Hn.add(An),Hn.add(En),Hn.add(Sn),Hn.add(Cn),Hn.add(Tn),Hn.add(In),Hn.add(On),Hn.add(kn),Hn.add(Rn),Hn.add(Pn),Hn.add(Ln),Hn.add(Mn),Hn.add(Nn),Hn.add(Dn);var Un=8,zn=13,Fn=32,Bn=33,Vn=34,jn=35,Wn=36,qn=37,Yn=38,Xn=39,Gn=40,Kn=46,Jn=27,Zn=9,Qn=new Map;Qn.set(Un,An),Qn.set(zn,En),Qn.set(Fn,Sn),Qn.set(Bn,Cn),Qn.set(Vn,Tn),Qn.set(jn,In),Qn.set(Wn,On),Qn.set(qn,kn),Qn.set(Yn,Rn),Qn.set(Xn,Pn),Qn.set(Gn,Ln),Qn.set(Kn,Mn),Qn.set(Jn,Nn),Qn.set(Zn,Dn);var tr=new Set;function er(t){var e=t.key;if(Hn.has(e))return e;var i=Qn.get(t.keyCode);return i||wn} -/** - * @license - * Copyright 2020 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */tr.add(Cn),tr.add(Tn),tr.add(In),tr.add(On),tr.add(kn),tr.add(Rn),tr.add(Pn),tr.add(Ln);var ir="Unknown",sr="Backspace",nr="Enter",rr="Spacebar",or="PageUp",ar="PageDown",lr="End",dr="Home",cr="ArrowLeft",hr="ArrowUp",pr="ArrowRight",ur="ArrowDown",mr="Delete",fr="Escape",gr="Tab",vr=new Set;vr.add(sr),vr.add(nr),vr.add(rr),vr.add(or),vr.add(ar),vr.add(lr),vr.add(dr),vr.add(cr),vr.add(hr),vr.add(pr),vr.add(ur),vr.add(mr),vr.add(fr),vr.add(gr);var _r=8,br=13,yr=32,xr=33,$r=34,wr=35,Ar=36,Er=37,Sr=38,Cr=39,Tr=40,Ir=46,Or=27,kr=9,Rr=new Map;Rr.set(_r,sr),Rr.set(br,nr),Rr.set(yr,rr),Rr.set(xr,or),Rr.set($r,ar),Rr.set(wr,lr),Rr.set(Ar,dr),Rr.set(Er,cr),Rr.set(Sr,hr),Rr.set(Cr,pr),Rr.set(Tr,ur),Rr.set(Ir,mr),Rr.set(Or,fr),Rr.set(kr,gr);var Pr,Lr,Mr=new Set;function Nr(t){var e=t.key;if(vr.has(e))return e;var i=Rr.get(t.keyCode);return i||ir} -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */Mr.add(or),Mr.add(ar),Mr.add(lr),Mr.add(dr),Mr.add(cr),Mr.add(hr),Mr.add(pr),Mr.add(ur);var Dr="mdc-list-item--activated",Hr="mdc-list-item",Ur="mdc-list-item--disabled",zr="mdc-list-item--selected",Fr="mdc-list-item__text",Br="mdc-list-item__primary-text",Vr="mdc-list";(Pr={})[""+Dr]="mdc-list-item--activated",Pr[""+Hr]="mdc-list-item",Pr[""+Ur]="mdc-list-item--disabled",Pr[""+zr]="mdc-list-item--selected",Pr[""+Br]="mdc-list-item__primary-text",Pr[""+Vr]="mdc-list";var jr=((Lr={})[""+Dr]="mdc-deprecated-list-item--activated",Lr[""+Hr]="mdc-deprecated-list-item",Lr[""+Ur]="mdc-deprecated-list-item--disabled",Lr[""+zr]="mdc-deprecated-list-item--selected",Lr[""+Fr]="mdc-deprecated-list-item__text",Lr[""+Br]="mdc-deprecated-list-item__primary-text",Lr[""+Vr]="mdc-deprecated-list",Lr),Wr={ACTION_EVENT:"MDCList:action",SELECTION_CHANGE_EVENT:"MDCList:selectionChange",ARIA_CHECKED:"aria-checked",ARIA_CHECKED_CHECKBOX_SELECTOR:'[role="checkbox"][aria-checked="true"]',ARIA_CHECKED_RADIO_SELECTOR:'[role="radio"][aria-checked="true"]',ARIA_CURRENT:"aria-current",ARIA_DISABLED:"aria-disabled",ARIA_ORIENTATION:"aria-orientation",ARIA_ORIENTATION_HORIZONTAL:"horizontal",ARIA_ROLE_CHECKBOX_SELECTOR:'[role="checkbox"]',ARIA_SELECTED:"aria-selected",ARIA_INTERACTIVE_ROLES_SELECTOR:'[role="listbox"], [role="menu"]',ARIA_MULTI_SELECTABLE_SELECTOR:'[aria-multiselectable="true"]',CHECKBOX_RADIO_SELECTOR:'input[type="checkbox"], input[type="radio"]',CHECKBOX_SELECTOR:'input[type="checkbox"]',CHILD_ELEMENTS_TO_TOGGLE_TABINDEX:"\n ."+Hr+" button:not(:disabled),\n ."+Hr+" a,\n ."+jr[Hr]+" button:not(:disabled),\n ."+jr[Hr]+" a\n ",DEPRECATED_SELECTOR:".mdc-deprecated-list",FOCUSABLE_CHILD_ELEMENTS:"\n ."+Hr+" button:not(:disabled),\n ."+Hr+" a,\n ."+Hr+' input[type="radio"]:not(:disabled),\n .'+Hr+' input[type="checkbox"]:not(:disabled),\n .'+jr[Hr]+" button:not(:disabled),\n ."+jr[Hr]+" a,\n ."+jr[Hr]+' input[type="radio"]:not(:disabled),\n .'+jr[Hr]+' input[type="checkbox"]:not(:disabled)\n ',RADIO_SELECTOR:'input[type="radio"]',SELECTED_ITEM_SELECTOR:'[aria-selected="true"], [aria-current="true"]'},qr={UNSET_INDEX:-1,TYPEAHEAD_BUFFER_CLEAR_TIMEOUT_MS:300},Yr=["input","button","textarea","select"],Xr=function(t){var e=t.target;if(e){var i=(""+e.tagName).toLowerCase();-1===Yr.indexOf(i)&&t.preventDefault()}};function Gr(t,e){for(var i=new Map,s=0;se&&!i(r[a].index)){l=a;break}if(-1!==l)return s.sortedIndexCursor=l,r[s.sortedIndexCursor].index;return-1}(r,o,l,e):function(t,e,i){var s=i.typeaheadBuffer[0],n=t.get(s);if(!n)return-1;var r=n[i.sortedIndexCursor];if(0===r.text.lastIndexOf(i.typeaheadBuffer,0)&&!e(r.index))return r.index;var o=(i.sortedIndexCursor+1)%n.length,a=-1;for(;o!==i.sortedIndexCursor;){var l=n[o],d=0===l.text.lastIndexOf(i.typeaheadBuffer,0),c=!e(l.index);if(d&&c){a=o;break}o=(o+1)%n.length}if(-1!==a)return i.sortedIndexCursor=a,n[i.sortedIndexCursor].index;return-1}(r,l,e),-1===i||a||n(i),i}function Jr(t){return t.typeaheadBuffer.length>0} -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Zr=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),Qr={LABEL_FLOAT_ABOVE:"mdc-floating-label--float-above",LABEL_REQUIRED:"mdc-floating-label--required",LABEL_SHAKE:"mdc-floating-label--shake",ROOT:"mdc-floating-label"},to=function(t){function e(i){var n=t.call(this,s(s({},e.defaultAdapter),i))||this;return n.shakeAnimationEndHandler=function(){n.handleShakeAnimationEnd()},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Qr},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},getWidth:function(){return 0},registerInteractionHandler:function(){},deregisterInteractionHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.getWidth=function(){return this.adapter.getWidth()},e.prototype.shake=function(t){var i=e.cssClasses.LABEL_SHAKE;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.float=function(t){var i=e.cssClasses,s=i.LABEL_FLOAT_ABOVE,n=i.LABEL_SHAKE;t?this.adapter.addClass(s):(this.adapter.removeClass(s),this.adapter.removeClass(n))},e.prototype.setRequired=function(t){var i=e.cssClasses.LABEL_REQUIRED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.handleShakeAnimationEnd=function(){var t=e.cssClasses.LABEL_SHAKE;this.adapter.removeClass(t)},e}(Zr); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const eo=1,io=3;let so=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */const no=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends so{constructor(t){switch(super(t),this.foundation=null,this.previousPart=null,t.type){case eo:case io:break;default:throw new Error("FloatingLabel directive only support attribute and property parts")}}update(t,[e]){if(t!==this.previousPart){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-floating-label");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),getWidth:()=>t.scrollWidth,registerInteractionHandler:(e,i)=>{t.addEventListener(e,i)},deregisterInteractionHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new to(i),this.foundation.init()}return this.render(e)}render(t){return this.foundation}}); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var ro=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),oo={LINE_RIPPLE_ACTIVE:"mdc-line-ripple--active",LINE_RIPPLE_DEACTIVATING:"mdc-line-ripple--deactivating"},ao=function(t){function e(i){var n=t.call(this,s(s({},e.defaultAdapter),i))||this;return n.transitionEndHandler=function(t){n.handleTransitionEnd(t)},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return oo},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},setStyle:function(){},registerEventHandler:function(){},deregisterEventHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerEventHandler("transitionend",this.transitionEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterEventHandler("transitionend",this.transitionEndHandler)},e.prototype.activate=function(){this.adapter.removeClass(oo.LINE_RIPPLE_DEACTIVATING),this.adapter.addClass(oo.LINE_RIPPLE_ACTIVE)},e.prototype.setRippleCenter=function(t){this.adapter.setStyle("transform-origin",t+"px center")},e.prototype.deactivate=function(){this.adapter.addClass(oo.LINE_RIPPLE_DEACTIVATING)},e.prototype.handleTransitionEnd=function(t){var e=this.adapter.hasClass(oo.LINE_RIPPLE_DEACTIVATING);"opacity"===t.propertyName&&e&&(this.adapter.removeClass(oo.LINE_RIPPLE_ACTIVE),this.adapter.removeClass(oo.LINE_RIPPLE_DEACTIVATING))},e}(ro); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const lo=1,co=3;let ho=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */const po=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends ho{constructor(t){switch(super(t),this.previousPart=null,this.foundation=null,t.type){case lo:case co:return;default:throw new Error("LineRipple only support attribute and property parts.")}}update(t,e){if(this.previousPart!==t){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-line-ripple");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),hasClass:e=>t.classList.contains(e),setStyle:(e,i)=>t.style.setProperty(e,i),registerEventHandler:(e,i)=>{t.addEventListener(e,i)},deregisterEventHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new ao(i),this.foundation.init()}return this.render()}render(){return this.foundation}}); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var uo=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),mo="Unknown",fo="Backspace",go="Enter",vo="Spacebar",_o="PageUp",bo="PageDown",yo="End",xo="Home",$o="ArrowLeft",wo="ArrowUp",Ao="ArrowRight",Eo="ArrowDown",So="Delete",Co="Escape",To="Tab",Io=new Set; -/** - * @license - * Copyright 2020 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */Io.add(fo),Io.add(go),Io.add(vo),Io.add(_o),Io.add(bo),Io.add(yo),Io.add(xo),Io.add($o),Io.add(wo),Io.add(Ao),Io.add(Eo),Io.add(So),Io.add(Co),Io.add(To);var Oo=8,ko=13,Ro=32,Po=33,Lo=34,Mo=35,No=36,Do=37,Ho=38,Uo=39,zo=40,Fo=46,Bo=27,Vo=9,jo=new Map;jo.set(Oo,fo),jo.set(ko,go),jo.set(Ro,vo),jo.set(Po,_o),jo.set(Lo,bo),jo.set(Mo,yo),jo.set(No,xo),jo.set(Do,$o),jo.set(Ho,wo),jo.set(Uo,Ao),jo.set(zo,Eo),jo.set(Fo,So),jo.set(Bo,Co),jo.set(Vo,To);var Wo=new Set;function qo(t){var e=t.key;if(Io.has(e))return e;var i=jo.get(t.keyCode);return i||mo} -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */Wo.add(_o),Wo.add(bo),Wo.add(yo),Wo.add(xo),Wo.add($o),Wo.add(wo),Wo.add(Ao),Wo.add(Eo);var Yo,Xo,Go={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"},Ko={CLOSED_EVENT:"MDCMenuSurface:closed",CLOSING_EVENT:"MDCMenuSurface:closing",OPENED_EVENT:"MDCMenuSurface:opened",OPENING_EVENT:"MDCMenuSurface:opening",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(", ")},Jo={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(t){t[t.BOTTOM=1]="BOTTOM",t[t.CENTER=2]="CENTER",t[t.RIGHT=4]="RIGHT",t[t.FLIP_RTL=8]="FLIP_RTL"}(Yo||(Yo={})),function(t){t[t.TOP_LEFT=0]="TOP_LEFT",t[t.TOP_RIGHT=4]="TOP_RIGHT",t[t.BOTTOM_LEFT=1]="BOTTOM_LEFT",t[t.BOTTOM_RIGHT=5]="BOTTOM_RIGHT",t[t.TOP_START=8]="TOP_START",t[t.TOP_END=12]="TOP_END",t[t.BOTTOM_START=9]="BOTTOM_START",t[t.BOTTOM_END=13]="BOTTOM_END"}(Xo||(Xo={})); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Zo={ACTIVATED:"mdc-select--activated",DISABLED:"mdc-select--disabled",FOCUSED:"mdc-select--focused",INVALID:"mdc-select--invalid",MENU_INVALID:"mdc-select__menu--invalid",OUTLINED:"mdc-select--outlined",REQUIRED:"mdc-select--required",ROOT:"mdc-select",WITH_LEADING_ICON:"mdc-select--with-leading-icon"},Qo={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",ARIA_SELECTED_ATTR:"aria-selected",CHANGE_EVENT:"MDCSelect:change",HIDDEN_INPUT_SELECTOR:'input[type="hidden"]',LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-select__icon",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",MENU_SELECTOR:".mdc-select__menu",OUTLINE_SELECTOR:".mdc-notched-outline",SELECTED_TEXT_SELECTOR:".mdc-select__selected-text",SELECT_ANCHOR_SELECTOR:".mdc-select__anchor",VALUE_ATTR:"data-value"},ta={LABEL_SCALE:.75,UNSET_INDEX:-1,CLICK_DEBOUNCE_TIMEOUT_MS:330},ea=function(t){function e(i,n){void 0===n&&(n={});var r=t.call(this,s(s({},e.defaultAdapter),i))||this;return r.disabled=!1,r.isMenuOpen=!1,r.useDefaultValidation=!0,r.customValidity=!0,r.lastSelectedIndex=ta.UNSET_INDEX,r.clickDebounceTimeout=0,r.recentlyClicked=!1,r.leadingIcon=n.leadingIcon,r.helperText=n.helperText,r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Zo},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return ta},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Qo},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},activateBottomLine:function(){},deactivateBottomLine:function(){},getSelectedIndex:function(){return-1},setSelectedIndex:function(){},hasLabel:function(){return!1},floatLabel:function(){},getLabelWidth:function(){return 0},setLabelRequired:function(){},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){},setRippleCenter:function(){},notifyChange:function(){},setSelectedText:function(){},isSelectAnchorFocused:function(){return!1},getSelectAnchorAttr:function(){return""},setSelectAnchorAttr:function(){},removeSelectAnchorAttr:function(){},addMenuClass:function(){},removeMenuClass:function(){},openMenu:function(){},closeMenu:function(){},getAnchorElement:function(){return null},setMenuAnchorElement:function(){},setMenuAnchorCorner:function(){},setMenuWrapFocus:function(){},focusMenuItemAtIndex:function(){},getMenuItemCount:function(){return 0},getMenuItemValues:function(){return[]},getMenuItemTextAtIndex:function(){return""},isTypeaheadInProgress:function(){return!1},typeaheadMatchItem:function(){return-1}}},enumerable:!1,configurable:!0}),e.prototype.getSelectedIndex=function(){return this.adapter.getSelectedIndex()},e.prototype.setSelectedIndex=function(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1),t>=this.adapter.getMenuItemCount()||(t===ta.UNSET_INDEX?this.adapter.setSelectedText(""):this.adapter.setSelectedText(this.adapter.getMenuItemTextAtIndex(t).trim()),this.adapter.setSelectedIndex(t),e&&this.adapter.closeMenu(),i||this.lastSelectedIndex===t||this.handleChange(),this.lastSelectedIndex=t)},e.prototype.setValue=function(t,e){void 0===e&&(e=!1);var i=this.adapter.getMenuItemValues().indexOf(t);this.setSelectedIndex(i,!1,e)},e.prototype.getValue=function(){var t=this.adapter.getSelectedIndex(),e=this.adapter.getMenuItemValues();return t!==ta.UNSET_INDEX?e[t]:""},e.prototype.getDisabled=function(){return this.disabled},e.prototype.setDisabled=function(t){this.disabled=t,this.disabled?(this.adapter.addClass(Zo.DISABLED),this.adapter.closeMenu()):this.adapter.removeClass(Zo.DISABLED),this.leadingIcon&&this.leadingIcon.setDisabled(this.disabled),this.disabled?this.adapter.removeSelectAnchorAttr("tabindex"):this.adapter.setSelectAnchorAttr("tabindex","0"),this.adapter.setSelectAnchorAttr("aria-disabled",this.disabled.toString())},e.prototype.openMenu=function(){this.adapter.addClass(Zo.ACTIVATED),this.adapter.openMenu(),this.isMenuOpen=!0,this.adapter.setSelectAnchorAttr("aria-expanded","true")},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.layout=function(){if(this.adapter.hasLabel()){var t=this.getValue().length>0,e=this.adapter.hasClass(Zo.FOCUSED),i=t||e,s=this.adapter.hasClass(Zo.REQUIRED);this.notchOutline(i),this.adapter.floatLabel(i),this.adapter.setLabelRequired(s)}},e.prototype.layoutOptions=function(){var t=this.adapter.getMenuItemValues().indexOf(this.getValue());this.setSelectedIndex(t,!1,!0)},e.prototype.handleMenuOpened=function(){if(0!==this.adapter.getMenuItemValues().length){var t=this.getSelectedIndex(),e=t>=0?t:0;this.adapter.focusMenuItemAtIndex(e)}},e.prototype.handleMenuClosing=function(){this.adapter.setSelectAnchorAttr("aria-expanded","false")},e.prototype.handleMenuClosed=function(){this.adapter.removeClass(Zo.ACTIVATED),this.isMenuOpen=!1,this.adapter.isSelectAnchorFocused()||this.blur()},e.prototype.handleChange=function(){this.layout(),this.adapter.notifyChange(this.getValue()),this.adapter.hasClass(Zo.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.handleMenuItemAction=function(t){this.setSelectedIndex(t,!0)},e.prototype.handleFocus=function(){this.adapter.addClass(Zo.FOCUSED),this.layout(),this.adapter.activateBottomLine()},e.prototype.handleBlur=function(){this.isMenuOpen||this.blur()},e.prototype.handleClick=function(t){this.disabled||this.recentlyClicked||(this.setClickDebounceTimeout(),this.isMenuOpen?this.adapter.closeMenu():(this.adapter.setRippleCenter(t),this.openMenu()))},e.prototype.handleKeydown=function(t){if(!this.isMenuOpen&&this.adapter.hasClass(Zo.FOCUSED)){var e=qo(t)===go,i=qo(t)===vo,s=qo(t)===wo,n=qo(t)===Eo;if(!(t.ctrlKey||t.metaKey)&&(!i&&t.key&&1===t.key.length||i&&this.adapter.isTypeaheadInProgress())){var r=i?" ":t.key,o=this.adapter.typeaheadMatchItem(r,this.getSelectedIndex());return o>=0&&this.setSelectedIndex(o),void t.preventDefault()}(e||i||s||n)&&(this.openMenu(),t.preventDefault())}},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()){var e=this.adapter.hasClass(Zo.FOCUSED);if(t){var i=ta.LABEL_SCALE,s=this.adapter.getLabelWidth()*i;this.adapter.notchOutline(s)}else e||this.adapter.closeOutline()}},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.getUseDefaultValidation=function(){return this.useDefaultValidation},e.prototype.setUseDefaultValidation=function(t){this.useDefaultValidation=t},e.prototype.setValid=function(t){this.useDefaultValidation||(this.customValidity=t),this.adapter.setSelectAnchorAttr("aria-invalid",(!t).toString()),t?(this.adapter.removeClass(Zo.INVALID),this.adapter.removeMenuClass(Zo.MENU_INVALID)):(this.adapter.addClass(Zo.INVALID),this.adapter.addMenuClass(Zo.MENU_INVALID)),this.syncHelperTextValidity(t)},e.prototype.isValid=function(){return this.useDefaultValidation&&this.adapter.hasClass(Zo.REQUIRED)&&!this.adapter.hasClass(Zo.DISABLED)?this.getSelectedIndex()!==ta.UNSET_INDEX&&(0!==this.getSelectedIndex()||Boolean(this.getValue())):this.customValidity},e.prototype.setRequired=function(t){t?this.adapter.addClass(Zo.REQUIRED):this.adapter.removeClass(Zo.REQUIRED),this.adapter.setSelectAnchorAttr("aria-required",t.toString()),this.adapter.setLabelRequired(t)},e.prototype.getRequired=function(){return"true"===this.adapter.getSelectAnchorAttr("aria-required")},e.prototype.init=function(){var t=this.adapter.getAnchorElement();t&&(this.adapter.setMenuAnchorElement(t),this.adapter.setMenuAnchorCorner(Xo.BOTTOM_START)),this.adapter.setMenuWrapFocus(!1),this.setDisabled(this.adapter.hasClass(Zo.DISABLED)),this.syncHelperTextValidity(!this.adapter.hasClass(Zo.INVALID)),this.layout(),this.layoutOptions()},e.prototype.blur=function(){this.adapter.removeClass(Zo.FOCUSED),this.layout(),this.adapter.deactivateBottomLine(),this.adapter.hasClass(Zo.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.syncHelperTextValidity=function(t){if(this.helperText){this.helperText.setValidity(t);var e=this.helperText.isVisible(),i=this.helperText.getId();e&&i?this.adapter.setSelectAnchorAttr(Qo.ARIA_DESCRIBEDBY,i):this.adapter.removeSelectAnchorAttr(Qo.ARIA_DESCRIBEDBY)}},e.prototype.setClickDebounceTimeout=function(){var t=this;clearTimeout(this.clickDebounceTimeout),this.clickDebounceTimeout=setTimeout(function(){t.recentlyClicked=!1},ta.CLICK_DEBOUNCE_TIMEOUT_MS),this.recentlyClicked=!0},e}(uo); -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const ia=window,sa=ia.ShadowRoot&&(void 0===ia.ShadyCSS||ia.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,na=Symbol(),ra=new WeakMap;let oa=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==na)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(sa&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=ra.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&ra.set(e,t))}return t}toString(){return this.cssText}};const aa=sa?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new oa("string"==typeof t?t:t+"",void 0,na))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var la;const da=window,ca=da.trustedTypes,ha=ca?ca.emptyScript:"",pa=da.reactiveElementPolyfillSupport,ua={toAttribute(t,e){switch(e){case Boolean:t=t?ha:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},ma=(t,e)=>e!==t&&(e==e||t==t),fa={attribute:!0,type:String,converter:ua,reflect:!1,hasChanged:ma},ga="finalized";let va=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=fa){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||fa}static finalize(){if(this.hasOwnProperty(ga))return!1;this[ga]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(aa(t))}else void 0!==t&&e.push(aa(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{sa?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=ia.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=fa){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:ua).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:ua;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||ma)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var _a;va[ga]=!0,va.elementProperties=new Map,va.elementStyles=[],va.shadowRootOptions={mode:"open"},null==pa||pa({ReactiveElement:va}),(null!==(la=da.reactiveElementVersions)&&void 0!==la?la:da.reactiveElementVersions=[]).push("1.6.3");const ba=window,ya=ba.trustedTypes,xa=ya?ya.createPolicy("lit-html",{createHTML:t=>t}):void 0,$a="$lit$",wa=`lit$${(Math.random()+"").slice(9)}$`,Aa="?"+wa,Ea=`<${Aa}>`,Sa=document,Ca=()=>Sa.createComment(""),Ta=t=>null===t||"object"!=typeof t&&"function"!=typeof t,Ia=Array.isArray,Oa="[ \t\n\f\r]",ka=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ra=/-->/g,Pa=/>/g,La=RegExp(`>|${Oa}(?:([^\\s"'>=/]+)(${Oa}*=${Oa}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Ma=/'/g,Na=/"/g,Da=/^(?:script|style|textarea|title)$/i,Ha=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),Ua=Symbol.for("lit-noChange"),za=Symbol.for("lit-nothing"),Fa=new WeakMap,Ba=Sa.createTreeWalker(Sa,129,null,!1);function Va(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==xa?xa.createHTML(e):e}let ja=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=ka;for(let e=0;e"===l[0]?(o=null!=n?n:ka,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?La:'"'===l[3]?Na:Ma):o===Na||o===Ma?o=La:o===Ra||o===Pa?o=ka:(o=La,n=void 0);const h=o===La&&t[e+1].startsWith("/>")?" ":"";r+=o===ka?i+Ea:d>=0?(s.push(a),i.slice(0,d)+$a+i.slice(d)+wa+h):i+wa+(-2===d?(s.push(void 0),e):h)}return[Va(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),Ba.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=Ba.nextNode())&&l.length0){n.textContent=ya?ya.emptyScript:"";for(let i=0;iIa(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==za&&Ta(this._$AH)?this._$AA.nextSibling.data=t:this.$(Sa.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=ja.createElement(Va(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new qa(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=Fa.get(t.strings);return void 0===e&&Fa.set(t.strings,e=new ja(t)),e}T(e){Ia(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(Ca()),this.k(Ca()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=za}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=Wa(this,t,e,0),r=!Ta(t)||t!==this._$AH&&t!==Ua,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new Ya(e.insertBefore(Ca(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return Ua}};sl.finalized=!0,sl._$litElement$=!0,null===(el=globalThis.litElementHydrateSupport)||void 0===el||el.call(globalThis,{LitElement:sl});const nl=globalThis.litElementPolyfillSupport;null==nl||nl({LitElement:sl}),(null!==(il=globalThis.litElementVersions)&&void 0!==il?il:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const rl=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function ol(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):rl(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function al(t){return ol({...t,state:!0})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const ll=({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function dl(t,e){return ll({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var cl;null===(cl=window.HTMLSlotElement)||void 0===cl||cl.prototype.assignedElements; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const hl=1;let pl=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const ul=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends pl{constructor(t){var e;if(super(t),t.type!==hl||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter(e=>t[e]).join(" ")+" "}update(t,[e]){var i,s;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach(t=>{t in e||(n.remove(t),this.it.delete(t))});for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(s=this.nt)||void 0===s?void 0:s.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return Ua}}),ml=t=>null!=t?t:za,fl=(t={})=>{const e={};for(const i in t)e[i]=t[i];return Object.assign({badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:!0,valueMissing:!1},e)}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */class gl extends rs{constructor(){super(...arguments),this.mdcFoundationClass=ea,this.disabled=!1,this.outlined=!1,this.label="",this.outlineOpen=!1,this.outlineWidth=0,this.value="",this.name="",this.selectedText="",this.icon="",this.menuOpen=!1,this.helper="",this.validateOnInitialRender=!1,this.validationMessage="",this.required=!1,this.naturalMenuWidth=!1,this.isUiValid=!0,this.fixedMenuPosition=!1,this.typeaheadState={bufferClearTimeout:0,currentFirstChar:"",sortedIndexCursor:0,typeaheadBuffer:""},this.sortedIndexByFirstChar=new Map,this.menuElement_=null,this.listeners=[],this.onBodyClickBound=()=>{},this._menuUpdateComplete=null,this.valueSetDirectly=!1,this.validityTransform=null,this._validity=fl()}get items(){return this.menuElement_||(this.menuElement_=this.menuElement),this.menuElement_?this.menuElement_.items:[]}get selected(){const t=this.menuElement;return t?t.selected:null}get index(){const t=this.menuElement;return t?t.index:-1}get shouldRenderHelperText(){return!!this.helper||!!this.validationMessage}get validity(){return this._checkValidity(this.value),this._validity}render(){const t={"mdc-select--disabled":this.disabled,"mdc-select--no-label":!this.label,"mdc-select--filled":!this.outlined,"mdc-select--outlined":this.outlined,"mdc-select--with-leading-icon":!!this.icon,"mdc-select--required":this.required,"mdc-select--invalid":!this.isUiValid},e=this.label?"label":void 0,i=this.shouldRenderHelperText?"helper-text":void 0;return Ha` -
- - -
- ${this.renderRipple()} - ${this.outlined?this.renderOutline():this.renderLabel()} - ${this.renderLeadingIcon()} - - ${this.selectedText} - - - - - - - - - - ${this.renderLineRipple()} -
- ${this.renderMenu()} -
- ${this.renderHelperText()}`}renderMenu(){const t=this.getMenuClasses();return Ha` - - ${this.renderMenuContent()} - `}getMenuClasses(){return{"mdc-select__menu":!0,"mdc-menu":!0,"mdc-menu-surface":!0,"mdc-select__menu--invalid":!this.isUiValid}}renderMenuContent(){return Ha``}renderRipple(){return this.outlined?za:Ha` - - `}renderOutline(){return this.outlined?Ha` - - ${this.renderLabel()} - `:za}renderLabel(){return this.label?Ha` - ${this.label} - `:za}renderLeadingIcon(){return this.icon?Ha`
${this.icon}
`:za}renderLineRipple(){return this.outlined?za:Ha` - - `}renderHelperText(){if(!this.shouldRenderHelperText)return za;const t=this.validationMessage&&!this.isUiValid;return Ha` -

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

`}createAdapter(){return Object.assign(Object.assign({},Xi(this.mdcRoot)),{activateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.activate()},deactivateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.deactivate()},hasLabel:()=>!!this.label,floatLabel:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.float(t)},getLabelWidth:()=>this.labelElement?this.labelElement.floatingLabelFoundation.getWidth():0,setLabelRequired:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.setRequired(t)},hasOutline:()=>this.outlined,notchOutline:t=>{this.outlineElement&&!this.outlineOpen&&(this.outlineWidth=t,this.outlineOpen=!0)},closeOutline:()=>{this.outlineElement&&(this.outlineOpen=!1)},setRippleCenter:t=>{if(this.lineRippleElement){this.lineRippleElement.lineRippleFoundation.setRippleCenter(t)}},notifyChange:async t=>{if(!this.valueSetDirectly&&t===this.value)return;this.valueSetDirectly=!1,this.value=t,await this.updateComplete;const e=new Event("change",{bubbles:!0});this.dispatchEvent(e)},setSelectedText:t=>this.selectedText=t,isSelectAnchorFocused:()=>{const t=this.anchorElement;if(!t)return!1;return t.getRootNode().activeElement===t},getSelectAnchorAttr:t=>{const e=this.anchorElement;return e?e.getAttribute(t):null},setSelectAnchorAttr:(t,e)=>{const i=this.anchorElement;i&&i.setAttribute(t,e)},removeSelectAnchorAttr:t=>{const e=this.anchorElement;e&&e.removeAttribute(t)},openMenu:()=>{this.menuOpen=!0},closeMenu:()=>{this.menuOpen=!1},addMenuClass:()=>{},removeMenuClass:()=>{},getAnchorElement:()=>this.anchorElement,setMenuAnchorElement:()=>{},setMenuAnchorCorner:()=>{const t=this.menuElement;t&&(t.corner="BOTTOM_START")},setMenuWrapFocus:t=>{const e=this.menuElement;e&&(e.wrapFocus=t)},focusMenuItemAtIndex:t=>{const e=this.menuElement;if(!e)return;const i=e.items[t];i&&i.focus()},getMenuItemCount:()=>{const t=this.menuElement;return t?t.items.length:0},getMenuItemValues:()=>{const t=this.menuElement;if(!t)return[];return t.items.map(t=>t.value)},getMenuItemTextAtIndex:t=>{const e=this.menuElement;if(!e)return"";const i=e.items[t];return i?i.text:""},getSelectedIndex:()=>this.index,setSelectedIndex:()=>{},isTypeaheadInProgress:()=>Jr(this.typeaheadState),typeaheadMatchItem:(t,e)=>{if(!this.menuElement)return-1;const i={focusItemAtIndex:t=>{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e||this.menuElement.getFocusedItemIndex(),nextChar:t,sortedIndexByFirstChar:this.sortedIndexByFirstChar,skipFocus:!1,isItemAtIndexDisabled:t=>this.items[t].disabled},s=Kr(i,this.typeaheadState);return-1!==s&&this.select(s),s}})}checkValidity(){const t=this._checkValidity(this.value);if(!t){const t=new Event("invalid",{bubbles:!1,cancelable:!0});this.dispatchEvent(t)}return t}reportValidity(){const t=this.checkValidity();return this.isUiValid=t,t}_checkValidity(t){const e=this.formElement.validity;let i=fl(e);if(this.validityTransform){const e=this.validityTransform(t,i);i=Object.assign(Object.assign({},i),e)}return this._validity=i,this._validity.valid}setCustomValidity(t){this.validationMessage=t,this.formElement.setCustomValidity(t)}async getUpdateComplete(){await this._menuUpdateComplete;return await super.getUpdateComplete()}async firstUpdated(){const t=this.menuElement;if(t&&(this._menuUpdateComplete=t.updateComplete,await this._menuUpdateComplete),super.firstUpdated(),this.mdcFoundation.isValid=()=>!0,this.mdcFoundation.setValid=()=>{},this.mdcFoundation.setDisabled(this.disabled),this.validateOnInitialRender&&this.reportValidity(),!this.selected){!this.items.length&&this.slotElement&&this.slotElement.assignedNodes({flatten:!0}).length&&(await new Promise(t=>requestAnimationFrame(t)),await this.layout());const t=this.items.length&&""===this.items[0].value;if(!this.value&&t)return void this.select(0);this.selectByValue(this.value)}this.sortedIndexByFirstChar=Gr(this.items.length,t=>this.items[t].text)}onItemsUpdated(){this.sortedIndexByFirstChar=Gr(this.items.length,t=>this.items[t].text)}select(t){const e=this.menuElement;e&&e.select(t)}selectByValue(t){let e=-1;for(let i=0;i0,n=i&&this.index{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e,isTargetListItem:!!i&&i.hasAttribute("mwc-list-item"),sortedIndexByFirstChar:this.sortedIndexByFirstChar,isItemAtIndexDisabled:t=>this.items[t].disabled},this.typeaheadState)}async onSelected(t){this.mdcFoundation||await this.updateComplete,this.mdcFoundation.handleMenuItemAction(t.detail.index);const e=this.items[t.detail.index];e&&(this.value=e.value)}onOpened(){this.mdcFoundation&&(this.menuOpen=!0,this.mdcFoundation.handleMenuOpened())}onClosed(){this.mdcFoundation&&(this.menuOpen=!1,this.mdcFoundation.handleMenuClosed())}setFormData(t){this.name&&null!==this.selected&&t.append(this.name,this.value)}async layout(t=!0){this.mdcFoundation&&this.mdcFoundation.layout(),await this.updateComplete;const e=this.menuElement;e&&e.layout(t);const i=this.labelElement;if(!i)return void(this.outlineOpen=!1);const s=!!this.label&&!!this.value;if(i.floatingLabelFoundation.float(s),!this.outlined)return;this.outlineOpen=s,await this.updateComplete;const n=i.floatingLabelFoundation.getWidth();this.outlineOpen&&(this.outlineWidth=n)}async layoutOptions(){this.mdcFoundation&&this.mdcFoundation.layoutOptions()}}n([dl(".mdc-select")],gl.prototype,"mdcRoot",void 0),n([dl(".formElement")],gl.prototype,"formElement",void 0),n([dl("slot")],gl.prototype,"slotElement",void 0),n([dl("select")],gl.prototype,"nativeSelectElement",void 0),n([dl("input")],gl.prototype,"nativeInputElement",void 0),n([dl(".mdc-line-ripple")],gl.prototype,"lineRippleElement",void 0),n([dl(".mdc-floating-label")],gl.prototype,"labelElement",void 0),n([dl("mwc-notched-outline")],gl.prototype,"outlineElement",void 0),n([dl(".mdc-menu")],gl.prototype,"menuElement",void 0),n([dl(".mdc-select__anchor")],gl.prototype,"anchorElement",void 0),n([ol({type:Boolean,attribute:"disabled",reflect:!0}),os(function(t){this.mdcFoundation&&this.mdcFoundation.setDisabled(t)})],gl.prototype,"disabled",void 0),n([ol({type:Boolean}),os(function(t,e){void 0!==e&&this.outlined!==e&&this.layout(!1)})],gl.prototype,"outlined",void 0),n([ol({type:String}),os(function(t,e){void 0!==e&&this.label!==e&&this.layout(!1)})],gl.prototype,"label",void 0),n([al()],gl.prototype,"outlineOpen",void 0),n([al()],gl.prototype,"outlineWidth",void 0),n([ol({type:String}),os(function(t){if(this.mdcFoundation){const e=null===this.selected&&!!t,i=this.selected&&this.selected.value!==t;(e||i)&&this.selectByValue(t),this.reportValidity()}})],gl.prototype,"value",void 0),n([ol()],gl.prototype,"name",void 0),n([al()],gl.prototype,"selectedText",void 0),n([ol({type:String})],gl.prototype,"icon",void 0),n([al()],gl.prototype,"menuOpen",void 0),n([ol({type:String})],gl.prototype,"helper",void 0),n([ol({type:Boolean})],gl.prototype,"validateOnInitialRender",void 0),n([ol({type:String})],gl.prototype,"validationMessage",void 0),n([ol({type:Boolean})],gl.prototype,"required",void 0),n([ol({type:Boolean})],gl.prototype,"naturalMenuWidth",void 0),n([al()],gl.prototype,"isUiValid",void 0),n([ol({type:Boolean})],gl.prototype,"fixedMenuPosition",void 0),n([function(t){return ll({finisher:(e,i)=>{Object.assign(e.prototype[i],t)}})}({capture:!0})],gl.prototype,"handleTypeahead",null); -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const vl=window,_l=vl.ShadowRoot&&(void 0===vl.ShadyCSS||vl.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,bl=Symbol(),yl=new WeakMap;let xl=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==bl)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(_l&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=yl.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&yl.set(e,t))}return t}toString(){return this.cssText}};const $l=(t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new xl(i,t,bl)},wl=_l?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new xl("string"==typeof t?t:t+"",void 0,bl))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Al;const El=window,Sl=El.trustedTypes,Cl=Sl?Sl.emptyScript:"",Tl=El.reactiveElementPolyfillSupport,Il={toAttribute(t,e){switch(e){case Boolean:t=t?Cl:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},Ol=(t,e)=>e!==t&&(e==e||t==t),kl={attribute:!0,type:String,converter:Il,reflect:!1,hasChanged:Ol},Rl="finalized";let Pl=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=kl){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||kl}static finalize(){if(this.hasOwnProperty(Rl))return!1;this[Rl]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(wl(t))}else void 0!==t&&e.push(wl(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{_l?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=vl.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=kl){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:Il).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:Il;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||Ol)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var Ll;Pl[Rl]=!0,Pl.elementProperties=new Map,Pl.elementStyles=[],Pl.shadowRootOptions={mode:"open"},null==Tl||Tl({ReactiveElement:Pl}),(null!==(Al=El.reactiveElementVersions)&&void 0!==Al?Al:El.reactiveElementVersions=[]).push("1.6.3");const Ml=window,Nl=Ml.trustedTypes,Dl=Nl?Nl.createPolicy("lit-html",{createHTML:t=>t}):void 0,Hl="$lit$",Ul=`lit$${(Math.random()+"").slice(9)}$`,zl="?"+Ul,Fl=`<${zl}>`,Bl=document,Vl=()=>Bl.createComment(""),jl=t=>null===t||"object"!=typeof t&&"function"!=typeof t,Wl=Array.isArray,ql="[ \t\n\f\r]",Yl=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Xl=/-->/g,Gl=/>/g,Kl=RegExp(`>|${ql}(?:([^\\s"'>=/]+)(${ql}*=${ql}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Jl=/'/g,Zl=/"/g,Ql=/^(?:script|style|textarea|title)$/i,td=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),ed=Symbol.for("lit-noChange"),id=Symbol.for("lit-nothing"),sd=new WeakMap,nd=Bl.createTreeWalker(Bl,129,null,!1);function rd(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==Dl?Dl.createHTML(e):e}let od=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=Yl;for(let e=0;e"===l[0]?(o=null!=n?n:Yl,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?Kl:'"'===l[3]?Zl:Jl):o===Zl||o===Jl?o=Kl:o===Xl||o===Gl?o=Yl:(o=Kl,n=void 0);const h=o===Kl&&t[e+1].startsWith("/>")?" ":"";r+=o===Yl?i+Fl:d>=0?(s.push(a),i.slice(0,d)+Hl+i.slice(d)+Ul+h):i+Ul+(-2===d?(s.push(void 0),e):h)}return[rd(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),nd.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=nd.nextNode())&&l.length0){n.textContent=Nl?Nl.emptyScript:"";for(let i=0;iWl(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==id&&jl(this._$AH)?this._$AA.nextSibling.data=t:this.$(Bl.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=od.createElement(rd(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new ld(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=sd.get(t.strings);return void 0===e&&sd.set(t.strings,e=new od(t)),e}T(e){Wl(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(Vl()),this.k(Vl()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=id}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=ad(this,t,e,0),r=!jl(t)||t!==this._$AH&&t!==ed,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new dd(e.insertBefore(Vl(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return ed}};bd.finalized=!0,bd._$litElement$=!0,null===(vd=globalThis.litElementHydrateSupport)||void 0===vd||vd.call(globalThis,{LitElement:bd});const yd=globalThis.litElementPolyfillSupport;null==yd||yd({LitElement:bd}),(null!==(_d=globalThis.litElementVersions)&&void 0!==_d?_d:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const xd=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function $d(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):xd(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function wd(t){return $d({...t,state:!0})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Ad=({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function Ed(t,e){return Ad({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var Sd;const Cd=null!=(null===(Sd=window.HTMLSlotElement)||void 0===Sd?void 0:Sd.prototype.assignedElements)?(t,e)=>t.assignedElements(e):(t,e)=>t.assignedNodes(e).filter(t=>t.nodeType===Node.ELEMENT_NODE); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function Td(t,e,i){let s;return s={flatten:e},i?function(t){const{slot:e,selector:i}=null!=t?t:{};return Ad({descriptor:s=>({get(){var s;const n="slot"+(e?`[name=${e}]`:":not([name])"),r=null===(s=this.renderRoot)||void 0===s?void 0:s.querySelector(n),o=null!=r?Cd(r,t):[];return i?o.filter(t=>t.matches(i)):o},enumerable:!0,configurable:!0})})}({slot:t,flatten:e,selector:i}):Ad({descriptor:t=>({get(){var t,e;const i=null===(t=this.renderRoot)||void 0===t?void 0:t.querySelector("slot:not([name])");return null!==(e=null==i?void 0:i.assignedNodes(s))&&void 0!==e?e:[]},enumerable:!0,configurable:!0})})} -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Id=t=>null!=t?t:id; -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var Od=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),kd="Unknown",Rd="Backspace",Pd="Enter",Ld="Spacebar",Md="PageUp",Nd="PageDown",Dd="End",Hd="Home",Ud="ArrowLeft",zd="ArrowUp",Fd="ArrowRight",Bd="ArrowDown",Vd="Delete",jd="Escape",Wd="Tab",qd=new Set; -/** - * @license - * Copyright 2020 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */qd.add(Rd),qd.add(Pd),qd.add(Ld),qd.add(Md),qd.add(Nd),qd.add(Dd),qd.add(Hd),qd.add(Ud),qd.add(zd),qd.add(Fd),qd.add(Bd),qd.add(Vd),qd.add(jd),qd.add(Wd);var Yd=8,Xd=13,Gd=32,Kd=33,Jd=34,Zd=35,Qd=36,tc=37,ec=38,ic=39,sc=40,nc=46,rc=27,oc=9,ac=new Map;ac.set(Yd,Rd),ac.set(Xd,Pd),ac.set(Gd,Ld),ac.set(Kd,Md),ac.set(Jd,Nd),ac.set(Zd,Dd),ac.set(Qd,Hd),ac.set(tc,Ud),ac.set(ec,zd),ac.set(ic,Fd),ac.set(sc,Bd),ac.set(nc,Vd),ac.set(rc,jd),ac.set(oc,Wd);var lc=new Set;function dc(t){var e=t.key;if(qd.has(e))return e;var i=ac.get(t.keyCode);return i||kd} -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */lc.add(Md),lc.add(Nd),lc.add(Dd),lc.add(Hd),lc.add(Ud),lc.add(zd),lc.add(Fd),lc.add(Bd);const cc=(t,e)=>t-e,hc=["input","button","textarea","select"];function pc(t){return t instanceof Set}const uc=t=>{const e=t===qr.UNSET_INDEX?new Set:t;return pc(e)?new Set(e):new Set([e])};class mc extends Od{constructor(t){super(Object.assign(Object.assign({},mc.defaultAdapter),t)),this.isMulti_=!1,this.wrapFocus_=!1,this.isVertical_=!0,this.selectedIndex_=qr.UNSET_INDEX,this.focusedItemIndex_=qr.UNSET_INDEX,this.useActivatedClass_=!1,this.ariaCurrentAttrValue_=null}static get strings(){return Wr}static get numbers(){return qr}static get defaultAdapter(){return{focusItemAtIndex:()=>{},getFocusedElementIndex:()=>0,getListItemCount:()=>0,isFocusInsideList:()=>!1,isRootFocused:()=>!1,notifyAction:()=>{},notifySelected:()=>{},getSelectedStateForElementIndex:()=>!1,setDisabledStateForElementIndex:()=>{},getDisabledStateForElementIndex:()=>!1,setSelectedStateForElementIndex:()=>{},setActivatedStateForElementIndex:()=>{},setTabIndexForElementIndex:()=>{},setAttributeForElementIndex:()=>{},getAttributeForElementIndex:()=>null}}setWrapFocus(t){this.wrapFocus_=t}setMulti(t){this.isMulti_=t;const e=this.selectedIndex_;if(t){if(!pc(e)){const t=e===qr.UNSET_INDEX;this.selectedIndex_=t?new Set:new Set([e])}}else if(pc(e))if(e.size){const t=Array.from(e).sort(cc);this.selectedIndex_=t[0]}else this.selectedIndex_=qr.UNSET_INDEX}setVerticalOrientation(t){this.isVertical_=t}setUseActivatedClass(t){this.useActivatedClass_=t}getSelectedIndex(){return this.selectedIndex_}setSelectedIndex(t){this.isIndexValid_(t)&&(this.isMulti_?this.setMultiSelectionAtIndex_(uc(t)):this.setSingleSelectionAtIndex_(t))}handleFocusIn(t,e){e>=0&&this.adapter.setTabIndexForElementIndex(e,0)}handleFocusOut(t,e){e>=0&&this.adapter.setTabIndexForElementIndex(e,-1),setTimeout(()=>{this.adapter.isFocusInsideList()||this.setTabindexToFirstSelectedItem_()},0)}handleKeydown(t,e,i){const s="ArrowLeft"===dc(t),n="ArrowUp"===dc(t),r="ArrowRight"===dc(t),o="ArrowDown"===dc(t),a="Home"===dc(t),l="End"===dc(t),d="Enter"===dc(t),c="Spacebar"===dc(t);if(this.adapter.isRootFocused())return void(n||l?(t.preventDefault(),this.focusLastElement()):(o||a)&&(t.preventDefault(),this.focusFirstElement()));let h,p=this.adapter.getFocusedElementIndex();if(!(-1===p&&(p=i,p<0))){if(this.isVertical_&&o||!this.isVertical_&&r)this.preventDefaultEvent(t),h=this.focusNextElement(p);else if(this.isVertical_&&n||!this.isVertical_&&s)this.preventDefaultEvent(t),h=this.focusPrevElement(p);else if(a)this.preventDefaultEvent(t),h=this.focusFirstElement();else if(l)this.preventDefaultEvent(t),h=this.focusLastElement();else if((d||c)&&e){const e=t.target;if(e&&"A"===e.tagName&&d)return;this.preventDefaultEvent(t),this.setSelectedIndexOnAction_(p,!0)}this.focusedItemIndex_=p,void 0!==h&&(this.setTabindexAtIndex_(h),this.focusedItemIndex_=h)}}handleSingleSelection(t,e,i){t!==qr.UNSET_INDEX&&(this.setSelectedIndexOnAction_(t,e,i),this.setTabindexAtIndex_(t),this.focusedItemIndex_=t)}focusNextElement(t){let e=t+1;if(e>=this.adapter.getListItemCount()){if(!this.wrapFocus_)return t;e=0}return this.adapter.focusItemAtIndex(e),e}focusPrevElement(t){let e=t-1;if(e<0){if(!this.wrapFocus_)return t;e=this.adapter.getListItemCount()-1}return this.adapter.focusItemAtIndex(e),e}focusFirstElement(){return this.adapter.focusItemAtIndex(0),0}focusLastElement(){const t=this.adapter.getListItemCount()-1;return this.adapter.focusItemAtIndex(t),t}setEnabled(t,e){this.isIndexValid_(t)&&this.adapter.setDisabledStateForElementIndex(t,!e)}preventDefaultEvent(t){const e=`${t.target.tagName}`.toLowerCase();-1===hc.indexOf(e)&&t.preventDefault()}setSingleSelectionAtIndex_(t,e=!0){this.selectedIndex_!==t&&(this.selectedIndex_!==qr.UNSET_INDEX&&(this.adapter.setSelectedStateForElementIndex(this.selectedIndex_,!1),this.useActivatedClass_&&this.adapter.setActivatedStateForElementIndex(this.selectedIndex_,!1)),e&&this.adapter.setSelectedStateForElementIndex(t,!0),this.useActivatedClass_&&this.adapter.setActivatedStateForElementIndex(t,!0),this.setAriaForSingleSelectionAtIndex_(t),this.selectedIndex_=t,this.adapter.notifySelected(t))}setMultiSelectionAtIndex_(t,e=!0){const i=((t,e)=>{const i=Array.from(t),s=Array.from(e),n={added:[],removed:[]},r=i.sort(cc),o=s.sort(cc);let a=0,l=0;for(;a=0&&this.focusedItemIndex_!==t&&this.adapter.setTabIndexForElementIndex(this.focusedItemIndex_,-1),this.adapter.setTabIndexForElementIndex(t,0)}setTabindexToFirstSelectedItem_(){let t=0;"number"==typeof this.selectedIndex_&&this.selectedIndex_!==qr.UNSET_INDEX?t=this.selectedIndex_:pc(this.selectedIndex_)&&this.selectedIndex_.size>0&&(t=Math.min(...this.selectedIndex_)),this.setTabindexAtIndex_(t)}isIndexValid_(t){if(t instanceof Set){if(!this.isMulti_)throw new Error("MDCListFoundation: Array of index is only supported for checkbox based list");if(0===t.size)return!0;{let e=!1;for(const i of t)if(e=this.isIndexInRange_(i),e)break;return e}}if("number"==typeof t){if(this.isMulti_)throw new Error("MDCListFoundation: Expected array of index for checkbox based list but got number: "+t);return t===qr.UNSET_INDEX||this.isIndexInRange_(t)}return!1}isIndexInRange_(t){const e=this.adapter.getListItemCount();return t>=0&&tt.hasAttribute("mwc-list-item");function gc(){const t=this.itemsReadyResolver;this.itemsReady=new Promise(t=>this.itemsReadyResolver=t),t()}class vc extends Qi{constructor(){super(),this.mdcAdapter=null,this.mdcFoundationClass=mc,this.activatable=!1,this.multi=!1,this.wrapFocus=!1,this.itemRoles=null,this.innerRole=null,this.innerAriaLabel=null,this.rootTabbable=!1,this.previousTabindex=null,this.noninteractive=!1,this.itemsReadyResolver=()=>{},this.itemsReady=Promise.resolve([]),this.items_=[];const t=function(t,e=50){let i;return function(s=!0){clearTimeout(i),i=setTimeout(()=>{t(s)},e)}}(this.layout.bind(this));this.debouncedLayout=(e=!0)=>{gc.call(this),t(e)}}async getUpdateComplete(){const t=await super.getUpdateComplete();return await this.itemsReady,t}get items(){return this.items_}updateItems(){var t;const e=null!==(t=this.assignedElements)&&void 0!==t?t:[],i=[];for(const t of e)fc(t)&&(i.push(t),t._managingList=this),t.hasAttribute("divider")&&!t.hasAttribute("role")&&t.setAttribute("role","separator");this.items_=i;const s=new Set;if(this.items_.forEach((t,e)=>{this.itemRoles?t.setAttribute("role",this.itemRoles):t.removeAttribute("role"),t.selected&&s.add(e)}),this.multi)this.select(s);else{const t=s.size?s.entries().next().value[1]:-1;this.select(t)}const n=new Event("items-updated",{bubbles:!0,composed:!0});this.dispatchEvent(n)}get selected(){const t=this.index;if(!pc(t))return-1===t?null:this.items[t];const e=[];for(const i of t)e.push(this.items[i]);return e}get index(){return this.mdcFoundation?this.mdcFoundation.getSelectedIndex():-1}render(){const t=null===this.innerRole?void 0:this.innerRole,e=null===this.innerAriaLabel?void 0:this.innerAriaLabel,i=this.rootTabbable?"0":"-1";return td` - -
    - - ${this.renderPlaceholder()} -
- `}renderPlaceholder(){var t;const e=null!==(t=this.assignedElements)&&void 0!==t?t:[];return void 0!==this.emptyMessage&&0===e.length?td` - ${this.emptyMessage} - `:null}firstUpdated(){super.firstUpdated(),this.items.length||(this.mdcFoundation.setMulti(this.multi),this.layout())}onFocusIn(t){if(this.mdcFoundation&&this.mdcRoot){const e=this.getIndexOfTarget(t);this.mdcFoundation.handleFocusIn(t,e)}}onFocusOut(t){if(this.mdcFoundation&&this.mdcRoot){const e=this.getIndexOfTarget(t);this.mdcFoundation.handleFocusOut(t,e)}}onKeydown(t){if(this.mdcFoundation&&this.mdcRoot){const e=this.getIndexOfTarget(t),i=t.target,s=fc(i);this.mdcFoundation.handleKeydown(t,s,e)}}onRequestSelected(t){if(this.mdcFoundation){let e=this.getIndexOfTarget(t);if(-1===e&&(this.layout(),e=this.getIndexOfTarget(t),-1===e))return;if(this.items[e].disabled)return;const i=t.detail.selected,s=t.detail.source;this.mdcFoundation.handleSingleSelection(e,"interaction"===s,i),t.stopPropagation()}}getIndexOfTarget(t){const e=this.items,i=t.composedPath();for(const t of i){let i=-1;if(Yi(t)&&fc(t)&&(i=e.indexOf(t)),-1!==i)return i}return-1}createAdapter(){return this.mdcAdapter={getListItemCount:()=>this.mdcRoot?this.items.length:0,getFocusedElementIndex:this.getFocusedItemIndex,getAttributeForElementIndex:(t,e)=>{if(!this.mdcRoot)return"";const i=this.items[t];return i?i.getAttribute(e):""},setAttributeForElementIndex:(t,e,i)=>{if(!this.mdcRoot)return;const s=this.items[t];s&&s.setAttribute(e,i)},focusItemAtIndex:t=>{const e=this.items[t];e&&e.focus()},setTabIndexForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.tabindex=e)},notifyAction:t=>{const e={bubbles:!0,composed:!0};e.detail={index:t};const i=new CustomEvent("action",e);this.dispatchEvent(i)},notifySelected:(t,e)=>{const i={bubbles:!0,composed:!0};i.detail={index:t,diff:e};const s=new CustomEvent("selected",i);this.dispatchEvent(s)},isFocusInsideList:()=>Zi(this),isRootFocused:()=>{const t=this.mdcRoot;return t.getRootNode().activeElement===t},setDisabledStateForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.disabled=e)},getDisabledStateForElementIndex:t=>{const e=this.items[t];return!!e&&e.disabled},setSelectedStateForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.selected=e)},getSelectedStateForElementIndex:t=>{const e=this.items[t];return!!e&&e.selected},setActivatedStateForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.activated=e)}},this.mdcAdapter}selectUi(t,e=!1){const i=this.items[t];i&&(i.selected=!0,i.activated=e)}deselectUi(t){const e=this.items[t];e&&(e.selected=!1,e.activated=!1)}select(t){this.mdcFoundation&&this.mdcFoundation.setSelectedIndex(t)}toggle(t,e){this.multi&&this.mdcFoundation.toggleMultiAtIndex(t,e)}onListItemConnected(t){const e=t.target;this.layout(-1===this.items.indexOf(e))}layout(t=!0){t&&this.updateItems();const e=this.items[0];for(const t of this.items)t.tabindex=-1;e&&(this.noninteractive?this.previousTabindex||(this.previousTabindex=e):e.tabindex=0),this.itemsReadyResolver()}getFocusedItemIndex(){if(!this.mdcRoot)return-1;if(!this.items.length)return-1;const t=Ji();if(!t.length)return-1;for(let e=t.length-1;e>=0;e--){const i=t[e];if(fc(i))return this.items.indexOf(i)}return-1}focusItemAtIndex(t){for(const t of this.items)if(0===t.tabindex){t.tabindex=-1;break}this.items[t].tabindex=0,this.items[t].focus()}focus(){const t=this.mdcRoot;t&&t.focus()}blur(){const t=this.mdcRoot;t&&t.blur()}}n([$d({type:String})],vc.prototype,"emptyMessage",void 0),n([Ed(".mdc-deprecated-list")],vc.prototype,"mdcRoot",void 0),n([Td("",!0,"*")],vc.prototype,"assignedElements",void 0),n([Td("",!0,'[tabindex="0"]')],vc.prototype,"tabbableElements",void 0),n([$d({type:Boolean}),os(function(t){this.mdcFoundation&&this.mdcFoundation.setUseActivatedClass(t)})],vc.prototype,"activatable",void 0),n([$d({type:Boolean}),os(function(t,e){this.mdcFoundation&&this.mdcFoundation.setMulti(t),void 0!==e&&this.layout()})],vc.prototype,"multi",void 0),n([$d({type:Boolean}),os(function(t){this.mdcFoundation&&this.mdcFoundation.setWrapFocus(t)})],vc.prototype,"wrapFocus",void 0),n([$d({type:String}),os(function(t,e){void 0!==e&&this.updateItems()})],vc.prototype,"itemRoles",void 0),n([$d({type:String})],vc.prototype,"innerRole",void 0),n([$d({type:String})],vc.prototype,"innerAriaLabel",void 0),n([$d({type:Boolean})],vc.prototype,"rootTabbable",void 0),n([$d({type:Boolean,reflect:!0}),os(function(t){var e,i;if(t){const t=null!==(i=null===(e=this.tabbableElements)||void 0===e?void 0:e[0])&&void 0!==i?i:null;this.previousTabindex=t,t&&t.setAttribute("tabindex","-1")}else!t&&this.previousTabindex&&(this.previousTabindex.setAttribute("tabindex","0"),this.previousTabindex=null)})],vc.prototype,"noninteractive",void 0); -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class _c{constructor(t){this.startPress=e=>{t().then(t=>{t&&t.startPress(e)})},this.endPress=()=>{t().then(t=>{t&&t.endPress()})},this.startFocus=()=>{t().then(t=>{t&&t.startFocus()})},this.endFocus=()=>{t().then(t=>{t&&t.endFocus()})},this.startHover=()=>{t().then(t=>{t&&t.startHover()})},this.endHover=()=>{t().then(t=>{t&&t.endHover()})}}} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const bc=1;let yc=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const xc=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends yc{constructor(t){var e;if(super(t),t.type!==bc||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter(e=>t[e]).join(" ")+" "}update(t,[e]){var i,s;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach(t=>{t in e||(n.remove(t),this.it.delete(t))});for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(s=this.nt)||void 0===s?void 0:s.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return ed}}); -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class $c extends bd{constructor(){super(...arguments),this.value="",this.group=null,this.tabindex=-1,this.disabled=!1,this.twoline=!1,this.activated=!1,this.graphic=null,this.multipleGraphics=!1,this.hasMeta=!1,this.noninteractive=!1,this.selected=!1,this.shouldRenderRipple=!1,this._managingList=null,this.boundOnClick=this.onClick.bind(this),this._firstChanged=!0,this._skipPropRequest=!1,this.rippleHandlers=new _c(()=>(this.shouldRenderRipple=!0,this.ripple)),this.listeners=[{target:this,eventNames:["click"],cb:()=>{this.onClick()}},{target:this,eventNames:["mouseenter"],cb:this.rippleHandlers.startHover},{target:this,eventNames:["mouseleave"],cb:this.rippleHandlers.endHover},{target:this,eventNames:["focus"],cb:this.rippleHandlers.startFocus},{target:this,eventNames:["blur"],cb:this.rippleHandlers.endFocus},{target:this,eventNames:["mousedown","touchstart"],cb:t=>{const e=t.type;this.onDown("mousedown"===e?"mouseup":"touchend",t)}}]}get text(){const t=this.textContent;return t?t.trim():""}render(){const t=this.renderText(),e=this.graphic?this.renderGraphic():td``,i=this.hasMeta?this.renderMeta():td``;return td` - ${this.renderRipple()} - ${e} - ${t} - ${i}`}renderRipple(){return this.shouldRenderRipple?td` - - `:this.activated?td`
`:""}renderGraphic(){const t={multi:this.multipleGraphics};return td` - - - `}renderMeta(){return td` - - - `}renderText(){const t=this.twoline?this.renderTwoline():this.renderSingleLine();return td` - - ${t} - `}renderSingleLine(){return td``}renderTwoline(){return td` - - - - - - - `}onClick(){this.fireRequestSelected(!this.selected,"interaction")}onDown(t,e){const i=()=>{window.removeEventListener(t,i),this.rippleHandlers.endPress()};window.addEventListener(t,i),this.rippleHandlers.startPress(e)}fireRequestSelected(t,e){if(this.noninteractive)return;const i=new CustomEvent("request-selected",{bubbles:!0,composed:!0,detail:{source:e,selected:t}});this.dispatchEvent(i)}connectedCallback(){super.connectedCallback(),this.noninteractive||this.setAttribute("mwc-list-item","");for(const t of this.listeners)for(const e of t.eventNames)t.target.addEventListener(e,t.cb,{passive:!0})}disconnectedCallback(){super.disconnectedCallback();for(const t of this.listeners)for(const e of t.eventNames)t.target.removeEventListener(e,t.cb);this._managingList&&(this._managingList.debouncedLayout?this._managingList.debouncedLayout(!0):this._managingList.layout(!0))}firstUpdated(){const t=new Event("list-item-rendered",{bubbles:!0,composed:!0});this.dispatchEvent(t)}}n([Ed("slot")],$c.prototype,"slotElement",void 0),n([function(t){return Ad({descriptor:e=>({async get(){var e;return await this.updateComplete,null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t)},enumerable:!0,configurable:!0})})}("mwc-ripple")],$c.prototype,"ripple",void 0),n([$d({type:String})],$c.prototype,"value",void 0),n([$d({type:String,reflect:!0})],$c.prototype,"group",void 0),n([$d({type:Number,reflect:!0})],$c.prototype,"tabindex",void 0),n([$d({type:Boolean,reflect:!0}),os(function(t){t?this.setAttribute("aria-disabled","true"):this.setAttribute("aria-disabled","false")})],$c.prototype,"disabled",void 0),n([$d({type:Boolean,reflect:!0})],$c.prototype,"twoline",void 0),n([$d({type:Boolean,reflect:!0})],$c.prototype,"activated",void 0),n([$d({type:String,reflect:!0})],$c.prototype,"graphic",void 0),n([$d({type:Boolean})],$c.prototype,"multipleGraphics",void 0),n([$d({type:Boolean})],$c.prototype,"hasMeta",void 0),n([$d({type:Boolean,reflect:!0}),os(function(t){t?(this.removeAttribute("aria-checked"),this.removeAttribute("mwc-list-item"),this.selected=!1,this.activated=!1,this.tabIndex=-1):this.setAttribute("mwc-list-item","")})],$c.prototype,"noninteractive",void 0),n([$d({type:Boolean,reflect:!0}),os(function(t){const e=this.getAttribute("role"),i="gridcell"===e||"option"===e||"row"===e||"tab"===e;i&&t?this.setAttribute("aria-selected","true"):i&&this.setAttribute("aria-selected","false"),this._firstChanged?this._firstChanged=!1:this._skipPropRequest||this.fireRequestSelected(t,"property")})],$c.prototype,"selected",void 0),n([wd()],$c.prototype,"shouldRenderRipple",void 0),n([wd()],$c.prototype,"_managingList",void 0); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var wc,Ac={MENU_SELECTED_LIST_ITEM:"mdc-menu-item--selected",MENU_SELECTION_GROUP:"mdc-menu__selection-group",ROOT:"mdc-menu"},Ec={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"},Sc={FOCUS_ROOT_INDEX:-1};!function(t){t[t.NONE=0]="NONE",t[t.LIST_ROOT=1]="LIST_ROOT",t[t.FIRST_ITEM=2]="FIRST_ITEM",t[t.LAST_ITEM=3]="LAST_ITEM"}(wc||(wc={})); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Cc=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),Tc=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),Ic=function(t){function e(i){var n=t.call(this,s(s({},e.defaultAdapter),i))||this;return n.isSurfaceOpen=!1,n.isQuickOpen=!1,n.isHoistedElement=!1,n.isFixedPosition=!1,n.isHorizontallyCenteredOnViewport=!1,n.maxHeight=0,n.openBottomBias=0,n.openAnimationEndTimerId=0,n.closeAnimationEndTimerId=0,n.animationRequestId=0,n.anchorCorner=Xo.TOP_START,n.originCorner=Xo.TOP_START,n.anchorMargin={top:0,right:0,bottom:0,left:0},n.position={x:0,y:0},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Go},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Ko},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return Jo},enumerable:!1,configurable:!0}),Object.defineProperty(e,"Corner",{get:function(){return Xo},enumerable:!1,configurable:!0}),Object.defineProperty(e,"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(){},notifyClosing:function(){},notifyOpen:function(){},notifyOpening:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t=e.cssClasses,i=t.ROOT,s=t.OPEN;if(!this.adapter.hasClass(i))throw new Error(i+" class required in root element.");this.adapter.hasClass(s)&&(this.isSurfaceOpen=!0)},e.prototype.destroy=function(){clearTimeout(this.openAnimationEndTimerId),clearTimeout(this.closeAnimationEndTimerId),cancelAnimationFrame(this.animationRequestId)},e.prototype.setAnchorCorner=function(t){this.anchorCorner=t},e.prototype.flipCornerHorizontally=function(){this.originCorner=this.originCorner^Yo.RIGHT},e.prototype.setAnchorMargin=function(t){this.anchorMargin.top=t.top||0,this.anchorMargin.right=t.right||0,this.anchorMargin.bottom=t.bottom||0,this.anchorMargin.left=t.left||0},e.prototype.setIsHoisted=function(t){this.isHoistedElement=t},e.prototype.setFixedPosition=function(t){this.isFixedPosition=t},e.prototype.isFixed=function(){return this.isFixedPosition},e.prototype.setAbsolutePosition=function(t,e){this.position.x=this.isFinite(t)?t:0,this.position.y=this.isFinite(e)?e:0},e.prototype.setIsHorizontallyCenteredOnViewport=function(t){this.isHorizontallyCenteredOnViewport=t},e.prototype.setQuickOpen=function(t){this.isQuickOpen=t},e.prototype.setMaxHeight=function(t){this.maxHeight=t},e.prototype.setOpenBottomBias=function(t){this.openBottomBias=t},e.prototype.isOpen=function(){return this.isSurfaceOpen},e.prototype.open=function(){var t=this;this.isSurfaceOpen||(this.adapter.notifyOpening(),this.adapter.saveFocus(),this.isQuickOpen?(this.isSurfaceOpen=!0,this.adapter.addClass(e.cssClasses.OPEN),this.dimensions=this.adapter.getInnerDimensions(),this.autoposition(),this.adapter.notifyOpen()):(this.adapter.addClass(e.cssClasses.ANIMATING_OPEN),this.animationRequestId=requestAnimationFrame(function(){t.dimensions=t.adapter.getInnerDimensions(),t.autoposition(),t.adapter.addClass(e.cssClasses.OPEN),t.openAnimationEndTimerId=setTimeout(function(){t.openAnimationEndTimerId=0,t.adapter.removeClass(e.cssClasses.ANIMATING_OPEN),t.adapter.notifyOpen()},Jo.TRANSITION_OPEN_DURATION)}),this.isSurfaceOpen=!0))},e.prototype.close=function(t){var i=this;if(void 0===t&&(t=!1),this.isSurfaceOpen){if(this.adapter.notifyClosing(),this.isQuickOpen)return this.isSurfaceOpen=!1,t||this.maybeRestoreFocus(),this.adapter.removeClass(e.cssClasses.OPEN),this.adapter.removeClass(e.cssClasses.IS_OPEN_BELOW),void this.adapter.notifyClose();this.adapter.addClass(e.cssClasses.ANIMATING_CLOSED),requestAnimationFrame(function(){i.adapter.removeClass(e.cssClasses.OPEN),i.adapter.removeClass(e.cssClasses.IS_OPEN_BELOW),i.closeAnimationEndTimerId=setTimeout(function(){i.closeAnimationEndTimerId=0,i.adapter.removeClass(e.cssClasses.ANIMATING_CLOSED),i.adapter.notifyClose()},Jo.TRANSITION_CLOSE_DURATION)}),this.isSurfaceOpen=!1,t||this.maybeRestoreFocus()}},e.prototype.handleBodyClick=function(t){var e=t.target;this.adapter.isElementInContainer(e)||this.close()},e.prototype.handleKeydown=function(t){var e=t.keyCode;("Escape"===t.key||27===e)&&this.close()},e.prototype.autoposition=function(){var t;this.measurements=this.getAutoLayoutmeasurements();var i=this.getoriginCorner(),s=this.getMenuSurfaceMaxHeight(i),n=this.hasBit(i,Yo.BOTTOM)?"bottom":"top",r=this.hasBit(i,Yo.RIGHT)?"right":"left",o=this.getHorizontalOriginOffset(i),a=this.getVerticalOriginOffset(i),l=this.measurements,d=l.anchorSize,c=l.surfaceSize,h=((t={})[r]=o,t[n]=a,t);d.width/c.width>Jo.ANCHOR_TO_MENU_SURFACE_WIDTH_RATIO&&(r="center"),(this.isHoistedElement||this.isFixedPosition)&&this.adjustPositionForHoistedElement(h),this.adapter.setTransformOrigin(r+" "+n),this.adapter.setPosition(h),this.adapter.setMaxHeight(s?s+"px":""),this.hasBit(i,Yo.BOTTOM)||this.adapter.addClass(e.cssClasses.IS_OPEN_BELOW)},e.prototype.getAutoLayoutmeasurements=function(){var t=this.adapter.getAnchorDimensions(),e=this.adapter.getBodyDimensions(),i=this.adapter.getWindowDimensions(),s=this.adapter.getWindowScroll();return t||(t={top:this.position.y,right:this.position.x,bottom:this.position.y,left:this.position.x,width:0,height:0}),{anchorSize:t,bodySize:e,surfaceSize:this.dimensions,viewportDistance:{top:t.top,right:i.width-t.right,bottom:i.height-t.bottom,left:t.left},viewportSize:i,windowScroll:s}},e.prototype.getoriginCorner=function(){var t,i,s=this.originCorner,n=this.measurements,r=n.viewportDistance,o=n.anchorSize,a=n.surfaceSize,l=e.numbers.MARGIN_TO_EDGE;this.hasBit(this.anchorCorner,Yo.BOTTOM)?(t=r.top-l+this.anchorMargin.bottom,i=r.bottom-l-this.anchorMargin.bottom):(t=r.top-l+this.anchorMargin.top,i=r.bottom-l+o.height-this.anchorMargin.top),!(i-a.height>0)&&t>i+this.openBottomBias&&(s=this.setBit(s,Yo.BOTTOM));var d,c,h=this.adapter.isRtl(),p=this.hasBit(this.anchorCorner,Yo.FLIP_RTL),u=this.hasBit(this.anchorCorner,Yo.RIGHT)||this.hasBit(s,Yo.RIGHT),m=!1;(m=h&&p?!u:u)?(d=r.left+o.width+this.anchorMargin.right,c=r.right-this.anchorMargin.right):(d=r.left+this.anchorMargin.left,c=r.right+o.width-this.anchorMargin.left);var f=d-a.width>0,g=c-a.width>0,v=this.hasBit(s,Yo.FLIP_RTL)&&this.hasBit(s,Yo.RIGHT);return g&&v&&h||!f&&v?s=this.unsetBit(s,Yo.RIGHT):(f&&m&&h||f&&!m&&u||!g&&d>=c)&&(s=this.setBit(s,Yo.RIGHT)),s},e.prototype.getMenuSurfaceMaxHeight=function(t){if(this.maxHeight>0)return this.maxHeight;var i=this.measurements.viewportDistance,s=0,n=this.hasBit(t,Yo.BOTTOM),r=this.hasBit(this.anchorCorner,Yo.BOTTOM),o=e.numbers.MARGIN_TO_EDGE;return n?(s=i.top+this.anchorMargin.top-o,r||(s+=this.measurements.anchorSize.height)):(s=i.bottom-this.anchorMargin.bottom+this.measurements.anchorSize.height-o,r&&(s-=this.measurements.anchorSize.height)),s},e.prototype.getHorizontalOriginOffset=function(t){var e=this.measurements.anchorSize,i=this.hasBit(t,Yo.RIGHT),s=this.hasBit(this.anchorCorner,Yo.RIGHT);if(i){var n=s?e.width-this.anchorMargin.left:this.anchorMargin.right;return this.isHoistedElement||this.isFixedPosition?n-(this.measurements.viewportSize.width-this.measurements.bodySize.width):n}return s?e.width-this.anchorMargin.right:this.anchorMargin.left},e.prototype.getVerticalOriginOffset=function(t){var e=this.measurements.anchorSize,i=this.hasBit(t,Yo.BOTTOM),s=this.hasBit(this.anchorCorner,Yo.BOTTOM);return i?s?e.height-this.anchorMargin.top:-this.anchorMargin.bottom:s?e.height+this.anchorMargin.bottom:this.anchorMargin.top},e.prototype.adjustPositionForHoistedElement=function(t){var e,i,s=this.measurements,n=s.windowScroll,o=s.viewportDistance,a=s.surfaceSize,l=s.viewportSize,d=Object.keys(t);try{for(var c=r(d),h=c.next();!h.done;h=c.next()){var p=h.value,u=t[p]||0;!this.isHorizontallyCenteredOnViewport||"left"!==p&&"right"!==p?(u+=o[p],this.isFixedPosition||("top"===p?u+=n.y:"bottom"===p?u-=n.y:"left"===p?u+=n.x:u-=n.x),t[p]=u):t[p]=(l.width-a.width)/2}}catch(t){e={error:t}}finally{try{h&&!h.done&&(i=c.return)&&i.call(c)}finally{if(e)throw e.error}}},e.prototype.maybeRestoreFocus=function(){var t=this,e=this.adapter.isFocused(),i=this.adapter.getOwnerDocument?this.adapter.getOwnerDocument():document,s=i.activeElement&&this.adapter.isElementInContainer(i.activeElement);(e||s)&&setTimeout(function(){t.adapter.restoreFocus()},Jo.TOUCH_EVENT_WAIT_MS)},e.prototype.hasBit=function(t,e){return Boolean(t&e)},e.prototype.setBit=function(t,e){return t|e},e.prototype.unsetBit=function(t,e){return t^e},e.prototype.isFinite=function(t){return"number"==typeof t&&isFinite(t)},e}(Tc),Oc=function(t){function e(i){var n=t.call(this,s(s({},e.defaultAdapter),i))||this;return n.closeAnimationEndTimerId=0,n.defaultFocusState=wc.LIST_ROOT,n.selectedIndex=-1,n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Ac},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Ec},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return Sc},enumerable:!1,configurable:!0}),Object.defineProperty(e,"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}),e.prototype.destroy=function(){this.closeAnimationEndTimerId&&clearTimeout(this.closeAnimationEndTimerId),this.adapter.closeSurface()},e.prototype.handleKeydown=function(t){var e=t.key,i=t.keyCode;("Tab"===e||9===i)&&this.adapter.closeSurface(!0)},e.prototype.handleItemAction=function(t){var e=this,i=this.adapter.getElementIndex(t);if(!(i<0)){this.adapter.notifySelected({index:i});var s="true"===this.adapter.getAttributeFromElementAtIndex(i,Ec.SKIP_RESTORE_FOCUS);this.adapter.closeSurface(s),this.closeAnimationEndTimerId=setTimeout(function(){var i=e.adapter.getElementIndex(t);i>=0&&e.adapter.isSelectableItemAtIndex(i)&&e.setSelectedIndex(i)},Ic.numbers.TRANSITION_CLOSE_DURATION)}},e.prototype.handleMenuSurfaceOpened=function(){switch(this.defaultFocusState){case wc.FIRST_ITEM:this.adapter.focusItemAtIndex(0);break;case wc.LAST_ITEM:this.adapter.focusItemAtIndex(this.adapter.getMenuItemCount()-1);break;case wc.NONE:break;default:this.adapter.focusListRoot()}},e.prototype.setDefaultFocusState=function(t){this.defaultFocusState=t},e.prototype.getSelectedIndex=function(){return this.selectedIndex},e.prototype.setSelectedIndex=function(t){if(this.validatedIndex(t),!this.adapter.isSelectableItemAtIndex(t))throw new Error("MDCMenuFoundation: No selection group at specified index.");var e=this.adapter.getSelectedSiblingOfItemAtIndex(t);e>=0&&(this.adapter.removeAttributeFromElementAtIndex(e,Ec.ARIA_CHECKED_ATTR),this.adapter.removeClassFromElementAtIndex(e,Ac.MENU_SELECTED_LIST_ITEM)),this.adapter.addClassToElementAtIndex(t,Ac.MENU_SELECTED_LIST_ITEM),this.adapter.addAttributeToElementAtIndex(t,Ec.ARIA_CHECKED_ATTR,"true"),this.selectedIndex=t},e.prototype.setEnabled=function(t,e){this.validatedIndex(t),e?(this.adapter.removeClassFromElementAtIndex(t,Ur),this.adapter.addAttributeToElementAtIndex(t,Ec.ARIA_DISABLED_ATTR,"false")):(this.adapter.addClassToElementAtIndex(t,Ur),this.adapter.addAttributeToElementAtIndex(t,Ec.ARIA_DISABLED_ATTR,"true"))},e.prototype.validatedIndex=function(t){var e=this.adapter.getMenuItemCount();if(!(t>=0&&t{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new Mc(i,t,Pc)},Dc=Rc?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new Mc("string"==typeof t?t:t+"",void 0,Pc))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Hc;const Uc=window,zc=Uc.trustedTypes,Fc=zc?zc.emptyScript:"",Bc=Uc.reactiveElementPolyfillSupport,Vc={toAttribute(t,e){switch(e){case Boolean:t=t?Fc:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},jc=(t,e)=>e!==t&&(e==e||t==t),Wc={attribute:!0,type:String,converter:Vc,reflect:!1,hasChanged:jc},qc="finalized";let Yc=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=Wc){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||Wc}static finalize(){if(this.hasOwnProperty(qc))return!1;this[qc]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(Dc(t))}else void 0!==t&&e.push(Dc(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{Rc?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=kc.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=Wc){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:Vc).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:Vc;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||jc)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var Xc;Yc[qc]=!0,Yc.elementProperties=new Map,Yc.elementStyles=[],Yc.shadowRootOptions={mode:"open"},null==Bc||Bc({ReactiveElement:Yc}),(null!==(Hc=Uc.reactiveElementVersions)&&void 0!==Hc?Hc:Uc.reactiveElementVersions=[]).push("1.6.3");const Gc=window,Kc=Gc.trustedTypes,Jc=Kc?Kc.createPolicy("lit-html",{createHTML:t=>t}):void 0,Zc="$lit$",Qc=`lit$${(Math.random()+"").slice(9)}$`,th="?"+Qc,eh=`<${th}>`,ih=document,sh=()=>ih.createComment(""),nh=t=>null===t||"object"!=typeof t&&"function"!=typeof t,rh=Array.isArray,oh="[ \t\n\f\r]",ah=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,lh=/-->/g,dh=/>/g,ch=RegExp(`>|${oh}(?:([^\\s"'>=/]+)(${oh}*=${oh}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),hh=/'/g,ph=/"/g,uh=/^(?:script|style|textarea|title)$/i,mh=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),fh=Symbol.for("lit-noChange"),gh=Symbol.for("lit-nothing"),vh=new WeakMap,_h=ih.createTreeWalker(ih,129,null,!1);function bh(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==Jc?Jc.createHTML(e):e}let yh=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=ah;for(let e=0;e"===l[0]?(o=null!=n?n:ah,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?ch:'"'===l[3]?ph:hh):o===ph||o===hh?o=ch:o===lh||o===dh?o=ah:(o=ch,n=void 0);const h=o===ch&&t[e+1].startsWith("/>")?" ":"";r+=o===ah?i+eh:d>=0?(s.push(a),i.slice(0,d)+Zc+i.slice(d)+Qc+h):i+Qc+(-2===d?(s.push(void 0),e):h)}return[bh(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),_h.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=_h.nextNode())&&l.length0){n.textContent=Kc?Kc.emptyScript:"";for(let i=0;irh(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==gh&&nh(this._$AH)?this._$AA.nextSibling.data=t:this.$(ih.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=yh.createElement(bh(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new $h(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=vh.get(t.strings);return void 0===e&&vh.set(t.strings,e=new yh(t)),e}T(e){rh(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(sh()),this.k(sh()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=gh}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=xh(this,t,e,0),r=!nh(t)||t!==this._$AH&&t!==fh,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new wh(e.insertBefore(sh(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return fh}};Ph.finalized=!0,Ph._$litElement$=!0,null===(kh=globalThis.litElementHydrateSupport)||void 0===kh||kh.call(globalThis,{LitElement:Ph});const Lh=globalThis.litElementPolyfillSupport;null==Lh||Lh({LitElement:Ph}),(null!==(Rh=globalThis.litElementVersions)&&void 0!==Rh?Rh:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Mh=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function Nh(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):Mh(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function Dh(t){return Nh({...t,state:!0})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function Hh(t,e){return(({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}})({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Uh;null===(Uh=window.HTMLSlotElement)||void 0===Uh||Uh.prototype.assignedElements; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const zh=1,Fh=t=>(...e)=>({_$litDirective$:t,values:e});let Bh=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Vh=Fh(class extends Bh{constructor(t){var e;if(super(t),t.type!==zh||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter(e=>t[e]).join(" ")+" "}update(t,[e]){var i,s;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach(t=>{t in e||(n.remove(t),this.it.delete(t))});for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(s=this.nt)||void 0===s?void 0:s.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return fh}}); -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class jh extends Qi{constructor(){super(...arguments),this.mdcFoundationClass=Oc,this.listElement_=null,this.anchor=null,this.open=!1,this.quick=!1,this.wrapFocus=!1,this.innerRole="menu",this.innerAriaLabel=null,this.corner="TOP_START",this.x=null,this.y=null,this.absolute=!1,this.multi=!1,this.activatable=!1,this.fixed=!1,this.forceGroupSelection=!1,this.fullwidth=!1,this.menuCorner="START",this.stayOpenOnBodyClick=!1,this.defaultFocus="LIST_ROOT",this._listUpdateComplete=null}get listElement(){return this.listElement_||(this.listElement_=this.renderRoot.querySelector("mwc-list")),this.listElement_}get items(){const t=this.listElement;return t?t.items:[]}get index(){const t=this.listElement;return t?t.index:-1}get selected(){const t=this.listElement;return t?t.selected:null}render(){return this.renderSurface()}renderSurface(){const t=this.getSurfaceClasses();return mh` - - ${this.renderList()} - `}getSurfaceClasses(){return{"mdc-menu":!0,"mdc-menu-surface":!0}}renderList(){const t="menu"===this.innerRole?"menuitem":"option",e=this.renderListClasses();return mh` - - - `}renderListClasses(){return{"mdc-deprecated-list":!0}}createAdapter(){return{addClassToElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return;const s=i.items[t];s&&("mdc-menu-item--selected"===e?this.forceGroupSelection&&!s.selected&&i.toggle(t,!0):s.classList.add(e))},removeClassFromElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return;const s=i.items[t];s&&("mdc-menu-item--selected"===e?s.selected&&i.toggle(t,!1):s.classList.remove(e))},addAttributeToElementAtIndex:(t,e,i)=>{const s=this.listElement;if(!s)return;const n=s.items[t];n&&n.setAttribute(e,i)},removeAttributeFromElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return;const s=i.items[t];s&&s.removeAttribute(e)},getAttributeFromElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return null;const s=i.items[t];return s?s.getAttribute(e):null},elementContainsClass:(t,e)=>t.classList.contains(e),closeSurface:()=>{this.open=!1},getElementIndex:t=>{const e=this.listElement;return e?e.items.indexOf(t):-1},notifySelected:()=>{},getMenuItemCount:()=>{const t=this.listElement;return t?t.items.length:0},focusItemAtIndex:t=>{const e=this.listElement;if(!e)return;const i=e.items[t];i&&i.focus()},focusListRoot:()=>{this.listElement&&this.listElement.focus()},getSelectedSiblingOfItemAtIndex:t=>{const e=this.listElement;if(!e)return-1;const i=e.items[t];if(!i||!i.group)return-1;for(let s=0;s{const e=this.listElement;if(!e)return!1;const i=e.items[t];return!!i&&i.hasAttribute("group")}}}onKeydown(t){this.mdcFoundation&&this.mdcFoundation.handleKeydown(t)}onAction(t){const e=this.listElement;if(this.mdcFoundation&&e){const i=t.detail.index,s=e.items[i];s&&this.mdcFoundation.handleItemAction(s)}}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 t=this.listElement;t&&(this._listUpdateComplete=t.updateComplete,await this._listUpdateComplete)}select(t){const e=this.listElement;e&&e.select(t)}close(){this.open=!1}show(){this.open=!0}getFocusedItemIndex(){const t=this.listElement;return t?t.getFocusedItemIndex():-1}focusItemAtIndex(t){const e=this.listElement;e&&e.focusItemAtIndex(t)}layout(t=!0){const e=this.listElement;e&&e.layout(t)}}n([Hh(".mdc-menu")],jh.prototype,"mdcRoot",void 0),n([Hh("slot")],jh.prototype,"slotElement",void 0),n([Nh({type:Object})],jh.prototype,"anchor",void 0),n([Nh({type:Boolean,reflect:!0})],jh.prototype,"open",void 0),n([Nh({type:Boolean})],jh.prototype,"quick",void 0),n([Nh({type:Boolean})],jh.prototype,"wrapFocus",void 0),n([Nh({type:String})],jh.prototype,"innerRole",void 0),n([Nh({type:String})],jh.prototype,"innerAriaLabel",void 0),n([Nh({type:String})],jh.prototype,"corner",void 0),n([Nh({type:Number})],jh.prototype,"x",void 0),n([Nh({type:Number})],jh.prototype,"y",void 0),n([Nh({type:Boolean})],jh.prototype,"absolute",void 0),n([Nh({type:Boolean})],jh.prototype,"multi",void 0),n([Nh({type:Boolean})],jh.prototype,"activatable",void 0),n([Nh({type:Boolean})],jh.prototype,"fixed",void 0),n([Nh({type:Boolean})],jh.prototype,"forceGroupSelection",void 0),n([Nh({type:Boolean})],jh.prototype,"fullwidth",void 0),n([Nh({type:String})],jh.prototype,"menuCorner",void 0),n([Nh({type:Boolean})],jh.prototype,"stayOpenOnBodyClick",void 0),n([Nh({type:String}),os(function(t){this.mdcFoundation&&this.mdcFoundation.setDefaultFocusState(wc[t])})],jh.prototype,"defaultFocus",void 0); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Wh="important",qh=" !"+Wh,Yh=Fh(class extends Bh{constructor(t){var e;if(super(t),t.type!==zh||"style"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce((e,i)=>{const s=t[i];return null==s?e:e+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`},"")}update(t,[e]){const{style:i}=t.element;if(void 0===this.ht){this.ht=new Set;for(const t in e)this.ht.add(t);return this.render(e)}this.ht.forEach(t=>{null==e[t]&&(this.ht.delete(t),t.includes("-")?i.removeProperty(t):i[t]="")});for(const t in e){const s=e[t];if(null!=s){this.ht.add(t);const e="string"==typeof s&&s.endsWith(qh);t.includes("-")||e?i.setProperty(t,e?s.slice(0,-11):s,e?Wh:""):i[t]=s}}return fh}}),Xh={TOP_LEFT:Xo.TOP_LEFT,TOP_RIGHT:Xo.TOP_RIGHT,BOTTOM_LEFT:Xo.BOTTOM_LEFT,BOTTOM_RIGHT:Xo.BOTTOM_RIGHT,TOP_START:Xo.TOP_START,TOP_END:Xo.TOP_END,BOTTOM_START:Xo.BOTTOM_START,BOTTOM_END:Xo.BOTTOM_END}; -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class Gh extends Qi{constructor(){super(...arguments),this.mdcFoundationClass=Ic,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=Xo.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(){return this.renderSurface()}renderSurface(){const t=this.getRootClasses(),e=this.getRootStyles();return mh` -
- ${this.renderContent()} -
`}getRootClasses(){return{"mdc-menu-surface":!0,"mdc-menu-surface--fixed":this.fixed,"mdc-menu-surface--fullwidth":this.fullwidth}}getRootStyles(){return{top:this.styleTop,left:this.styleLeft,right:this.styleRight,bottom:this.styleBottom,"max-height":this.styleMaxHeight,"transform-origin":this.styleTransformOrigin}}renderContent(){return mh``}createAdapter(){return Object.assign(Object.assign({},Xi(this.mdcRoot)),{hasAnchor:()=>!!this.anchor,notifyClose:()=>{const t=new CustomEvent("closed",{bubbles:!0,composed:!0});this.open=!1,this.mdcRoot.dispatchEvent(t)},notifyClosing:()=>{const t=new CustomEvent("closing",{bubbles:!0,composed:!0});this.mdcRoot.dispatchEvent(t)},notifyOpen:()=>{const t=new CustomEvent("opened",{bubbles:!0,composed:!0});this.open=!0,this.mdcRoot.dispatchEvent(t)},notifyOpening:()=>{const t=new CustomEvent("opening",{bubbles:!0,composed:!0});this.mdcRoot.dispatchEvent(t)},isElementInContainer:()=>!1,isRtl:()=>!!this.mdcRoot&&"rtl"===getComputedStyle(this.mdcRoot).direction,setTransformOrigin:t=>{this.mdcRoot&&(this.styleTransformOrigin=t)},isFocused:()=>Zi(this),saveFocus:()=>{const t=Ji(),e=t.length;e||(this.previouslyFocused=null),this.previouslyFocused=t[e-1]},restoreFocus:()=>{this.previouslyFocused&&"focus"in this.previouslyFocused&&this.previouslyFocused.focus()},getInnerDimensions:()=>{const t=this.mdcRoot;return t?{width:t.offsetWidth,height:t.offsetHeight}:{width:0,height:0}},getAnchorDimensions:()=>{const t=this.anchor;return t?t.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:t=>{this.mdcRoot&&(this.styleLeft="left"in t?`${t.left}px`:"",this.styleRight="right"in t?`${t.right}px`:"",this.styleTop="top"in t?`${t.top}px`:"",this.styleBottom="bottom"in t?`${t.bottom}px`:"")},setMaxHeight:async t=>{this.mdcRoot&&(this.styleMaxHeight=t,await this.updateComplete,this.styleMaxHeight=`var(--mdc-menu-max-height, ${t})`)}})}onKeydown(t){this.mdcFoundation&&this.mdcFoundation.handleKeydown(t)}onBodyClick(t){if(this.stayOpenOnBodyClick)return;-1===t.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})}onOpenChanged(t,e){this.mdcFoundation&&(t?this.mdcFoundation.open():void 0!==e&&this.mdcFoundation.close())}close(){this.open=!1}show(){this.open=!0}}n([Hh(".mdc-menu-surface")],Gh.prototype,"mdcRoot",void 0),n([Hh("slot")],Gh.prototype,"slotElement",void 0),n([Nh({type:Boolean}),os(function(t){this.mdcFoundation&&!this.fixed&&this.mdcFoundation.setIsHoisted(t)})],Gh.prototype,"absolute",void 0),n([Nh({type:Boolean})],Gh.prototype,"fullwidth",void 0),n([Nh({type:Boolean}),os(function(t){this.mdcFoundation&&!this.absolute&&this.mdcFoundation.setFixedPosition(t)})],Gh.prototype,"fixed",void 0),n([Nh({type:Number}),os(function(t){this.mdcFoundation&&null!==this.y&&null!==t&&(this.mdcFoundation.setAbsolutePosition(t,this.y),this.mdcFoundation.setAnchorMargin({left:t,top:this.y,right:-t,bottom:this.y}))})],Gh.prototype,"x",void 0),n([Nh({type:Number}),os(function(t){this.mdcFoundation&&null!==this.x&&null!==t&&(this.mdcFoundation.setAbsolutePosition(this.x,t),this.mdcFoundation.setAnchorMargin({left:this.x,top:t,right:-this.x,bottom:t}))})],Gh.prototype,"y",void 0),n([Nh({type:Boolean}),os(function(t){this.mdcFoundation&&this.mdcFoundation.setQuickOpen(t)})],Gh.prototype,"quick",void 0),n([Nh({type:Boolean,reflect:!0}),os(function(t,e){this.onOpenChanged(t,e)})],Gh.prototype,"open",void 0),n([Nh({type:Boolean})],Gh.prototype,"stayOpenOnBodyClick",void 0),n([Dh(),os(function(t){this.mdcFoundation&&this.mdcFoundation.setAnchorCorner(t)})],Gh.prototype,"bitwiseCorner",void 0),n([Nh({type:String}),os(function(t){if(this.mdcFoundation){const e="START"===t||"END"===t,i=null===this.previousMenuCorner,s=!i&&t!==this.previousMenuCorner;e&&(s||i&&"END"===t)&&(this.bitwiseCorner=this.bitwiseCorner^Yo.RIGHT,this.mdcFoundation.flipCornerHorizontally(),this.previousMenuCorner=t)}})],Gh.prototype,"menuCorner",void 0),n([Nh({type:String}),os(function(t){if(this.mdcFoundation&&t){let e=Xh[t];"END"===this.menuCorner&&(e^=Yo.RIGHT),this.bitwiseCorner=e}})],Gh.prototype,"corner",void 0),n([Dh()],Gh.prototype,"styleTop",void 0),n([Dh()],Gh.prototype,"styleLeft",void 0),n([Dh()],Gh.prototype,"styleRight",void 0),n([Dh()],Gh.prototype,"styleBottom",void 0),n([Dh()],Gh.prototype,"styleMaxHeight",void 0),n([Dh()],Gh.prototype,"styleTransformOrigin",void 0); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Kh=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),Jh={BG_FOCUSED:"mdc-ripple-upgraded--background-focused",FG_ACTIVATION:"mdc-ripple-upgraded--foreground-activation",FG_DEACTIVATION:"mdc-ripple-upgraded--foreground-deactivation",ROOT:"mdc-ripple-upgraded",UNBOUNDED:"mdc-ripple-upgraded--unbounded"},Zh={VAR_FG_SCALE:"--mdc-ripple-fg-scale",VAR_FG_SIZE:"--mdc-ripple-fg-size",VAR_FG_TRANSLATE_END:"--mdc-ripple-fg-translate-end",VAR_FG_TRANSLATE_START:"--mdc-ripple-fg-translate-start",VAR_LEFT:"--mdc-ripple-left",VAR_TOP:"--mdc-ripple-top"},Qh={DEACTIVATION_TIMEOUT_MS:225,FG_DEACTIVATION_MS:150,INITIAL_ORIGIN_SCALE:.6,PADDING:10,TAP_DELAY_MS:300}; -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var tp=["touchstart","pointerdown","mousedown","keydown"],ep=["touchend","pointerup","mouseup","contextmenu"],ip=[],sp=function(t){function e(i){var n=t.call(this,s(s({},e.defaultAdapter),i))||this;return n.activationAnimationHasEnded=!1,n.activationTimer=0,n.fgDeactivationRemovalTimer=0,n.fgScale="0",n.frame={width:0,height:0},n.initialSize=0,n.layoutFrame=0,n.maxRadius=0,n.unboundedCoords={left:0,top:0},n.activationState=n.defaultActivationState(),n.activationTimerCallback=function(){n.activationAnimationHasEnded=!0,n.runDeactivationUXLogicIfReady()},n.activateHandler=function(t){n.activateImpl(t)},n.deactivateHandler=function(){n.deactivateImpl()},n.focusHandler=function(){n.handleFocus()},n.blurHandler=function(){n.handleBlur()},n.resizeHandler=function(){n.layout()},n}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Jh},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Zh},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return Qh},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},browserSupportsCssVars:function(){return!0},computeBoundingRect:function(){return{top:0,right:0,bottom:0,left:0,width:0,height:0}},containsEventTarget:function(){return!0},deregisterDocumentInteractionHandler:function(){},deregisterInteractionHandler:function(){},deregisterResizeHandler:function(){},getWindowPageOffset:function(){return{x:0,y:0}},isSurfaceActive:function(){return!0},isSurfaceDisabled:function(){return!0},isUnbounded:function(){return!0},registerDocumentInteractionHandler:function(){},registerInteractionHandler:function(){},registerResizeHandler:function(){},removeClass:function(){},updateCssVariable:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t=this,i=this.supportsPressRipple();if(this.registerRootHandlers(i),i){var s=e.cssClasses,n=s.ROOT,r=s.UNBOUNDED;requestAnimationFrame(function(){t.adapter.addClass(n),t.adapter.isUnbounded()&&(t.adapter.addClass(r),t.layoutInternal())})}},e.prototype.destroy=function(){var t=this;if(this.supportsPressRipple()){this.activationTimer&&(clearTimeout(this.activationTimer),this.activationTimer=0,this.adapter.removeClass(e.cssClasses.FG_ACTIVATION)),this.fgDeactivationRemovalTimer&&(clearTimeout(this.fgDeactivationRemovalTimer),this.fgDeactivationRemovalTimer=0,this.adapter.removeClass(e.cssClasses.FG_DEACTIVATION));var i=e.cssClasses,s=i.ROOT,n=i.UNBOUNDED;requestAnimationFrame(function(){t.adapter.removeClass(s),t.adapter.removeClass(n),t.removeCssVars()})}this.deregisterRootHandlers(),this.deregisterDeactivationHandlers()},e.prototype.activate=function(t){this.activateImpl(t)},e.prototype.deactivate=function(){this.deactivateImpl()},e.prototype.layout=function(){var t=this;this.layoutFrame&&cancelAnimationFrame(this.layoutFrame),this.layoutFrame=requestAnimationFrame(function(){t.layoutInternal(),t.layoutFrame=0})},e.prototype.setUnbounded=function(t){var i=e.cssClasses.UNBOUNDED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.handleFocus=function(){var t=this;requestAnimationFrame(function(){return t.adapter.addClass(e.cssClasses.BG_FOCUSED)})},e.prototype.handleBlur=function(){var t=this;requestAnimationFrame(function(){return t.adapter.removeClass(e.cssClasses.BG_FOCUSED)})},e.prototype.supportsPressRipple=function(){return this.adapter.browserSupportsCssVars()},e.prototype.defaultActivationState=function(){return{activationEvent:void 0,hasDeactivationUXRun:!1,isActivated:!1,isProgrammatic:!1,wasActivatedByPointer:!1,wasElementMadeActive:!1}},e.prototype.registerRootHandlers=function(t){var e,i;if(t){try{for(var s=r(tp),n=s.next();!n.done;n=s.next()){var o=n.value;this.adapter.registerInteractionHandler(o,this.activateHandler)}}catch(t){e={error:t}}finally{try{n&&!n.done&&(i=s.return)&&i.call(s)}finally{if(e)throw e.error}}this.adapter.isUnbounded()&&this.adapter.registerResizeHandler(this.resizeHandler)}this.adapter.registerInteractionHandler("focus",this.focusHandler),this.adapter.registerInteractionHandler("blur",this.blurHandler)},e.prototype.registerDeactivationHandlers=function(t){var e,i;if("keydown"===t.type)this.adapter.registerInteractionHandler("keyup",this.deactivateHandler);else try{for(var s=r(ep),n=s.next();!n.done;n=s.next()){var o=n.value;this.adapter.registerDocumentInteractionHandler(o,this.deactivateHandler)}}catch(t){e={error:t}}finally{try{n&&!n.done&&(i=s.return)&&i.call(s)}finally{if(e)throw e.error}}},e.prototype.deregisterRootHandlers=function(){var t,e;try{for(var i=r(tp),s=i.next();!s.done;s=i.next()){var n=s.value;this.adapter.deregisterInteractionHandler(n,this.activateHandler)}}catch(e){t={error:e}}finally{try{s&&!s.done&&(e=i.return)&&e.call(i)}finally{if(t)throw t.error}}this.adapter.deregisterInteractionHandler("focus",this.focusHandler),this.adapter.deregisterInteractionHandler("blur",this.blurHandler),this.adapter.isUnbounded()&&this.adapter.deregisterResizeHandler(this.resizeHandler)},e.prototype.deregisterDeactivationHandlers=function(){var t,e;this.adapter.deregisterInteractionHandler("keyup",this.deactivateHandler);try{for(var i=r(ep),s=i.next();!s.done;s=i.next()){var n=s.value;this.adapter.deregisterDocumentInteractionHandler(n,this.deactivateHandler)}}catch(e){t={error:e}}finally{try{s&&!s.done&&(e=i.return)&&e.call(i)}finally{if(t)throw t.error}}},e.prototype.removeCssVars=function(){var t=this,i=e.strings;Object.keys(i).forEach(function(e){0===e.indexOf("VAR_")&&t.adapter.updateCssVariable(i[e],null)})},e.prototype.activateImpl=function(t){var e=this;if(!this.adapter.isSurfaceDisabled()){var i=this.activationState;if(!i.isActivated){var s=this.previousActivationEvent;if(!(s&&void 0!==t&&s.type!==t.type))i.isActivated=!0,i.isProgrammatic=void 0===t,i.activationEvent=t,i.wasActivatedByPointer=!i.isProgrammatic&&(void 0!==t&&("mousedown"===t.type||"touchstart"===t.type||"pointerdown"===t.type)),void 0!==t&&ip.length>0&&ip.some(function(t){return e.adapter.containsEventTarget(t)})?this.resetActivationState():(void 0!==t&&(ip.push(t.target),this.registerDeactivationHandlers(t)),i.wasElementMadeActive=this.checkElementMadeActive(t),i.wasElementMadeActive&&this.animateActivation(),requestAnimationFrame(function(){ip=[],i.wasElementMadeActive||void 0===t||" "!==t.key&&32!==t.keyCode||(i.wasElementMadeActive=e.checkElementMadeActive(t),i.wasElementMadeActive&&e.animateActivation()),i.wasElementMadeActive||(e.activationState=e.defaultActivationState())}))}}},e.prototype.checkElementMadeActive=function(t){return void 0===t||"keydown"!==t.type||this.adapter.isSurfaceActive()},e.prototype.animateActivation=function(){var t=this,i=e.strings,s=i.VAR_FG_TRANSLATE_START,n=i.VAR_FG_TRANSLATE_END,r=e.cssClasses,o=r.FG_DEACTIVATION,a=r.FG_ACTIVATION,l=e.numbers.DEACTIVATION_TIMEOUT_MS;this.layoutInternal();var d="",c="";if(!this.adapter.isUnbounded()){var h=this.getFgTranslationCoordinates(),p=h.startPoint,u=h.endPoint;d=p.x+"px, "+p.y+"px",c=u.x+"px, "+u.y+"px"}this.adapter.updateCssVariable(s,d),this.adapter.updateCssVariable(n,c),clearTimeout(this.activationTimer),clearTimeout(this.fgDeactivationRemovalTimer),this.rmBoundedActivationClasses(),this.adapter.removeClass(o),this.adapter.computeBoundingRect(),this.adapter.addClass(a),this.activationTimer=setTimeout(function(){t.activationTimerCallback()},l)},e.prototype.getFgTranslationCoordinates=function(){var t,e=this.activationState,i=e.activationEvent;return t=e.wasActivatedByPointer?function(t,e,i){if(!t)return{x:0,y:0};var s,n,r=e.x,o=e.y,a=r+i.left,l=o+i.top;if("touchstart"===t.type){var d=t;s=d.changedTouches[0].pageX-a,n=d.changedTouches[0].pageY-l}else{var c=t;s=c.pageX-a,n=c.pageY-l}return{x:s,y:n}}(i,this.adapter.getWindowPageOffset(),this.adapter.computeBoundingRect()):{x:this.frame.width/2,y:this.frame.height/2},{startPoint:t={x:t.x-this.initialSize/2,y:t.y-this.initialSize/2},endPoint:{x:this.frame.width/2-this.initialSize/2,y:this.frame.height/2-this.initialSize/2}}},e.prototype.runDeactivationUXLogicIfReady=function(){var t=this,i=e.cssClasses.FG_DEACTIVATION,s=this.activationState,n=s.hasDeactivationUXRun,r=s.isActivated;(n||!r)&&this.activationAnimationHasEnded&&(this.rmBoundedActivationClasses(),this.adapter.addClass(i),this.fgDeactivationRemovalTimer=setTimeout(function(){t.adapter.removeClass(i)},Qh.FG_DEACTIVATION_MS))},e.prototype.rmBoundedActivationClasses=function(){var t=e.cssClasses.FG_ACTIVATION;this.adapter.removeClass(t),this.activationAnimationHasEnded=!1,this.adapter.computeBoundingRect()},e.prototype.resetActivationState=function(){var t=this;this.previousActivationEvent=this.activationState.activationEvent,this.activationState=this.defaultActivationState(),setTimeout(function(){return t.previousActivationEvent=void 0},e.numbers.TAP_DELAY_MS)},e.prototype.deactivateImpl=function(){var t=this,e=this.activationState;if(e.isActivated){var i=s({},e);e.isProgrammatic?(requestAnimationFrame(function(){t.animateDeactivation(i)}),this.resetActivationState()):(this.deregisterDeactivationHandlers(),requestAnimationFrame(function(){t.activationState.hasDeactivationUXRun=!0,t.animateDeactivation(i),t.resetActivationState()}))}},e.prototype.animateDeactivation=function(t){var e=t.wasActivatedByPointer,i=t.wasElementMadeActive;(e||i)&&this.runDeactivationUXLogicIfReady()},e.prototype.layoutInternal=function(){var t=this;this.frame=this.adapter.computeBoundingRect();var i=Math.max(this.frame.height,this.frame.width);this.maxRadius=this.adapter.isUnbounded()?i:Math.sqrt(Math.pow(t.frame.width,2)+Math.pow(t.frame.height,2))+e.numbers.PADDING;var s=Math.floor(i*e.numbers.INITIAL_ORIGIN_SCALE);this.adapter.isUnbounded()&&s%2!=0?this.initialSize=s-1:this.initialSize=s,this.fgScale=""+this.maxRadius/this.initialSize,this.updateLayoutCssVars()},e.prototype.updateLayoutCssVars=function(){var t=e.strings,i=t.VAR_FG_SIZE,s=t.VAR_LEFT,n=t.VAR_TOP,r=t.VAR_FG_SCALE;this.adapter.updateCssVariable(i,this.initialSize+"px"),this.adapter.updateCssVariable(r,this.fgScale),this.adapter.isUnbounded()&&(this.unboundedCoords={left:Math.round(this.frame.width/2-this.initialSize/2),top:Math.round(this.frame.height/2-this.initialSize/2)},this.adapter.updateCssVariable(s,this.unboundedCoords.left+"px"),this.adapter.updateCssVariable(n,this.unboundedCoords.top+"px"))},e}(Kh); -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const np=window,rp=np.ShadowRoot&&(void 0===np.ShadyCSS||np.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,op=Symbol(),ap=new WeakMap;let lp=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==op)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(rp&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=ap.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&ap.set(e,t))}return t}toString(){return this.cssText}};const dp=rp?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new lp("string"==typeof t?t:t+"",void 0,op))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var cp;const hp=window,pp=hp.trustedTypes,up=pp?pp.emptyScript:"",mp=hp.reactiveElementPolyfillSupport,fp={toAttribute(t,e){switch(e){case Boolean:t=t?up:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},gp=(t,e)=>e!==t&&(e==e||t==t),vp={attribute:!0,type:String,converter:fp,reflect:!1,hasChanged:gp},_p="finalized";let bp=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=vp){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||vp}static finalize(){if(this.hasOwnProperty(_p))return!1;this[_p]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(dp(t))}else void 0!==t&&e.push(dp(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{rp?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=np.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=vp){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:fp).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:fp;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||gp)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var yp;bp[_p]=!0,bp.elementProperties=new Map,bp.elementStyles=[],bp.shadowRootOptions={mode:"open"},null==mp||mp({ReactiveElement:bp}),(null!==(cp=hp.reactiveElementVersions)&&void 0!==cp?cp:hp.reactiveElementVersions=[]).push("1.6.3");const xp=window,$p=xp.trustedTypes,wp=$p?$p.createPolicy("lit-html",{createHTML:t=>t}):void 0,Ap="$lit$",Ep=`lit$${(Math.random()+"").slice(9)}$`,Sp="?"+Ep,Cp=`<${Sp}>`,Tp=document,Ip=()=>Tp.createComment(""),Op=t=>null===t||"object"!=typeof t&&"function"!=typeof t,kp=Array.isArray,Rp="[ \t\n\f\r]",Pp=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Lp=/-->/g,Mp=/>/g,Np=RegExp(`>|${Rp}(?:([^\\s"'>=/]+)(${Rp}*=${Rp}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Dp=/'/g,Hp=/"/g,Up=/^(?:script|style|textarea|title)$/i,zp=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),Fp=Symbol.for("lit-noChange"),Bp=Symbol.for("lit-nothing"),Vp=new WeakMap,jp=Tp.createTreeWalker(Tp,129,null,!1);function Wp(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==wp?wp.createHTML(e):e}let qp=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=Pp;for(let e=0;e"===l[0]?(o=null!=n?n:Pp,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?Np:'"'===l[3]?Hp:Dp):o===Hp||o===Dp?o=Np:o===Lp||o===Mp?o=Pp:(o=Np,n=void 0);const h=o===Np&&t[e+1].startsWith("/>")?" ":"";r+=o===Pp?i+Cp:d>=0?(s.push(a),i.slice(0,d)+Ap+i.slice(d)+Ep+h):i+Ep+(-2===d?(s.push(void 0),e):h)}return[Wp(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),jp.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=jp.nextNode())&&l.length0){n.textContent=$p?$p.emptyScript:"";for(let i=0;ikp(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==Bp&&Op(this._$AH)?this._$AA.nextSibling.data=t:this.$(Tp.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=qp.createElement(Wp(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new Xp(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=Vp.get(t.strings);return void 0===e&&Vp.set(t.strings,e=new qp(t)),e}T(e){kp(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(Ip()),this.k(Ip()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=Bp}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=Yp(this,t,e,0),r=!Op(t)||t!==this._$AH&&t!==Fp,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new Gp(e.insertBefore(Ip(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return Fp}};ru.finalized=!0,ru._$litElement$=!0,null===(su=globalThis.litElementHydrateSupport)||void 0===su||su.call(globalThis,{LitElement:ru});const ou=globalThis.litElementPolyfillSupport;null==ou||ou({LitElement:ru}),(null!==(nu=globalThis.litElementVersions)&&void 0!==nu?nu:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const au=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function lu(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):au(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function du(t){return lu({...t,state:!0})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var cu;null===(cu=window.HTMLSlotElement)||void 0===cu||cu.prototype.assignedElements; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const hu=1,pu=t=>(...e)=>({_$litDirective$:t,values:e});let uu=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const mu=pu(class extends uu{constructor(t){var e;if(super(t),t.type!==hu||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter(e=>t[e]).join(" ")+" "}update(t,[e]){var i,s;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach(t=>{t in e||(n.remove(t),this.it.delete(t))});for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(s=this.nt)||void 0===s?void 0:s.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return Fp}}),fu="important",gu=" !"+fu,vu=pu(class extends uu{constructor(t){var e;if(super(t),t.type!==hu||"style"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce((e,i)=>{const s=t[i];return null==s?e:e+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${s};`},"")}update(t,[e]){const{style:i}=t.element;if(void 0===this.ht){this.ht=new Set;for(const t in e)this.ht.add(t);return this.render(e)}this.ht.forEach(t=>{null==e[t]&&(this.ht.delete(t),t.includes("-")?i.removeProperty(t):i[t]="")});for(const t in e){const s=e[t];if(null!=s){this.ht.add(t);const e="string"==typeof s&&s.endsWith(gu);t.includes("-")||e?i.setProperty(t,e?s.slice(0,-11):s,e?fu:""):i[t]=s}}return Fp}}); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class _u extends Qi{constructor(){super(...arguments),this.primary=!1,this.accent=!1,this.unbounded=!1,this.disabled=!1,this.activated=!1,this.selected=!1,this.internalUseStateLayerCustomProperties=!1,this.hovering=!1,this.bgFocused=!1,this.fgActivation=!1,this.fgDeactivation=!1,this.fgScale="",this.fgSize="",this.translateStart="",this.translateEnd="",this.leftPos="",this.topPos="",this.mdcFoundationClass=sp}get isActive(){return t=this.parentElement||this,e=":active",(t.matches||t.webkitMatchesSelector||t.msMatchesSelector).call(t,e); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var t,e}createAdapter(){return{browserSupportsCssVars:()=>!0,isUnbounded:()=>this.unbounded,isSurfaceActive:()=>this.isActive,isSurfaceDisabled:()=>this.disabled,addClass:t=>{switch(t){case"mdc-ripple-upgraded--background-focused":this.bgFocused=!0;break;case"mdc-ripple-upgraded--foreground-activation":this.fgActivation=!0;break;case"mdc-ripple-upgraded--foreground-deactivation":this.fgDeactivation=!0}},removeClass:t=>{switch(t){case"mdc-ripple-upgraded--background-focused":this.bgFocused=!1;break;case"mdc-ripple-upgraded--foreground-activation":this.fgActivation=!1;break;case"mdc-ripple-upgraded--foreground-deactivation":this.fgDeactivation=!1}},containsEventTarget:()=>!0,registerInteractionHandler:()=>{},deregisterInteractionHandler:()=>{},registerDocumentInteractionHandler:()=>{},deregisterDocumentInteractionHandler:()=>{},registerResizeHandler:()=>{},deregisterResizeHandler:()=>{},updateCssVariable:(t,e)=>{switch(t){case"--mdc-ripple-fg-scale":this.fgScale=e;break;case"--mdc-ripple-fg-size":this.fgSize=e;break;case"--mdc-ripple-fg-translate-end":this.translateEnd=e;break;case"--mdc-ripple-fg-translate-start":this.translateStart=e;break;case"--mdc-ripple-left":this.leftPos=e;break;case"--mdc-ripple-top":this.topPos=e}},computeBoundingRect:()=>(this.parentElement||this).getBoundingClientRect(),getWindowPageOffset:()=>({x:window.pageXOffset,y:window.pageYOffset})}}startPress(t){this.waitForFoundation(()=>{this.mdcFoundation.activate(t)})}endPress(){this.waitForFoundation(()=>{this.mdcFoundation.deactivate()})}startFocus(){this.waitForFoundation(()=>{this.mdcFoundation.handleFocus()})}endFocus(){this.waitForFoundation(()=>{this.mdcFoundation.handleBlur()})}startHover(){this.hovering=!0}endHover(){this.hovering=!1}waitForFoundation(t){this.mdcFoundation?t():this.updateComplete.then(t)}update(t){t.has("disabled")&&this.disabled&&this.endHover(),super.update(t)}render(){const t=this.activated&&(this.primary||!this.accent),e=this.selected&&(this.primary||!this.accent),i={"mdc-ripple-surface--accent":this.accent,"mdc-ripple-surface--primary--activated":t,"mdc-ripple-surface--accent--activated":this.accent&&this.activated,"mdc-ripple-surface--primary--selected":e,"mdc-ripple-surface--accent--selected":this.accent&&this.selected,"mdc-ripple-surface--disabled":this.disabled,"mdc-ripple-surface--hover":this.hovering,"mdc-ripple-surface--primary":this.primary,"mdc-ripple-surface--selected":this.selected,"mdc-ripple-upgraded--background-focused":this.bgFocused,"mdc-ripple-upgraded--foreground-activation":this.fgActivation,"mdc-ripple-upgraded--foreground-deactivation":this.fgDeactivation,"mdc-ripple-upgraded--unbounded":this.unbounded,"mdc-ripple-surface--internal-use-state-layer-custom-properties":this.internalUseStateLayerCustomProperties};return zp` -
`}}n([ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function(t){return(({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}})({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})}(".mdc-ripple-surface")],_u.prototype,"mdcRoot",void 0),n([lu({type:Boolean})],_u.prototype,"primary",void 0),n([lu({type:Boolean})],_u.prototype,"accent",void 0),n([lu({type:Boolean})],_u.prototype,"unbounded",void 0),n([lu({type:Boolean})],_u.prototype,"disabled",void 0),n([lu({type:Boolean})],_u.prototype,"activated",void 0),n([lu({type:Boolean})],_u.prototype,"selected",void 0),n([lu({type:Boolean})],_u.prototype,"internalUseStateLayerCustomProperties",void 0),n([du()],_u.prototype,"hovering",void 0),n([du()],_u.prototype,"bgFocused",void 0),n([du()],_u.prototype,"fgActivation",void 0),n([du()],_u.prototype,"fgDeactivation",void 0),n([du()],_u.prototype,"fgScale",void 0),n([du()],_u.prototype,"fgSize",void 0),n([du()],_u.prototype,"translateStart",void 0),n([du()],_u.prototype,"translateEnd",void 0),n([du()],_u.prototype,"leftPos",void 0),n([du()],_u.prototype,"topPos",void 0); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var bu=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),yu={NOTCH_ELEMENT_SELECTOR:".mdc-notched-outline__notch"},xu={NOTCH_ELEMENT_PADDING:8},$u={NO_LABEL:"mdc-notched-outline--no-label",OUTLINE_NOTCHED:"mdc-notched-outline--notched",OUTLINE_UPGRADED:"mdc-notched-outline--upgraded"},wu=function(t){function e(i){return t.call(this,s(s({},e.defaultAdapter),i))||this}return i(e,t),Object.defineProperty(e,"strings",{get:function(){return yu},enumerable:!1,configurable:!0}),Object.defineProperty(e,"cssClasses",{get:function(){return $u},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return xu},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},setNotchWidthProperty:function(){},removeNotchWidthProperty:function(){}}},enumerable:!1,configurable:!0}),e.prototype.notch=function(t){var i=e.cssClasses.OUTLINE_NOTCHED;t>0&&(t+=xu.NOTCH_ELEMENT_PADDING),this.adapter.setNotchWidthProperty(t),this.adapter.addClass(i)},e.prototype.closeNotch=function(){var t=e.cssClasses.OUTLINE_NOTCHED;this.adapter.removeClass(t),this.adapter.removeNotchWidthProperty()},e}(bu); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Au=window,Eu=Au.ShadowRoot&&(void 0===Au.ShadyCSS||Au.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Su=Symbol(),Cu=new WeakMap;let Tu=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==Su)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(Eu&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=Cu.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&Cu.set(e,t))}return t}toString(){return this.cssText}};const Iu=Eu?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new Tu("string"==typeof t?t:t+"",void 0,Su))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Ou;const ku=window,Ru=ku.trustedTypes,Pu=Ru?Ru.emptyScript:"",Lu=ku.reactiveElementPolyfillSupport,Mu={toAttribute(t,e){switch(e){case Boolean:t=t?Pu:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},Nu=(t,e)=>e!==t&&(e==e||t==t),Du={attribute:!0,type:String,converter:Mu,reflect:!1,hasChanged:Nu},Hu="finalized";let Uu=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=Du){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||Du}static finalize(){if(this.hasOwnProperty(Hu))return!1;this[Hu]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(Iu(t))}else void 0!==t&&e.push(Iu(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{Eu?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=Au.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=Du){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:Mu).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:Mu;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||Nu)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var zu;Uu[Hu]=!0,Uu.elementProperties=new Map,Uu.elementStyles=[],Uu.shadowRootOptions={mode:"open"},null==Lu||Lu({ReactiveElement:Uu}),(null!==(Ou=ku.reactiveElementVersions)&&void 0!==Ou?Ou:ku.reactiveElementVersions=[]).push("1.6.3");const Fu=window,Bu=Fu.trustedTypes,Vu=Bu?Bu.createPolicy("lit-html",{createHTML:t=>t}):void 0,ju="$lit$",Wu=`lit$${(Math.random()+"").slice(9)}$`,qu="?"+Wu,Yu=`<${qu}>`,Xu=document,Gu=()=>Xu.createComment(""),Ku=t=>null===t||"object"!=typeof t&&"function"!=typeof t,Ju=Array.isArray,Zu="[ \t\n\f\r]",Qu=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,tm=/-->/g,em=/>/g,im=RegExp(`>|${Zu}(?:([^\\s"'>=/]+)(${Zu}*=${Zu}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),sm=/'/g,nm=/"/g,rm=/^(?:script|style|textarea|title)$/i,om=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),am=Symbol.for("lit-noChange"),lm=Symbol.for("lit-nothing"),dm=new WeakMap,cm=Xu.createTreeWalker(Xu,129,null,!1);function hm(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==Vu?Vu.createHTML(e):e}let pm=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=Qu;for(let e=0;e"===l[0]?(o=null!=n?n:Qu,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?im:'"'===l[3]?nm:sm):o===nm||o===sm?o=im:o===tm||o===em?o=Qu:(o=im,n=void 0);const h=o===im&&t[e+1].startsWith("/>")?" ":"";r+=o===Qu?i+Yu:d>=0?(s.push(a),i.slice(0,d)+ju+i.slice(d)+Wu+h):i+Wu+(-2===d?(s.push(void 0),e):h)}return[hm(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),cm.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=cm.nextNode())&&l.length0){n.textContent=Bu?Bu.emptyScript:"";for(let i=0;iJu(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==lm&&Ku(this._$AH)?this._$AA.nextSibling.data=t:this.$(Xu.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=pm.createElement(hm(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new mm(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=dm.get(t.strings);return void 0===e&&dm.set(t.strings,e=new pm(t)),e}T(e){Ju(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(Gu()),this.k(Gu()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=lm}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=um(this,t,e,0),r=!Ku(t)||t!==this._$AH&&t!==am,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new fm(e.insertBefore(Gu(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return am}};Em.finalized=!0,Em._$litElement$=!0,null===(wm=globalThis.litElementHydrateSupport)||void 0===wm||wm.call(globalThis,{LitElement:Em});const Sm=globalThis.litElementPolyfillSupport;null==Sm||Sm({LitElement:Em}),(null!==(Am=globalThis.litElementVersions)&&void 0!==Am?Am:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Cm=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function Tm(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):Cm(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function Im(t,e){return(({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}})({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Om;null===(Om=window.HTMLSlotElement)||void 0===Om||Om.prototype.assignedElements; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const km=1;let Rm=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Pm=(t=>(...e)=>({_$litDirective$:t,values:e}))(class extends Rm{constructor(t){var e;if(super(t),t.type!==km||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter(e=>t[e]).join(" ")+" "}update(t,[e]){var i,s;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach(t=>{t in e||(n.remove(t),this.it.delete(t))});for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(s=this.nt)||void 0===s?void 0:s.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return am}}); -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class Lm extends Qi{constructor(){super(...arguments),this.mdcFoundationClass=wu,this.width=0,this.open=!1,this.lastOpen=this.open}createAdapter(){return{addClass:t=>this.mdcRoot.classList.add(t),removeClass:t=>this.mdcRoot.classList.remove(t),setNotchWidthProperty:t=>this.notchElement.style.setProperty("width",`${t}px`),removeNotchWidthProperty:()=>this.notchElement.style.removeProperty("width")}}openOrClose(t,e){this.mdcFoundation&&(t&&void 0!==e?this.mdcFoundation.notch(e):this.mdcFoundation.closeNotch())}render(){this.openOrClose(this.open,this.width);const t=Pm({"mdc-notched-outline--notched":this.open});return om` - - - - - - - `}}n([Im(".mdc-notched-outline")],Lm.prototype,"mdcRoot",void 0),n([Tm({type:Number})],Lm.prototype,"width",void 0),n([Tm({type:Boolean,reflect:!0})],Lm.prototype,"open",void 0),n([Im(".mdc-notched-outline__notch")],Lm.prototype,"notchElement",void 0); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const Mm=((t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new oa(i,t,na)})`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}.mdc-select{display:inline-flex;position:relative}.mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87)}.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-select.mdc-select--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#6200ee;fill:var(--mdc-theme-primary, #6200ee)}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled)+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__icon{color:rgba(0, 0, 0, 0.54)}.mdc-select.mdc-select--disabled .mdc-select__icon{color:rgba(0, 0, 0, 0.38)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:red}.mdc-select.mdc-select--disabled .mdc-floating-label{color:GrayText}.mdc-select.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}.mdc-select.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select.mdc-select--disabled .mdc-notched-outline__trailing{border-color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__icon{color:GrayText}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:GrayText}}.mdc-select .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-select .mdc-select__anchor{padding-left:16px;padding-right:0}[dir=rtl] .mdc-select .mdc-select__anchor,.mdc-select .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:16px}.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor{padding-left:0;padding-right:0}[dir=rtl] .mdc-select.mdc-select--with-leading-icon .mdc-select__anchor,.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:0}.mdc-select .mdc-select__icon{width:24px;height:24px;font-size:24px}.mdc-select .mdc-select__dropdown-icon{width:24px;height:24px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item,.mdc-select .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic{margin-left:0;margin-right:12px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic,.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:12px;margin-right:0}.mdc-select__dropdown-icon{margin-left:12px;margin-right:12px;display:inline-flex;position:relative;align-self:center;align-items:center;justify-content:center;flex-shrink:0;pointer-events:none}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active,.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{position:absolute;top:0;left:0}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-graphic{width:41.6666666667%;height:20.8333333333%}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:1;transition:opacity 75ms linear 75ms}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:0;transition:opacity 75ms linear}[dir=rtl] .mdc-select__dropdown-icon,.mdc-select__dropdown-icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:0;transition:opacity 49.5ms linear}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:1;transition:opacity 100.5ms linear 49.5ms}.mdc-select__anchor{width:200px;min-width:0;flex:1 1 auto;position:relative;box-sizing:border-box;overflow:hidden;outline:none;cursor:pointer}.mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-select__selected-text-container{display:flex;appearance:none;pointer-events:none;box-sizing:border-box;width:auto;min-width:0;flex-grow:1;height:28px;border:none;outline:none;padding:0;background-color:transparent;color:inherit}.mdc-select__selected-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);line-height:1.75rem;line-height:var(--mdc-typography-subtitle1-line-height, 1.75rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;width:100%;text-align:left}[dir=rtl] .mdc-select__selected-text,.mdc-select__selected-text[dir=rtl]{text-align:right}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--invalid+.mdc-select-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--disabled{cursor:default;pointer-events:none}.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item{padding-left:12px;padding-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item,.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:12px;padding-right:12px}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select__menu::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid transparent;border-radius:inherit;content:"";pointer-events:none}}@media screen and (forced-colors: active)and (forced-colors: active),screen and (-ms-high-contrast: active)and (forced-colors: active){.mdc-select__menu::before{border-color:CanvasText}}.mdc-select__menu .mdc-deprecated-list .mdc-select__icon,.mdc-select__menu .mdc-list .mdc-select__icon{margin-left:0;margin-right:0}[dir=rtl] .mdc-select__menu .mdc-deprecated-list .mdc-select__icon,[dir=rtl] .mdc-select__menu .mdc-list .mdc-select__icon,.mdc-select__menu .mdc-deprecated-list .mdc-select__icon[dir=rtl],.mdc-select__menu .mdc-list .mdc-select__icon[dir=rtl]{margin-left:0;margin-right:0}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-list-item__start{display:inline-flex;align-items:center}.mdc-select__option{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select__option,.mdc-select__option[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select__one-line-option.mdc-list-item--with-one-line{height:48px}.mdc-select__two-line-option.mdc-list-item--with-two-lines{height:64px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__start{margin-top:20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before{display:inline-block;width:0;height:28px;content:"";vertical-align:0}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end{display:block;margin-top:0;line-height:normal}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{display:inline-block;width:0;height:36px;content:"";vertical-align:0}.mdc-select__option-with-leading-content{padding-left:0;padding-right:12px}.mdc-select__option-with-leading-content.mdc-list-item{padding-left:0;padding-right:auto}[dir=rtl] .mdc-select__option-with-leading-content.mdc-list-item,.mdc-select__option-with-leading-content.mdc-list-item[dir=rtl]{padding-left:auto;padding-right:0}.mdc-select__option-with-leading-content .mdc-list-item__start{margin-left:12px;margin-right:0}[dir=rtl] .mdc-select__option-with-leading-content .mdc-list-item__start,.mdc-select__option-with-leading-content .mdc-list-item__start[dir=rtl]{margin-left:0;margin-right:12px}.mdc-select__option-with-leading-content .mdc-list-item__start{width:36px;height:24px}[dir=rtl] .mdc-select__option-with-leading-content,.mdc-select__option-with-leading-content[dir=rtl]{padding-left:12px;padding-right:0}.mdc-select__option-with-meta.mdc-list-item{padding-left:auto;padding-right:0}[dir=rtl] .mdc-select__option-with-meta.mdc-list-item,.mdc-select__option-with-meta.mdc-list-item[dir=rtl]{padding-left:0;padding-right:auto}.mdc-select__option-with-meta .mdc-list-item__end{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select__option-with-meta .mdc-list-item__end,.mdc-select__option-with-meta .mdc-list-item__end[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--filled .mdc-select__anchor{height:56px;display:flex;align-items:baseline}.mdc-select--filled .mdc-select__anchor::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor::before{display:none}.mdc-select--filled .mdc-select__anchor{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0}.mdc-select--filled:not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke}.mdc-select--filled.mdc-select--disabled .mdc-select__anchor{background-color:#fafafa}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-select--filled:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--filled.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-select--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-select--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-select--filled .mdc-menu-surface--is-open-below{border-top-left-radius:0px;border-top-right-radius:0px}.mdc-select--filled.mdc-select--focused.mdc-line-ripple::after{transform:scale(1, 2);opacity:1}.mdc-select--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-select--filled .mdc-floating-label,.mdc-select--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{left:48px;right:initial}[dir=rtl] .mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined{border:none}.mdc-select--outlined .mdc-select__anchor{height:56px}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-56px{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-select--outlined .mdc-select__anchor{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-select--outlined+.mdc-select-helper-text{margin-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-select__anchor{background-color:transparent}.mdc-select--outlined.mdc-select--disabled .mdc-select__anchor{background-color:transparent}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}.mdc-select--outlined .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-select--outlined .mdc-select__anchor{display:flex;align-items:baseline;overflow:visible}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined 250ms 1}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--outlined .mdc-select__anchor::before{display:none}.mdc-select--outlined .mdc-select__selected-text-container{display:flex;border:none;z-index:1;background-color:transparent}.mdc-select--outlined .mdc-select__icon{z-index:2}.mdc-select--outlined .mdc-floating-label{line-height:1.15rem;left:4px;right:initial}[dir=rtl] .mdc-select--outlined .mdc-floating-label,.mdc-select--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-select--outlined.mdc-select--focused .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake,.mdc-select--outlined.mdc-select--with-leading-icon[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 96px)}.mdc-select--outlined .mdc-menu-surface{margin-bottom:8px}.mdc-select--outlined.mdc-select--no-label .mdc-menu-surface,.mdc-select--outlined .mdc-menu-surface--is-open-below{margin-bottom:0}.mdc-select__anchor{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-select__anchor .mdc-select__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-select__anchor .mdc-select__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-select__anchor.mdc-ripple-upgraded--unbounded .mdc-select__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-select__anchor.mdc-ripple-upgraded--foreground-activation .mdc-select__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-select__anchor.mdc-ripple-upgraded--foreground-deactivation .mdc-select__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-select__anchor:hover .mdc-select__ripple::before,.mdc-select__anchor.mdc-ripple-surface--hover .mdc-select__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__anchor.mdc-ripple-upgraded--background-focused .mdc-select__ripple::before,.mdc-select__anchor:not(.mdc-ripple-upgraded):focus .mdc-select__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__anchor .mdc-select__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-deprecated-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-deprecated-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-deprecated-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-deprecated-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select-helper-text{margin:0;margin-left:16px;margin-right:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal}[dir=rtl] .mdc-select-helper-text,.mdc-select-helper-text[dir=rtl]{margin-left:16px;margin-right:16px}.mdc-select-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-select-helper-text--validation-msg{opacity:0;transition:opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-select--invalid+.mdc-select-helper-text--validation-msg,.mdc-select-helper-text--validation-msg-persistent{opacity:1}.mdc-select--with-leading-icon .mdc-select__icon{display:inline-block;box-sizing:border-box;border:none;text-decoration:none;cursor:pointer;user-select:none;flex-shrink:0;align-self:center;background-color:transparent;fill:currentColor}.mdc-select--with-leading-icon .mdc-select__icon{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__icon,.mdc-select--with-leading-icon .mdc-select__icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select__icon:not([tabindex]),.mdc-select__icon[tabindex="-1"]{cursor:default;pointer-events:none}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-block;vertical-align:top;outline:none}.mdc-select{width:100%}[hidden]{display:none}.mdc-select__icon{z-index:2}.mdc-select--with-leading-icon{--mdc-list-item-graphic-margin: calc( 48px - var(--mdc-list-item-graphic-size, 24px) - var(--mdc-list-side-padding, 16px) )}.mdc-select .mdc-select__anchor .mdc-select__selected-text{overflow:hidden}.mdc-select .mdc-select__anchor *{display:inline-flex}.mdc-select .mdc-select__anchor .mdc-floating-label{display:inline-block}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-idle-border-color, rgba(0, 0, 0, 0.38) );--mdc-notched-outline-notch-offset: 1px}:host(:not([disabled]):hover) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87);color:var(--mdc-select-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-select-idle-line-color, rgba(0, 0, 0, 0.42))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-select-hover-line-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--outlined):not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke;background-color:var(--mdc-select-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-select__dropdown-icon{fill:var(--mdc-select-error-dropdown-icon-color, var(--mdc-select-error-color, var(--mdc-theme-error, #b00020)))}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label,:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label::after{color:var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select.mdc-select--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}.mdc-select__menu--invalid{--mdc-theme-primary: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.6);color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54);fill:var(--mdc-select-dropdown-icon-color, rgba(0, 0, 0, 0.54))}:host(:not([disabled])) .mdc-select.mdc-select--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px;--mdc-notched-outline-notch-offset: 2px}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-select__dropdown-icon{fill:rgba(98,0,238,.87);fill:var(--mdc-select-focused-dropdown-icon-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)))}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label::after{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select-helper-text:not(.mdc-select-helper-text--validation-msg){color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]){pointer-events:none}:host([disabled]) .mdc-select:not(.mdc-select--outlined).mdc-select--disabled .mdc-select__anchor{background-color:#fafafa;background-color:var(--mdc-select-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-select.mdc-select--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-select .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38);fill:var(--mdc-select-disabled-dropdown-icon-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select-helper-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,Nm=$l`@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}:host{display:block}.mdc-deprecated-list{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);line-height:1.75rem;line-height:var(--mdc-typography-subtitle1-line-height, 1.75rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);line-height:1.5rem;margin:0;padding:8px 0;list-style-type:none;color:rgba(0, 0, 0, 0.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87));padding:var(--mdc-list-vertical-padding, 8px) 0}.mdc-deprecated-list:focus{outline:none}.mdc-deprecated-list-item{height:48px}.mdc-deprecated-list--dense{padding-top:4px;padding-bottom:4px;font-size:.812rem}.mdc-deprecated-list ::slotted([divider]){height:0;margin:0;border:none;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgba(0, 0, 0, 0.12)}.mdc-deprecated-list ::slotted([divider][padded]){margin:0 var(--mdc-list-side-padding, 16px)}.mdc-deprecated-list ::slotted([divider][inset]){margin-left:var(--mdc-list-inset-margin, 72px);margin-right:0;width:calc( 100% - var(--mdc-list-inset-margin, 72px) )}[dir=rtl] .mdc-deprecated-list ::slotted([divider][inset]),.mdc-deprecated-list ::slotted([divider][inset][dir=rtl]){margin-left:0;margin-right:var(--mdc-list-inset-margin, 72px)}.mdc-deprecated-list ::slotted([divider][inset][padded]){width:calc( 100% - var(--mdc-list-inset-margin, 72px) - var(--mdc-list-side-padding, 16px) )}.mdc-deprecated-list--dense ::slotted([mwc-list-item]){height:40px}.mdc-deprecated-list--dense ::slotted([mwc-list]){--mdc-list-item-graphic-size: 20px}.mdc-deprecated-list--two-line.mdc-deprecated-list--dense ::slotted([mwc-list-item]),.mdc-deprecated-list--avatar-list.mdc-deprecated-list--dense ::slotted([mwc-list-item]){height:60px}.mdc-deprecated-list--avatar-list.mdc-deprecated-list--dense ::slotted([mwc-list]){--mdc-list-item-graphic-size: 36px}:host([noninteractive]){pointer-events:none;cursor:default}.mdc-deprecated-list--dense ::slotted(.mdc-deprecated-list-item__primary-text){display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-deprecated-list--dense ::slotted(.mdc-deprecated-list-item__primary-text)::before{display:inline-block;width:0;height:24px;content:"";vertical-align:0}.mdc-deprecated-list--dense ::slotted(.mdc-deprecated-list-item__primary-text)::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,Dm=$l`:host{cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent;height:48px;display:flex;position:relative;align-items:center;justify-content:flex-start;overflow:hidden;padding:0;padding-left:var(--mdc-list-side-padding, 16px);padding-right:var(--mdc-list-side-padding, 16px);outline:none;height:48px;color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87))}:host:focus{outline:none}:host([activated]){color:#6200ee;color:var(--mdc-theme-primary, #6200ee);--mdc-ripple-color: var( --mdc-theme-primary, #6200ee )}:host([activated]) .mdc-deprecated-list-item__graphic{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host([activated]) .fake-activated-ripple::before{position:absolute;display:block;top:0;bottom:0;left:0;right:0;width:100%;height:100%;pointer-events:none;z-index:1;content:"";opacity:0.12;opacity:var(--mdc-ripple-activated-opacity, 0.12);background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-deprecated-list-item__graphic{flex-shrink:0;align-items:center;justify-content:center;fill:currentColor;display:inline-flex}.mdc-deprecated-list-item__graphic ::slotted(*){flex-shrink:0;align-items:center;justify-content:center;fill:currentColor;width:100%;height:100%;text-align:center}.mdc-deprecated-list-item__meta{width:var(--mdc-list-item-meta-size, 24px);height:var(--mdc-list-item-meta-size, 24px);margin-left:auto;margin-right:0;color:rgba(0, 0, 0, 0.38);color:var(--mdc-theme-text-hint-on-background, rgba(0, 0, 0, 0.38))}.mdc-deprecated-list-item__meta.multi{width:auto}.mdc-deprecated-list-item__meta ::slotted(*){width:var(--mdc-list-item-meta-size, 24px);line-height:var(--mdc-list-item-meta-size, 24px)}.mdc-deprecated-list-item__meta ::slotted(.material-icons),.mdc-deprecated-list-item__meta ::slotted(mwc-icon){line-height:var(--mdc-list-item-meta-size, 24px) !important}.mdc-deprecated-list-item__meta ::slotted(:not(.material-icons):not(mwc-icon)){-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit)}[dir=rtl] .mdc-deprecated-list-item__meta,.mdc-deprecated-list-item__meta[dir=rtl]{margin-left:0;margin-right:auto}.mdc-deprecated-list-item__meta ::slotted(*){width:100%;height:100%}.mdc-deprecated-list-item__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mdc-deprecated-list-item__text ::slotted([for]),.mdc-deprecated-list-item__text[for]{pointer-events:none}.mdc-deprecated-list-item__primary-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;margin-top:0;line-height:normal;margin-bottom:-20px;display:block}.mdc-deprecated-list-item__primary-text::before{display:inline-block;width:0;height:32px;content:"";vertical-align:0}.mdc-deprecated-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-deprecated-list-item__secondary-text{-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);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;margin-top:0;line-height:normal;display:block}.mdc-deprecated-list-item__secondary-text::before{display:inline-block;width:0;height:20px;content:"";vertical-align:0}.mdc-deprecated-list--dense .mdc-deprecated-list-item__secondary-text{font-size:inherit}* ::slotted(a),a{color:inherit;text-decoration:none}:host([twoline]){height:72px}:host([twoline]) .mdc-deprecated-list-item__text{align-self:flex-start}:host([disabled]),:host([noninteractive]){cursor:default;pointer-events:none}:host([disabled]) .mdc-deprecated-list-item__text ::slotted(*){opacity:.38}:host([disabled]) .mdc-deprecated-list-item__text ::slotted(*),:host([disabled]) .mdc-deprecated-list-item__primary-text ::slotted(*),:host([disabled]) .mdc-deprecated-list-item__secondary-text ::slotted(*){color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-deprecated-list-item__secondary-text ::slotted(*){color:rgba(0, 0, 0, 0.54);color:var(--mdc-theme-text-secondary-on-background, rgba(0, 0, 0, 0.54))}.mdc-deprecated-list-item__graphic ::slotted(*){background-color:transparent;color:rgba(0, 0, 0, 0.38);color:var(--mdc-theme-text-icon-on-background, rgba(0, 0, 0, 0.38))}.mdc-deprecated-list-group__subheader ::slotted(*){color:rgba(0, 0, 0, 0.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87))}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic{width:var(--mdc-list-item-graphic-size, 40px);height:var(--mdc-list-item-graphic-size, 40px)}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic.multi{width:auto}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(*){width:var(--mdc-list-item-graphic-size, 40px);line-height:var(--mdc-list-item-graphic-size, 40px)}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon){line-height:var(--mdc-list-item-graphic-size, 40px) !important}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(*){border-radius:50%}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic,:host([graphic=medium]) .mdc-deprecated-list-item__graphic,:host([graphic=large]) .mdc-deprecated-list-item__graphic,:host([graphic=control]) .mdc-deprecated-list-item__graphic{margin-left:0;margin-right:var(--mdc-list-item-graphic-margin, 16px)}[dir=rtl] :host([graphic=avatar]) .mdc-deprecated-list-item__graphic,[dir=rtl] :host([graphic=medium]) .mdc-deprecated-list-item__graphic,[dir=rtl] :host([graphic=large]) .mdc-deprecated-list-item__graphic,[dir=rtl] :host([graphic=control]) .mdc-deprecated-list-item__graphic,:host([graphic=avatar]) .mdc-deprecated-list-item__graphic[dir=rtl],:host([graphic=medium]) .mdc-deprecated-list-item__graphic[dir=rtl],:host([graphic=large]) .mdc-deprecated-list-item__graphic[dir=rtl],:host([graphic=control]) .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:var(--mdc-list-item-graphic-margin, 16px);margin-right:0}:host([graphic=icon]) .mdc-deprecated-list-item__graphic{width:var(--mdc-list-item-graphic-size, 24px);height:var(--mdc-list-item-graphic-size, 24px);margin-left:0;margin-right:var(--mdc-list-item-graphic-margin, 32px)}:host([graphic=icon]) .mdc-deprecated-list-item__graphic.multi{width:auto}:host([graphic=icon]) .mdc-deprecated-list-item__graphic ::slotted(*){width:var(--mdc-list-item-graphic-size, 24px);line-height:var(--mdc-list-item-graphic-size, 24px)}:host([graphic=icon]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=icon]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon){line-height:var(--mdc-list-item-graphic-size, 24px) !important}[dir=rtl] :host([graphic=icon]) .mdc-deprecated-list-item__graphic,:host([graphic=icon]) .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:var(--mdc-list-item-graphic-margin, 32px);margin-right:0}:host([graphic=avatar]:not([twoLine])),:host([graphic=icon]:not([twoLine])){height:56px}:host([graphic=medium]:not([twoLine])),:host([graphic=large]:not([twoLine])){height:72px}:host([graphic=medium]) .mdc-deprecated-list-item__graphic,:host([graphic=large]) .mdc-deprecated-list-item__graphic{width:var(--mdc-list-item-graphic-size, 56px);height:var(--mdc-list-item-graphic-size, 56px)}:host([graphic=medium]) .mdc-deprecated-list-item__graphic.multi,:host([graphic=large]) .mdc-deprecated-list-item__graphic.multi{width:auto}:host([graphic=medium]) .mdc-deprecated-list-item__graphic ::slotted(*),:host([graphic=large]) .mdc-deprecated-list-item__graphic ::slotted(*){width:var(--mdc-list-item-graphic-size, 56px);line-height:var(--mdc-list-item-graphic-size, 56px)}:host([graphic=medium]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=medium]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon),:host([graphic=large]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=large]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon){line-height:var(--mdc-list-item-graphic-size, 56px) !important}:host([graphic=large]){padding-left:0px}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,Hm=((t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new lp(i,t,op)})`.mdc-ripple-surface{--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;position:relative;outline:none;overflow:hidden}.mdc-ripple-surface::before,.mdc-ripple-surface::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-ripple-surface::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-ripple-surface::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-ripple-surface.mdc-ripple-upgraded::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-ripple-surface.mdc-ripple-upgraded::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-ripple-surface.mdc-ripple-upgraded--unbounded::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-ripple-surface.mdc-ripple-upgraded--foreground-activation::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-ripple-surface.mdc-ripple-upgraded--foreground-deactivation::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-ripple-surface::before,.mdc-ripple-surface::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-ripple-surface.mdc-ripple-upgraded::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-ripple-surface[data-mdc-ripple-is-unbounded],.mdc-ripple-upgraded--unbounded{overflow:visible}.mdc-ripple-surface[data-mdc-ripple-is-unbounded]::before,.mdc-ripple-surface[data-mdc-ripple-is-unbounded]::after,.mdc-ripple-upgraded--unbounded::before,.mdc-ripple-upgraded--unbounded::after{top:calc(50% - 50%);left:calc(50% - 50%);width:100%;height:100%}.mdc-ripple-surface[data-mdc-ripple-is-unbounded].mdc-ripple-upgraded::before,.mdc-ripple-surface[data-mdc-ripple-is-unbounded].mdc-ripple-upgraded::after,.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::before,.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::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-ripple-surface[data-mdc-ripple-is-unbounded].mdc-ripple-upgraded::after,.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-ripple-surface::before,.mdc-ripple-surface::after{background-color:#000;background-color:var(--mdc-ripple-color, #000)}.mdc-ripple-surface:hover::before,.mdc-ripple-surface.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-ripple-surface.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-ripple-surface:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}:host{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;display:block}:host .mdc-ripple-surface{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;will-change:unset}.mdc-ripple-surface--primary::before,.mdc-ripple-surface--primary::after{background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-ripple-surface--primary:hover::before,.mdc-ripple-surface--primary.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-ripple-surface--primary.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--primary:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-ripple-surface--primary:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--primary:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--primary.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--primary--activated::before{opacity:0.12;opacity:var(--mdc-ripple-activated-opacity, 0.12)}.mdc-ripple-surface--primary--activated::before,.mdc-ripple-surface--primary--activated::after{background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-ripple-surface--primary--activated:hover::before,.mdc-ripple-surface--primary--activated.mdc-ripple-surface--hover::before{opacity:0.16;opacity:var(--mdc-ripple-hover-opacity, 0.16)}.mdc-ripple-surface--primary--activated.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--primary--activated:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-focus-opacity, 0.24)}.mdc-ripple-surface--primary--activated:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--primary--activated:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--primary--activated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--primary--selected::before{opacity:0.08;opacity:var(--mdc-ripple-selected-opacity, 0.08)}.mdc-ripple-surface--primary--selected::before,.mdc-ripple-surface--primary--selected::after{background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-ripple-surface--primary--selected:hover::before,.mdc-ripple-surface--primary--selected.mdc-ripple-surface--hover::before{opacity:0.12;opacity:var(--mdc-ripple-hover-opacity, 0.12)}.mdc-ripple-surface--primary--selected.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--primary--selected:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-focus-opacity, 0.2)}.mdc-ripple-surface--primary--selected:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--primary--selected:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--primary--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--accent::before,.mdc-ripple-surface--accent::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-ripple-surface--accent:hover::before,.mdc-ripple-surface--accent.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-ripple-surface--accent.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--accent:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-ripple-surface--accent:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--accent:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--accent.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--accent--activated::before{opacity:0.12;opacity:var(--mdc-ripple-activated-opacity, 0.12)}.mdc-ripple-surface--accent--activated::before,.mdc-ripple-surface--accent--activated::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-ripple-surface--accent--activated:hover::before,.mdc-ripple-surface--accent--activated.mdc-ripple-surface--hover::before{opacity:0.16;opacity:var(--mdc-ripple-hover-opacity, 0.16)}.mdc-ripple-surface--accent--activated.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--accent--activated:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-focus-opacity, 0.24)}.mdc-ripple-surface--accent--activated:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--accent--activated:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--accent--activated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--accent--selected::before{opacity:0.08;opacity:var(--mdc-ripple-selected-opacity, 0.08)}.mdc-ripple-surface--accent--selected::before,.mdc-ripple-surface--accent--selected::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-ripple-surface--accent--selected:hover::before,.mdc-ripple-surface--accent--selected.mdc-ripple-surface--hover::before{opacity:0.12;opacity:var(--mdc-ripple-hover-opacity, 0.12)}.mdc-ripple-surface--accent--selected.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--accent--selected:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-focus-opacity, 0.2)}.mdc-ripple-surface--accent--selected:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--accent--selected:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--accent--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--disabled{opacity:0}.mdc-ripple-surface--internal-use-state-layer-custom-properties::before,.mdc-ripple-surface--internal-use-state-layer-custom-properties::after{background-color:#000;background-color:var(--mdc-ripple-hover-state-layer-color, #000)}.mdc-ripple-surface--internal-use-state-layer-custom-properties:hover::before,.mdc-ripple-surface--internal-use-state-layer-custom-properties.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-state-layer-opacity, 0.04)}.mdc-ripple-surface--internal-use-state-layer-custom-properties.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--internal-use-state-layer-custom-properties:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-state-layer-opacity, 0.12)}.mdc-ripple-surface--internal-use-state-layer-custom-properties:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--internal-use-state-layer-custom-properties:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-pressed-state-layer-opacity, 0.12)}.mdc-ripple-surface--internal-use-state-layer-custom-properties.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-pressed-state-layer-opacity, 0.12)}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,Um=Nc`mwc-list ::slotted([mwc-list-item]:not([twoline])),mwc-list ::slotted([noninteractive]:not([twoline])){height:var(--mdc-menu-item-height, 48px)}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,zm=Nc`.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)}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,Fm=((t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new Tu(i,t,Su)})`.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}:host{display:block;position:absolute;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] :host,:host([dir=rtl]){text-align:right}::slotted(.mdc-floating-label){display:inline-block;position:relative;top:17px;bottom:auto;max-width:100%}::slotted(.mdc-floating-label--float-above){text-overflow:clip}.mdc-notched-outline--upgraded ::slotted(.mdc-floating-label--float-above){max-width:calc(100% / 0.75)}.mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-notched-outline .mdc-notched-outline__leading,.mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{border-color:var(--mdc-notched-outline-border-color, var(--mdc-theme-primary, #6200ee));border-width:1px;border-width:var(--mdc-notched-outline-stroke-width, 1px)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0;padding-top:var(--mdc-notched-outline-notch-offset, 0)}`,Bm={"mwc-select":class extends gl{static get styles(){return Mm}},"mwc-list":class extends vc{static get styles(){return Nm}},"mwc-list-item":class extends $c{static get styles(){return Dm}},"mwc-ripple":class extends _u{static get styles(){return Hm}},"mwc-menu":class extends jh{static get styles(){return Um}},"mwc-menu-surface":class extends Gh{static get styles(){return zm}},"mwc-notched-outline":class extends Lm{static get styles(){return Fm}}};function Vm(t,e,i){if(void 0!==e) -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -return function(t,e,i){const s=t.constructor;if(!i){const t=`__${e}`;if(!(i=s.getPropertyDescriptor(e,t)))throw new Error("@ariaProperty must be used after a @property decorator")}const n=i;let r="";if(!n.set)throw new Error(`@ariaProperty requires a setter for ${e}`);if(t.dispatchWizEvent)return i;const o={configurable:!0,enumerable:!0,set(t){if(""===r){const t=s.getPropertyOptions(e);r="string"==typeof t.attribute?t.attribute:e}this.hasAttribute(r)&&this.removeAttribute(r),n.set.call(this,t)}};return n.get&&(o.get=function(){return n.get.call(this)}),o}(t,e,i);throw new Error("@ariaProperty only supports TypeScript Decorators")} -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var jm=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),Wm={CHECKED:"mdc-switch--checked",DISABLED:"mdc-switch--disabled"},qm={ARIA_CHECKED_ATTR:"aria-checked",NATIVE_CONTROL_SELECTOR:".mdc-switch__native-control",RIPPLE_SURFACE_SELECTOR:".mdc-switch__thumb-underlay"},Ym=function(t){function e(i){return t.call(this,s(s({},e.defaultAdapter),i))||this}return i(e,t),Object.defineProperty(e,"strings",{get:function(){return qm},enumerable:!1,configurable:!0}),Object.defineProperty(e,"cssClasses",{get:function(){return Wm},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},setNativeControlChecked:function(){},setNativeControlDisabled:function(){},setNativeControlAttr:function(){}}},enumerable:!1,configurable:!0}),e.prototype.setChecked=function(t){this.adapter.setNativeControlChecked(t),this.updateAriaChecked(t),this.updateCheckedStyling(t)},e.prototype.setDisabled=function(t){this.adapter.setNativeControlDisabled(t),t?this.adapter.addClass(Wm.DISABLED):this.adapter.removeClass(Wm.DISABLED)},e.prototype.handleChange=function(t){var e=t.target;this.updateAriaChecked(e.checked),this.updateCheckedStyling(e.checked)},e.prototype.updateCheckedStyling=function(t){t?this.adapter.addClass(Wm.CHECKED):this.adapter.removeClass(Wm.CHECKED)},e.prototype.updateAriaChecked=function(t){this.adapter.setNativeControlAttr(qm.ARIA_CHECKED_ATTR,""+!!t)},e}(jm); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Xm=window,Gm=Xm.ShadowRoot&&(void 0===Xm.ShadyCSS||Xm.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Km=Symbol(),Jm=new WeakMap;let Zm=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==Km)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(Gm&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=Jm.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&Jm.set(e,t))}return t}toString(){return this.cssText}};const Qm=Gm?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new Zm("string"==typeof t?t:t+"",void 0,Km))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var tf;const ef=window,sf=ef.trustedTypes,nf=sf?sf.emptyScript:"",rf=ef.reactiveElementPolyfillSupport,of={toAttribute(t,e){switch(e){case Boolean:t=t?nf:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},af=(t,e)=>e!==t&&(e==e||t==t),lf={attribute:!0,type:String,converter:of,reflect:!1,hasChanged:af},df="finalized";let cf=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=lf){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||lf}static finalize(){if(this.hasOwnProperty(df))return!1;this[df]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(Qm(t))}else void 0!==t&&e.push(Qm(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{Gm?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=Xm.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=lf){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:of).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:of;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||af)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var hf;cf[df]=!0,cf.elementProperties=new Map,cf.elementStyles=[],cf.shadowRootOptions={mode:"open"},null==rf||rf({ReactiveElement:cf}),(null!==(tf=ef.reactiveElementVersions)&&void 0!==tf?tf:ef.reactiveElementVersions=[]).push("1.6.3");const pf=window,uf=pf.trustedTypes,mf=uf?uf.createPolicy("lit-html",{createHTML:t=>t}):void 0,ff="$lit$",gf=`lit$${(Math.random()+"").slice(9)}$`,vf="?"+gf,_f=`<${vf}>`,bf=document,yf=()=>bf.createComment(""),xf=t=>null===t||"object"!=typeof t&&"function"!=typeof t,$f=Array.isArray,wf="[ \t\n\f\r]",Af=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Ef=/-->/g,Sf=/>/g,Cf=RegExp(`>|${wf}(?:([^\\s"'>=/]+)(${wf}*=${wf}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Tf=/'/g,If=/"/g,Of=/^(?:script|style|textarea|title)$/i,kf=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),Rf=Symbol.for("lit-noChange"),Pf=Symbol.for("lit-nothing"),Lf=new WeakMap,Mf=bf.createTreeWalker(bf,129,null,!1);function Nf(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==mf?mf.createHTML(e):e}let Df=class t{constructor({strings:e,_$litType$:i},s){let n;this.parts=[];let r=0,o=0;const a=e.length-1,l=this.parts,[d,c]=((t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=Af;for(let e=0;e"===l[0]?(o=null!=n?n:Af,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?Cf:'"'===l[3]?If:Tf):o===If||o===Tf?o=Cf:o===Ef||o===Sf?o=Af:(o=Cf,n=void 0);const h=o===Cf&&t[e+1].startsWith("/>")?" ":"";r+=o===Af?i+_f:d>=0?(s.push(a),i.slice(0,d)+ff+i.slice(d)+gf+h):i+gf+(-2===d?(s.push(void 0),e):h)}return[Nf(t,r+(t[i]||"")+(2===e?"":"")),s]})(e,i);if(this.el=t.createElement(d,s),Mf.currentNode=this.el.content,2===i){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=Mf.nextNode())&&l.length0){n.textContent=uf?uf.emptyScript:"";for(let i=0;i$f(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==Pf&&xf(this._$AH)?this._$AA.nextSibling.data=t:this.$(bf.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=Df.createElement(Nf(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new Uf(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=Lf.get(t.strings);return void 0===e&&Lf.set(t.strings,e=new Df(t)),e}T(e){$f(this._$AH)||(this._$AH=[],this._$AR());const i=this._$AH;let s,n=0;for(const r of e)n===i.length?i.push(s=new t(this.k(yf()),this.k(yf()),this,this.options)):s=i[n],s._$AI(r),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=Pf}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=Hf(this,t,e,0),r=!xf(t)||t!==this._$AH&&t!==Rf,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new zf(e.insertBefore(yf(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return Rf}};Kf.finalized=!0,Kf._$litElement$=!0,null===(Xf=globalThis.litElementHydrateSupport)||void 0===Xf||Xf.call(globalThis,{LitElement:Kf});const Jf=globalThis.litElementPolyfillSupport;null==Jf||Jf({LitElement:Kf}),(null!==(Gf=globalThis.litElementVersions)&&void 0!==Gf?Gf:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Zf=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function Qf(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):Zf(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const tg=({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function eg(t){return tg({finisher:(e,i)=>{Object.assign(e.prototype[i],t)}})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function ig(t,e){return tg({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var sg;null===(sg=window.HTMLSlotElement)||void 0===sg||sg.prototype.assignedElements; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const ng=t=>null!=t?t:Pf; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class rg extends Qi{constructor(){super(...arguments),this.checked=!1,this.disabled=!1,this.shouldRenderRipple=!1,this.mdcFoundationClass=Ym,this.rippleHandlers=new _c(()=>(this.shouldRenderRipple=!0,this.ripple))}changeHandler(t){this.mdcFoundation.handleChange(t),this.checked=this.formElement.checked}createAdapter(){return Object.assign(Object.assign({},Xi(this.mdcRoot)),{setNativeControlChecked:t=>{this.formElement.checked=t},setNativeControlDisabled:t=>{this.formElement.disabled=t},setNativeControlAttr:(t,e)=>{this.formElement.setAttribute(t,e)}})}renderRipple(){return this.shouldRenderRipple?kf` - - `:""}focus(){const t=this.formElement;t&&(this.rippleHandlers.startFocus(),t.focus())}blur(){const t=this.formElement;t&&(this.rippleHandlers.endFocus(),t.blur())}click(){this.formElement&&!this.disabled&&(this.formElement.focus(),this.formElement.click())}firstUpdated(){super.firstUpdated(),this.shadowRoot&&this.mdcRoot.addEventListener("change",t=>{this.dispatchEvent(new Event("change",t))})}render(){return kf` -
-
-
- ${this.renderRipple()} -
- -
-
-
`}handleRippleMouseDown(t){const e=()=>{window.removeEventListener("mouseup",e),this.handleRippleDeactivate()};window.addEventListener("mouseup",e),this.rippleHandlers.startPress(t)}handleRippleTouchStart(t){this.rippleHandlers.startPress(t)}handleRippleDeactivate(){this.rippleHandlers.endPress()}handleRippleMouseEnter(){this.rippleHandlers.startHover()}handleRippleMouseLeave(){this.rippleHandlers.endHover()}handleRippleFocus(){this.rippleHandlers.startFocus()}handleRippleBlur(){this.rippleHandlers.endFocus()}}n([Qf({type:Boolean}),os(function(t){this.mdcFoundation.setChecked(t)})],rg.prototype,"checked",void 0),n([Qf({type:Boolean}),os(function(t){this.mdcFoundation.setDisabled(t)})],rg.prototype,"disabled",void 0),n([Vm,Qf({attribute:"aria-label"})],rg.prototype,"ariaLabel",void 0),n([Vm,Qf({attribute:"aria-labelledby"})],rg.prototype,"ariaLabelledBy",void 0),n([ig(".mdc-switch")],rg.prototype,"mdcRoot",void 0),n([ig("input")],rg.prototype,"formElement",void 0),n([function(t){return tg({descriptor:e=>({async get(){var e;return await this.updateComplete,null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t)},enumerable:!0,configurable:!0})})}("mwc-ripple")],rg.prototype,"ripple",void 0),n([function(t){return Qf({...t,state:!0})}()],rg.prototype,"shouldRenderRipple",void 0),n([eg({passive:!0})],rg.prototype,"handleRippleMouseDown",null),n([eg({passive:!0})],rg.prototype,"handleRippleTouchStart",null); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const og=((t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new Zm(i,t,Km)})`.mdc-switch__thumb-underlay{left:-14px;right:initial;top:-17px;width:48px;height:48px}[dir=rtl] .mdc-switch__thumb-underlay,.mdc-switch__thumb-underlay[dir=rtl]{left:initial;right:-14px}.mdc-switch__native-control{width:64px;height:48px}.mdc-switch{display:inline-block;position:relative;outline:none;user-select:none}.mdc-switch.mdc-switch--checked .mdc-switch__track{background-color:#018786;background-color:var(--mdc-theme-secondary, #018786)}.mdc-switch.mdc-switch--checked .mdc-switch__thumb{background-color:#018786;background-color:var(--mdc-theme-secondary, #018786);border-color:#018786;border-color:var(--mdc-theme-secondary, #018786)}.mdc-switch:not(.mdc-switch--checked) .mdc-switch__track{background-color:#000;background-color:var(--mdc-theme-on-surface, #000)}.mdc-switch:not(.mdc-switch--checked) .mdc-switch__thumb{background-color:#fff;background-color:var(--mdc-theme-surface, #fff);border-color:#fff;border-color:var(--mdc-theme-surface, #fff)}.mdc-switch__native-control{left:0;right:initial;position:absolute;top:0;margin:0;opacity:0;cursor:pointer;pointer-events:auto;transition:transform 90ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-switch__native-control,.mdc-switch__native-control[dir=rtl]{left:initial;right:0}.mdc-switch__track{box-sizing:border-box;width:36px;height:14px;border:1px solid transparent;border-radius:7px;opacity:.38;transition:opacity 90ms cubic-bezier(0.4, 0, 0.2, 1),background-color 90ms cubic-bezier(0.4, 0, 0.2, 1),border-color 90ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-switch__thumb-underlay{display:flex;position:absolute;align-items:center;justify-content:center;transform:translateX(0);transition:transform 90ms cubic-bezier(0.4, 0, 0.2, 1),background-color 90ms cubic-bezier(0.4, 0, 0.2, 1),border-color 90ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-switch__thumb{box-shadow:0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0,0,0,.12);box-sizing:border-box;width:20px;height:20px;border:10px solid;border-radius:50%;pointer-events:none;z-index:1}.mdc-switch--checked .mdc-switch__track{opacity:.54}.mdc-switch--checked .mdc-switch__thumb-underlay{transform:translateX(16px)}[dir=rtl] .mdc-switch--checked .mdc-switch__thumb-underlay,.mdc-switch--checked .mdc-switch__thumb-underlay[dir=rtl]{transform:translateX(-16px)}.mdc-switch--checked .mdc-switch__native-control{transform:translateX(-16px)}[dir=rtl] .mdc-switch--checked .mdc-switch__native-control,.mdc-switch--checked .mdc-switch__native-control[dir=rtl]{transform:translateX(16px)}.mdc-switch--disabled{opacity:.38;pointer-events:none}.mdc-switch--disabled .mdc-switch__thumb{border-width:1px}.mdc-switch--disabled .mdc-switch__native-control{cursor:default;pointer-events:none}:host{display:inline-flex;outline:none;-webkit-tap-highlight-color:transparent}`,ag={"mwc-switch":class extends rg{static get styles(){return og}},"mwc-ripple":class extends _u{static get styles(){return Hm}}}; -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var lg=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),dg={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",INPUT_SELECTOR:".mdc-text-field__input",LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-text-field__icon--leading",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",OUTLINE_SELECTOR:".mdc-notched-outline",PREFIX_SELECTOR:".mdc-text-field__affix--prefix",SUFFIX_SELECTOR:".mdc-text-field__affix--suffix",TRAILING_ICON_SELECTOR:".mdc-text-field__icon--trailing"},cg={DISABLED:"mdc-text-field--disabled",FOCUSED:"mdc-text-field--focused",HELPER_LINE:"mdc-text-field-helper-line",INVALID:"mdc-text-field--invalid",LABEL_FLOATING:"mdc-text-field--label-floating",NO_LABEL:"mdc-text-field--no-label",OUTLINED:"mdc-text-field--outlined",ROOT:"mdc-text-field",TEXTAREA:"mdc-text-field--textarea",WITH_LEADING_ICON:"mdc-text-field--with-leading-icon",WITH_TRAILING_ICON:"mdc-text-field--with-trailing-icon",WITH_INTERNAL_COUNTER:"mdc-text-field--with-internal-counter"},hg={LABEL_SCALE:.75},pg=["pattern","min","max","required","step","minlength","maxlength"],ug=["color","date","datetime-local","month","range","time","week"],mg=["mousedown","touchstart"],fg=["click","keydown"],gg=function(t){function e(i,n){void 0===n&&(n={});var r=t.call(this,s(s({},e.defaultAdapter),i))||this;return r.isFocused=!1,r.receivedUserInput=!1,r.valid=!0,r.useNativeValidation=!0,r.validateOnValueChange=!0,r.helperText=n.helperText,r.characterCounter=n.characterCounter,r.leadingIcon=n.leadingIcon,r.trailingIcon=n.trailingIcon,r.inputFocusHandler=function(){r.activateFocus()},r.inputBlurHandler=function(){r.deactivateFocus()},r.inputInputHandler=function(){r.handleInput()},r.setPointerXOffset=function(t){r.setTransformOrigin(t)},r.textFieldInteractionHandler=function(){r.handleTextFieldInteraction()},r.validationAttributeChangeHandler=function(t){r.handleValidationAttributeChange(t)},r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return cg},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return dg},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return hg},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldAlwaysFloat",{get:function(){var t=this.getNativeInput().type;return ug.indexOf(t)>=0},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldFloat",{get:function(){return this.shouldAlwaysFloat||this.isFocused||!!this.getValue()||this.isBadInput()},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldShake",{get:function(){return!this.isFocused&&!this.isValid()&&!!this.getValue()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!0},setInputAttr:function(){},removeInputAttr:function(){},registerTextFieldInteractionHandler:function(){},deregisterTextFieldInteractionHandler:function(){},registerInputInteractionHandler:function(){},deregisterInputInteractionHandler:function(){},registerValidationAttributeChangeHandler:function(){return new MutationObserver(function(){})},deregisterValidationAttributeChangeHandler:function(){},getNativeInput:function(){return null},isFocused:function(){return!1},activateLineRipple:function(){},deactivateLineRipple:function(){},setLineRippleTransformOrigin:function(){},shakeLabel:function(){},floatLabel:function(){},setLabelRequired:function(){},hasLabel:function(){return!1},getLabelWidth:function(){return 0},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t,e,i,s;this.adapter.hasLabel()&&this.getNativeInput().required&&this.adapter.setLabelRequired(!0),this.adapter.isFocused()?this.inputFocusHandler():this.adapter.hasLabel()&&this.shouldFloat&&(this.notchOutline(!0),this.adapter.floatLabel(!0),this.styleFloating(!0)),this.adapter.registerInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.registerInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.registerInputInteractionHandler("input",this.inputInputHandler);try{for(var n=r(mg),o=n.next();!o.done;o=n.next()){var a=o.value;this.adapter.registerInputInteractionHandler(a,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{o&&!o.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}try{for(var l=r(fg),d=l.next();!d.done;d=l.next()){a=d.value;this.adapter.registerTextFieldInteractionHandler(a,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{d&&!d.done&&(s=l.return)&&s.call(l)}finally{if(i)throw i.error}}this.validationObserver=this.adapter.registerValidationAttributeChangeHandler(this.validationAttributeChangeHandler),this.setcharacterCounter(this.getValue().length)},e.prototype.destroy=function(){var t,e,i,s;this.adapter.deregisterInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.deregisterInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.deregisterInputInteractionHandler("input",this.inputInputHandler);try{for(var n=r(mg),o=n.next();!o.done;o=n.next()){var a=o.value;this.adapter.deregisterInputInteractionHandler(a,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{o&&!o.done&&(e=n.return)&&e.call(n)}finally{if(t)throw t.error}}try{for(var l=r(fg),d=l.next();!d.done;d=l.next()){a=d.value;this.adapter.deregisterTextFieldInteractionHandler(a,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{d&&!d.done&&(s=l.return)&&s.call(l)}finally{if(i)throw i.error}}this.adapter.deregisterValidationAttributeChangeHandler(this.validationObserver)},e.prototype.handleTextFieldInteraction=function(){var t=this.adapter.getNativeInput();t&&t.disabled||(this.receivedUserInput=!0)},e.prototype.handleValidationAttributeChange=function(t){var e=this;t.some(function(t){return pg.indexOf(t)>-1&&(e.styleValidity(!0),e.adapter.setLabelRequired(e.getNativeInput().required),!0)}),t.indexOf("maxlength")>-1&&this.setcharacterCounter(this.getValue().length)},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()&&this.adapter.hasLabel())if(t){var e=this.adapter.getLabelWidth()*hg.LABEL_SCALE;this.adapter.notchOutline(e)}else this.adapter.closeOutline()},e.prototype.activateFocus=function(){this.isFocused=!0,this.styleFocused(this.isFocused),this.adapter.activateLineRipple(),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),!this.helperText||!this.helperText.isPersistent()&&this.helperText.isValidation()&&this.valid||this.helperText.showToScreenReader()},e.prototype.setTransformOrigin=function(t){if(!this.isDisabled()&&!this.adapter.hasOutline()){var e=t.touches,i=e?e[0]:t,s=i.target.getBoundingClientRect(),n=i.clientX-s.left;this.adapter.setLineRippleTransformOrigin(n)}},e.prototype.handleInput=function(){this.autoCompleteFocus(),this.setcharacterCounter(this.getValue().length)},e.prototype.autoCompleteFocus=function(){this.receivedUserInput||this.activateFocus()},e.prototype.deactivateFocus=function(){this.isFocused=!1,this.adapter.deactivateLineRipple();var t=this.isValid();this.styleValidity(t),this.styleFocused(this.isFocused),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),this.shouldFloat||(this.receivedUserInput=!1)},e.prototype.getValue=function(){return this.getNativeInput().value},e.prototype.setValue=function(t){if(this.getValue()!==t&&(this.getNativeInput().value=t),this.setcharacterCounter(t.length),this.validateOnValueChange){var e=this.isValid();this.styleValidity(e)}this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.validateOnValueChange&&this.adapter.shakeLabel(this.shouldShake))},e.prototype.isValid=function(){return this.useNativeValidation?this.isNativeInputValid():this.valid},e.prototype.setValid=function(t){this.valid=t,this.styleValidity(t);var e=!t&&!this.isFocused&&!!this.getValue();this.adapter.hasLabel()&&this.adapter.shakeLabel(e)},e.prototype.setValidateOnValueChange=function(t){this.validateOnValueChange=t},e.prototype.getValidateOnValueChange=function(){return this.validateOnValueChange},e.prototype.setUseNativeValidation=function(t){this.useNativeValidation=t},e.prototype.isDisabled=function(){return this.getNativeInput().disabled},e.prototype.setDisabled=function(t){this.getNativeInput().disabled=t,this.styleDisabled(t)},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.setTrailingIconAriaLabel=function(t){this.trailingIcon&&this.trailingIcon.setAriaLabel(t)},e.prototype.setTrailingIconContent=function(t){this.trailingIcon&&this.trailingIcon.setContent(t)},e.prototype.setcharacterCounter=function(t){if(this.characterCounter){var e=this.getNativeInput().maxLength;if(-1===e)throw new Error("MDCTextFieldFoundation: Expected maxlength html property on text input or textarea.");this.characterCounter.setCounterValue(t,e)}},e.prototype.isBadInput=function(){return this.getNativeInput().validity.badInput||!1},e.prototype.isNativeInputValid=function(){return this.getNativeInput().validity.valid},e.prototype.styleValidity=function(t){var i=e.cssClasses.INVALID;if(t?this.adapter.removeClass(i):this.adapter.addClass(i),this.helperText){if(this.helperText.setValidity(t),!this.helperText.isValidation())return;var s=this.helperText.isVisible(),n=this.helperText.getId();s&&n?this.adapter.setInputAttr(dg.ARIA_DESCRIBEDBY,n):this.adapter.removeInputAttr(dg.ARIA_DESCRIBEDBY)}},e.prototype.styleFocused=function(t){var i=e.cssClasses.FOCUSED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.styleDisabled=function(t){var i=e.cssClasses,s=i.DISABLED,n=i.INVALID;t?(this.adapter.addClass(s),this.adapter.removeClass(n)):this.adapter.removeClass(s),this.leadingIcon&&this.leadingIcon.setDisabled(t),this.trailingIcon&&this.trailingIcon.setDisabled(t)},e.prototype.styleFloating=function(t){var i=e.cssClasses.LABEL_FLOATING;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.getNativeInput=function(){return(this.adapter?this.adapter.getNativeInput():null)||{disabled:!1,maxLength:-1,required:!1,type:"input",validity:{badInput:!1,valid:!0},value:""}},e}(lg); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const vg=window,_g=vg.ShadowRoot&&(void 0===vg.ShadyCSS||vg.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,bg=Symbol(),yg=new WeakMap;let xg=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==bg)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(_g&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=yg.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&yg.set(e,t))}return t}toString(){return this.cssText}};const $g=_g?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new xg("string"==typeof t?t:t+"",void 0,bg))(e)})(t):t; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var wg;const Ag=window,Eg=Ag.trustedTypes,Sg=Eg?Eg.emptyScript:"",Cg=Ag.reactiveElementPolyfillSupport,Tg={toAttribute(t,e){switch(e){case Boolean:t=t?Sg:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},Ig=(t,e)=>e!==t&&(e==e||t==t),Og={attribute:!0,type:String,converter:Tg,reflect:!1,hasChanged:Ig},kg="finalized";let Rg=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach((e,i)=>{const s=this._$Ep(i,e);void 0!==s&&(this._$Ev.set(s,i),t.push(s))}),t}static createProperty(t,e=Og){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,s=this.getPropertyDescriptor(t,i,e);void 0!==s&&Object.defineProperty(this.prototype,t,s)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(s){const n=this[t];this[e]=s,this.requestUpdate(t,n,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||Og}static finalize(){if(this.hasOwnProperty(kg))return!1;this[kg]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift($g(t))}else void 0!==t&&e.push($g(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise(t=>this.enableUpdating=t),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach(t=>t(this))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])})}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{_g?t.adoptedStyleSheets=e.map(t=>t instanceof CSSStyleSheet?t:t.styleSheet):e.forEach(e=>{const i=document.createElement("style"),s=vg.litNonce;void 0!==s&&i.setAttribute("nonce",s),i.textContent=e.cssText,t.appendChild(i)})})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)})}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)})}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=Og){var s;const n=this.constructor._$Ep(t,i);if(void 0!==n&&!0===i.reflect){const r=(void 0!==(null===(s=i.converter)||void 0===s?void 0:s.toAttribute)?i.converter:Tg).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(n):this.setAttribute(n,r),this._$El=null}}_$AK(t,e){var i;const s=this.constructor,n=s._$Ev.get(t);if(void 0!==n&&this._$El!==n){const t=s.getPropertyOptions(n),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:Tg;this._$El=n,this[n]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let s=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||Ig)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):s=!1),!this.isUpdatePending&&s&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach((t,e)=>this[e]=t),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach(t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)}),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach(t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)}),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach((t,e)=>this._$EO(e,this[e],t)),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var Pg;Rg[kg]=!0,Rg.elementProperties=new Map,Rg.elementStyles=[],Rg.shadowRootOptions={mode:"open"},null==Cg||Cg({ReactiveElement:Rg}),(null!==(wg=Ag.reactiveElementVersions)&&void 0!==wg?wg:Ag.reactiveElementVersions=[]).push("1.6.3");const Lg=window,Mg=Lg.trustedTypes,Ng=Mg?Mg.createPolicy("lit-html",{createHTML:t=>t}):void 0,Dg="$lit$",Hg=`lit$${(Math.random()+"").slice(9)}$`,Ug="?"+Hg,zg=`<${Ug}>`,Fg=document,Bg=()=>Fg.createComment(""),Vg=t=>null===t||"object"!=typeof t&&"function"!=typeof t,jg=Array.isArray,Wg="[ \t\n\f\r]",qg=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,Yg=/-->/g,Xg=/>/g,Gg=RegExp(`>|${Wg}(?:([^\\s"'>=/]+)(${Wg}*=${Wg}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),Kg=/'/g,Jg=/"/g,Zg=/^(?:script|style|textarea|title)$/i,Qg=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),tv=Symbol.for("lit-noChange"),ev=Symbol.for("lit-nothing"),iv=new WeakMap,sv=Fg.createTreeWalker(Fg,129,null,!1);function nv(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==Ng?Ng.createHTML(e):e}const rv=(t,e)=>{const i=t.length-1,s=[];let n,r=2===e?"":"",o=qg;for(let e=0;e"===l[0]?(o=null!=n?n:qg,d=-1):void 0===l[1]?d=-2:(d=o.lastIndex-l[2].length,a=l[1],o=void 0===l[3]?Gg:'"'===l[3]?Jg:Kg):o===Jg||o===Kg?o=Gg:o===Yg||o===Xg?o=qg:(o=Gg,n=void 0);const h=o===Gg&&t[e+1].startsWith("/>")?" ":"";r+=o===qg?i+zg:d>=0?(s.push(a),i.slice(0,d)+Dg+i.slice(d)+Hg+h):i+Hg+(-2===d?(s.push(void 0),e):h)}return[nv(t,r+(t[i]||"")+(2===e?"":"")),s]};class ov{constructor({strings:t,_$litType$:e},i){let s;this.parts=[];let n=0,r=0;const o=t.length-1,a=this.parts,[l,d]=rv(t,e);if(this.el=ov.createElement(l,i),sv.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(s=sv.nextNode())&&a.length0){s.textContent=Mg?Mg.emptyScript:"";for(let i=0;ijg(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==ev&&Vg(this._$AH)?this._$AA.nextSibling.data=t:this.$(Fg.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:s}=t,n="number"==typeof s?this._$AC(t):(void 0===s.el&&(s.el=ov.createElement(nv(s.h,s.h[0]),this.options)),s);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===n)this._$AH.v(i);else{const t=new lv(n,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=iv.get(t.strings);return void 0===e&&iv.set(t.strings,e=new ov(t)),e}T(t){jg(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,s=0;for(const n of t)s===e.length?e.push(i=new dv(this.k(Bg()),this.k(Bg()),this,this.options)):i=e[s],i._$AI(n),s++;s2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=ev}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,s){const n=this.strings;let r=!1;if(void 0===n)t=av(this,t,e,0),r=!Vg(t)||t!==this._$AH&&t!==tv,r&&(this._$AH=t);else{const s=t;let o,a;for(t=n[0],o=0;o{var s,n;const r=null!==(s=null==i?void 0:i.renderBefore)&&void 0!==s?s:e;let o=r._$litPart$;if(void 0===o){const t=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:null;r._$litPart$=o=new dv(e.insertBefore(Bg(),t),t,void 0,null!=i?i:{})}return o._$AI(t),o})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return tv}};bv.finalized=!0,bv._$litElement$=!0,null===(vv=globalThis.litElementHydrateSupport)||void 0===vv||vv.call(globalThis,{LitElement:bv});const yv=globalThis.litElementPolyfillSupport;null==yv||yv({LitElement:bv}),(null!==(_v=globalThis.litElementVersions)&&void 0!==_v?_v:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const xv=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function $v(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):xv(t,e)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function wv(t){return $v({...t,state:!0})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Av=({finisher:t,descriptor:e})=>(i,s)=>{var n;if(void 0===s){const s=null!==(n=i.originalKey)&&void 0!==n?n:i.key,r=null!=e?{kind:"method",placement:"prototype",key:s,descriptor:e(i.key)}:{...i,key:s};return null!=t&&(r.finisher=function(e){t(e,s)}),r}{const n=i.constructor;void 0!==e&&Object.defineProperty(i,s,e(s)),null==t||t(n,s)}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function Ev(t,e){return Av({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Sv;null===(Sv=window.HTMLSlotElement)||void 0===Sv||Sv.prototype.assignedElements; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Cv=1,Tv=3,Iv=4,Ov=t=>(...e)=>({_$litDirective$:t,values:e});class kv{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}} -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Rv=Ov(class extends kv{constructor(t){var e;if(super(t),t.type!==Cv||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter(e=>t[e]).join(" ")+" "}update(t,[e]){var i,s;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter(t=>""!==t)));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const n=t.element.classList;this.it.forEach(t=>{t in e||(n.remove(t),this.it.delete(t))});for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(s=this.nt)||void 0===s?void 0:s.has(t))||(i?(n.add(t),this.it.add(t)):(n.remove(t),this.it.delete(t)))}return tv}}),Pv=t=>null!=t?t:ev,Lv={},Mv=Ov(class extends kv{constructor(t){if(super(t),t.type!==Tv&&t.type!==Cv&&t.type!==Iv)throw Error("The `live` directive is not allowed on child or event bindings");if(!(t=>void 0===t.strings)(t))throw Error("`live` bindings can only contain a single expression")}render(t){return t}update(t,[e]){if(e===tv||e===ev)return e;const i=t.element,s=t.name;if(t.type===Tv){if(e===i[s])return tv}else if(t.type===Iv){if(!!e===i.hasAttribute(s))return tv}else if(t.type===Cv&&i.getAttribute(s)===e+"")return tv;return((t,e=Lv)=>{t._$AH=e; -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */})(t),e}}),Nv=["touchstart","touchmove","scroll","mousewheel"],Dv=(t={})=>{const e={};for(const i in t)e[i]=t[i];return Object.assign({badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:!0,valueMissing:!1},e)}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */class Hv extends rs{constructor(){super(...arguments),this.mdcFoundationClass=gg,this.value="",this.type="text",this.placeholder="",this.label="",this.icon="",this.iconTrailing="",this.disabled=!1,this.required=!1,this.minLength=-1,this.maxLength=-1,this.outlined=!1,this.helper="",this.validateOnInitialRender=!1,this.validationMessage="",this.autoValidate=!1,this.pattern="",this.min="",this.max="",this.step=null,this.size=null,this.helperPersistent=!1,this.charCounter=!1,this.endAligned=!1,this.prefix="",this.suffix="",this.name="",this.readOnly=!1,this.autocapitalize="",this.outlineOpen=!1,this.outlineWidth=0,this.isUiValid=!0,this.focused=!1,this._validity=Dv(),this.validityTransform=null}get validity(){return this._checkValidity(this.value),this._validity}get willValidate(){return this.formElement.willValidate}get selectionStart(){return this.formElement.selectionStart}get selectionEnd(){return this.formElement.selectionEnd}focus(){const t=new CustomEvent("focus");this.formElement.dispatchEvent(t),this.formElement.focus()}blur(){const t=new CustomEvent("blur");this.formElement.dispatchEvent(t),this.formElement.blur()}select(){this.formElement.select()}setSelectionRange(t,e,i){this.formElement.setSelectionRange(t,e,i)}update(t){t.has("autoValidate")&&this.mdcFoundation&&this.mdcFoundation.setValidateOnValueChange(this.autoValidate),t.has("value")&&"string"!=typeof this.value&&(this.value=`${this.value}`),super.update(t)}setFormData(t){this.name&&t.append(this.name,this.value)}render(){const t=this.charCounter&&-1!==this.maxLength,e=!!this.helper||!!this.validationMessage||t,i={"mdc-text-field--disabled":this.disabled,"mdc-text-field--no-label":!this.label,"mdc-text-field--filled":!this.outlined,"mdc-text-field--outlined":this.outlined,"mdc-text-field--with-leading-icon":this.icon,"mdc-text-field--with-trailing-icon":this.iconTrailing,"mdc-text-field--end-aligned":this.endAligned};return Qg` - - ${this.renderHelperText(e,t)} - `}updated(t){t.has("value")&&void 0!==t.get("value")&&(this.mdcFoundation.setValue(this.value),this.autoValidate&&this.reportValidity())}renderRipple(){return this.outlined?"":Qg` - - `}renderOutline(){return this.outlined?Qg` - - ${this.renderLabel()} - `:""}renderLabel(){return this.label?Qg` - ${this.label} - `:""}renderLeadingIcon(){return this.icon?this.renderIcon(this.icon):""}renderTrailingIcon(){return this.iconTrailing?this.renderIcon(this.iconTrailing,!0):""}renderIcon(t,e=!1){return Qg`${t}`}renderPrefix(){return this.prefix?this.renderAffix(this.prefix):""}renderSuffix(){return this.suffix?this.renderAffix(this.suffix,!0):""}renderAffix(t,e=!1){return Qg` - ${t}`}renderInput(t){const e=-1===this.minLength?void 0:this.minLength,i=-1===this.maxLength?void 0:this.maxLength,s=this.autocapitalize?this.autocapitalize:void 0,n=this.validationMessage&&!this.isUiValid,r=this.label?"label":void 0,o=t?"helper-text":void 0,a=this.focused||this.helperPersistent||n?"helper-text":void 0;return Qg` - `}renderLineRipple(){return this.outlined?"":Qg` - - `}renderHelperText(t,e){const i=this.validationMessage&&!this.isUiValid,s={"mdc-text-field-helper-text--persistent":this.helperPersistent,"mdc-text-field-helper-text--validation-msg":i},n=this.focused||this.helperPersistent||i?void 0:"true",r=i?this.validationMessage:this.helper;return t?Qg` -
-
${r}
- ${this.renderCharCounter(e)} -
`:""}renderCharCounter(t){const e=Math.min(this.value.length,this.maxLength);return t?Qg` - ${e} / ${this.maxLength}`:""}onInputFocus(){this.focused=!0}onInputBlur(){this.focused=!1,this.reportValidity()}checkValidity(){const t=this._checkValidity(this.value);if(!t){const t=new Event("invalid",{bubbles:!1,cancelable:!0});this.dispatchEvent(t)}return t}reportValidity(){const t=this.checkValidity();return this.mdcFoundation.setValid(t),this.isUiValid=t,t}_checkValidity(t){const e=this.formElement.validity;let i=Dv(e);if(this.validityTransform){const e=this.validityTransform(t,i);i=Object.assign(Object.assign({},i),e),this.mdcFoundation.setUseNativeValidation(!1)}else this.mdcFoundation.setUseNativeValidation(!0);return this._validity=i,this._validity.valid}setCustomValidity(t){this.validationMessage=t,this.formElement.setCustomValidity(t)}handleInputChange(){this.value=this.formElement.value}createAdapter(){return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},this.getRootAdapterMethods()),this.getInputAdapterMethods()),this.getLabelAdapterMethods()),this.getLineRippleAdapterMethods()),this.getOutlineAdapterMethods())}getRootAdapterMethods(){return Object.assign({registerTextFieldInteractionHandler:(t,e)=>this.addEventListener(t,e),deregisterTextFieldInteractionHandler:(t,e)=>this.removeEventListener(t,e),registerValidationAttributeChangeHandler:t=>{const e=new MutationObserver(e=>{t((t=>t.map(t=>t.attributeName).filter(t=>t))(e))});return e.observe(this.formElement,{attributes:!0}),e},deregisterValidationAttributeChangeHandler:t=>t.disconnect()},Xi(this.mdcRoot))}getInputAdapterMethods(){return{getNativeInput:()=>this.formElement,setInputAttr:()=>{},removeInputAttr:()=>{},isFocused:()=>!!this.shadowRoot&&this.shadowRoot.activeElement===this.formElement,registerInputInteractionHandler:(t,e)=>this.formElement.addEventListener(t,e,{passive:t in Nv}),deregisterInputInteractionHandler:(t,e)=>this.formElement.removeEventListener(t,e)}}getLabelAdapterMethods(){return{floatLabel:t=>this.labelElement&&this.labelElement.floatingLabelFoundation.float(t),getLabelWidth:()=>this.labelElement?this.labelElement.floatingLabelFoundation.getWidth():0,hasLabel:()=>Boolean(this.labelElement),shakeLabel:t=>this.labelElement&&this.labelElement.floatingLabelFoundation.shake(t),setLabelRequired:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.setRequired(t)}}}getLineRippleAdapterMethods(){return{activateLineRipple:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.activate()},deactivateLineRipple:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.deactivate()},setLineRippleTransformOrigin:t=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.setRippleCenter(t)}}}async getUpdateComplete(){var t;const e=await super.getUpdateComplete();return await(null===(t=this.outlineElement)||void 0===t?void 0:t.updateComplete),e}firstUpdated(){var t;super.firstUpdated(),this.mdcFoundation.setValidateOnValueChange(this.autoValidate),this.validateOnInitialRender&&this.reportValidity(),null===(t=this.outlineElement)||void 0===t||t.updateComplete.then(()=>{var t;this.outlineWidth=(null===(t=this.labelElement)||void 0===t?void 0:t.floatingLabelFoundation.getWidth())||0})}getOutlineAdapterMethods(){return{closeOutline:()=>this.outlineElement&&(this.outlineOpen=!1),hasOutline:()=>Boolean(this.outlineElement),notchOutline:t=>{this.outlineElement&&!this.outlineOpen&&(this.outlineWidth=t,this.outlineOpen=!0)}}}async layout(){await this.updateComplete;const t=this.labelElement;if(!t)return void(this.outlineOpen=!1);const e=!!this.label&&!!this.value;if(t.floatingLabelFoundation.float(e),!this.outlined)return;this.outlineOpen=e,await this.updateComplete;const i=t.floatingLabelFoundation.getWidth();this.outlineOpen&&(this.outlineWidth=i,await this.updateComplete)}}n([Ev(".mdc-text-field")],Hv.prototype,"mdcRoot",void 0),n([Ev("input")],Hv.prototype,"formElement",void 0),n([Ev(".mdc-floating-label")],Hv.prototype,"labelElement",void 0),n([Ev(".mdc-line-ripple")],Hv.prototype,"lineRippleElement",void 0),n([Ev("mwc-notched-outline")],Hv.prototype,"outlineElement",void 0),n([Ev(".mdc-notched-outline__notch")],Hv.prototype,"notchElement",void 0),n([$v({type:String})],Hv.prototype,"value",void 0),n([$v({type:String})],Hv.prototype,"type",void 0),n([$v({type:String})],Hv.prototype,"placeholder",void 0),n([$v({type:String}),os(function(t,e){void 0!==e&&this.label!==e&&this.layout()})],Hv.prototype,"label",void 0),n([$v({type:String})],Hv.prototype,"icon",void 0),n([$v({type:String})],Hv.prototype,"iconTrailing",void 0),n([$v({type:Boolean,reflect:!0})],Hv.prototype,"disabled",void 0),n([$v({type:Boolean})],Hv.prototype,"required",void 0),n([$v({type:Number})],Hv.prototype,"minLength",void 0),n([$v({type:Number})],Hv.prototype,"maxLength",void 0),n([$v({type:Boolean,reflect:!0}),os(function(t,e){void 0!==e&&this.outlined!==e&&this.layout()})],Hv.prototype,"outlined",void 0),n([$v({type:String})],Hv.prototype,"helper",void 0),n([$v({type:Boolean})],Hv.prototype,"validateOnInitialRender",void 0),n([$v({type:String})],Hv.prototype,"validationMessage",void 0),n([$v({type:Boolean})],Hv.prototype,"autoValidate",void 0),n([$v({type:String})],Hv.prototype,"pattern",void 0),n([$v({type:String})],Hv.prototype,"min",void 0),n([$v({type:String})],Hv.prototype,"max",void 0),n([$v({type:String})],Hv.prototype,"step",void 0),n([$v({type:Number})],Hv.prototype,"size",void 0),n([$v({type:Boolean})],Hv.prototype,"helperPersistent",void 0),n([$v({type:Boolean})],Hv.prototype,"charCounter",void 0),n([$v({type:Boolean})],Hv.prototype,"endAligned",void 0),n([$v({type:String})],Hv.prototype,"prefix",void 0),n([$v({type:String})],Hv.prototype,"suffix",void 0),n([$v({type:String})],Hv.prototype,"name",void 0),n([$v({type:String})],Hv.prototype,"inputMode",void 0),n([$v({type:Boolean})],Hv.prototype,"readOnly",void 0),n([$v({type:String})],Hv.prototype,"autocapitalize",void 0),n([wv()],Hv.prototype,"outlineOpen",void 0),n([wv()],Hv.prototype,"outlineWidth",void 0),n([wv()],Hv.prototype,"isUiValid",void 0),n([wv()],Hv.prototype,"focused",void 0),n([function(t){return Av({finisher:(e,i)=>{Object.assign(e.prototype[i],t)}})}({passive:!0})],Hv.prototype,"handleInputChange",null); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const Uv=((t,...e)=>{const i=1===t.length?t[0]:e.reduce((e,i,s)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[s+1],t[0]);return new xg(i,t,bg)})`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-text-field--filled{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-text-field--filled .mdc-text-field__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-text-field--filled .mdc-text-field__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-text-field--filled.mdc-ripple-upgraded--unbounded .mdc-text-field__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-activation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-deactivation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-text-field__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-text-field{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0;display:inline-flex;align-items:baseline;padding:0 16px;position:relative;box-sizing:border-box;overflow:hidden;will-change:opacity,transform,color}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input{color:rgba(0, 0, 0, 0.87)}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.54)}}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.54)}}.mdc-text-field .mdc-text-field__input{caret-color:#6200ee;caret-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field-character-counter,.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-text-field__input{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);width:100%;min-width:0;border:none;border-radius:0;background:none;appearance:none;padding:0}.mdc-text-field__input::-ms-clear{display:none}.mdc-text-field__input::-webkit-calendar-picker-indicator{display:none}.mdc-text-field__input:focus{outline:none}.mdc-text-field__input:invalid{box-shadow:none}@media all{.mdc-text-field__input::placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field__input:-ms-input-placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field--no-label .mdc-text-field__input::placeholder,.mdc-text-field--focused .mdc-text-field__input::placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}@media all{.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder,.mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}.mdc-text-field__affix{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0;white-space:nowrap}.mdc-text-field--label-floating .mdc-text-field__affix,.mdc-text-field--no-label .mdc-text-field__affix{opacity:1}@supports(-webkit-hyphens: none){.mdc-text-field--outlined .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field__affix--prefix,.mdc-text-field__affix--prefix[dir=rtl]{padding-left:2px;padding-right:0}.mdc-text-field--end-aligned .mdc-text-field__affix--prefix{padding-left:0;padding-right:12px}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--end-aligned .mdc-text-field__affix--prefix[dir=rtl]{padding-left:12px;padding-right:0}.mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field__affix--suffix,.mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:12px}.mdc-text-field--end-aligned .mdc-text-field__affix--suffix{padding-left:2px;padding-right:0}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--end-aligned .mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:2px}.mdc-text-field--filled{height:56px}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-text-field--filled:hover .mdc-text-field__ripple::before,.mdc-text-field--filled.mdc-ripple-surface--hover .mdc-text-field__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-text-field--filled.mdc-ripple-upgraded--background-focused .mdc-text-field__ripple::before,.mdc-text-field--filled:not(.mdc-ripple-upgraded):focus .mdc-text-field__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-text-field--filled::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-text-field--filled:not(.mdc-text-field--disabled){background-color:whitesmoke}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-text-field--filled:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--filled .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-text-field--filled .mdc-floating-label,.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{height:100%}.mdc-text-field--filled.mdc-text-field--no-label .mdc-floating-label{display:none}.mdc-text-field--filled.mdc-text-field--no-label::before{display:none}@supports(-webkit-hyphens: none){.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field--outlined{height:56px;overflow:visible}.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--outlined .mdc-text-field__input{height:100%}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-text-field--outlined{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined{padding-right:max(16px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-right:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-left:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-right:max(16px, var(--mdc-shape-small, 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-right:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-right:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-text-field--outlined .mdc-text-field__ripple::before,.mdc-text-field--outlined .mdc-text-field__ripple::after{background-color:transparent;background-color:var(--mdc-ripple-color, transparent)}.mdc-text-field--outlined .mdc-floating-label{left:4px;right:initial}[dir=rtl] .mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-text-field--outlined .mdc-text-field__input{display:flex;border:none !important;background-color:transparent}.mdc-text-field--outlined .mdc-notched-outline{z-index:1}.mdc-text-field--textarea{flex-direction:column;align-items:center;width:auto;height:auto;padding:0;transition:none}.mdc-text-field--textarea .mdc-floating-label{top:19px}.mdc-text-field--textarea .mdc-floating-label:not(.mdc-floating-label--float-above){transform:none}.mdc-text-field--textarea .mdc-text-field__input{flex-grow:1;height:auto;min-height:1.5rem;overflow-x:hidden;overflow-y:auto;box-sizing:border-box;resize:none;padding:0 16px;line-height:1.5rem}.mdc-text-field--textarea.mdc-text-field--filled::before{display:none}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-10.25px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-filled 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-filled{0%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-10.25px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-10.25px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--filled .mdc-text-field__input{margin-top:23px;margin-bottom:9px}.mdc-text-field--textarea.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-27.25px) scale(1)}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-24.75px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-24.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-24.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label{top:18px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field__input{margin-bottom:2px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter{align-self:flex-end;padding:0 16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::after{display:inline-block;width:0;height:16px;content:"";vertical-align:-16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::before{display:none}.mdc-text-field__resizer{align-self:stretch;display:inline-flex;flex-direction:column;flex-grow:1;max-height:100%;max-width:100%;min-height:56px;min-width:fit-content;min-width:-moz-available;min-width:-webkit-fill-available;overflow:hidden;resize:both}.mdc-text-field--filled .mdc-text-field__resizer{transform:translateY(-1px)}.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateY(1px)}.mdc-text-field--outlined .mdc-text-field__resizer{transform:translateX(-1px) translateY(-1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer,.mdc-text-field--outlined .mdc-text-field__resizer[dir=rtl]{transform:translateX(1px) translateY(-1px)}.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateX(1px) translateY(1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input[dir=rtl],.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter[dir=rtl]{transform:translateX(-1px) translateY(1px)}.mdc-text-field--with-leading-icon{padding-left:0;padding-right:16px}[dir=rtl] .mdc-text-field--with-leading-icon,.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:16px;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 48px);left:48px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-text-field--with-leading-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake,.mdc-text-field--with-leading-icon.mdc-text-field--outlined[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--with-trailing-icon{padding-left:16px;padding-right:0}[dir=rtl] .mdc-text-field--with-trailing-icon,.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-trailing-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-text-field-helper-line{display:flex;justify-content:space-between;box-sizing:border-box}.mdc-text-field+.mdc-text-field-helper-line{padding-right:16px;padding-left:16px}.mdc-form-field>.mdc-text-field+label{align-self:flex-start}.mdc-text-field--focused:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--focused .mdc-notched-outline__trailing{border-width:2px}.mdc-text-field--focused+.mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg){opacity:1}.mdc-text-field--focused.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-text-field--focused.mdc-text-field--outlined.mdc-text-field--textarea .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid .mdc-text-field__input{caret-color:#b00020;caret-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{opacity:1}.mdc-text-field--disabled{pointer-events:none}.mdc-text-field--disabled .mdc-text-field__input{color:rgba(0, 0, 0, 0.38)}@media all{.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.38)}}@media all{.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.38)}}.mdc-text-field--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-floating-label{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--leading{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:GrayText}}@media screen and (forced-colors: active){.mdc-text-field--disabled .mdc-text-field__input{background-color:Window}.mdc-text-field--disabled .mdc-floating-label{z-index:1}}.mdc-text-field--disabled .mdc-floating-label{cursor:default}.mdc-text-field--disabled.mdc-text-field--filled{background-color:#fafafa}.mdc-text-field--disabled.mdc-text-field--filled .mdc-text-field__ripple{display:none}.mdc-text-field--disabled .mdc-text-field__input{pointer-events:auto}.mdc-text-field--end-aligned .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--end-aligned .mdc-text-field__input[dir=rtl]{text-align:left}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix{direction:ltr}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--leading,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--leading{order:1}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{order:2}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input{order:3}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{order:4}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--trailing,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--trailing{order:5}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--prefix{padding-right:12px}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--suffix{padding-left:2px}.mdc-text-field-helper-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin:0;opacity:0;will-change:opacity;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-text-field-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-text-field-helper-text--persistent{transition:none;opacity:1;will-change:initial}.mdc-text-field-character-counter{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin-left:auto;margin-right:0;padding-left:16px;padding-right:0;white-space:nowrap}.mdc-text-field-character-counter::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{margin-left:0;margin-right:auto}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field__icon{align-self:center;cursor:pointer}.mdc-text-field__icon:not([tabindex]),.mdc-text-field__icon[tabindex="-1"]{cursor:default;pointer-events:none}.mdc-text-field__icon svg{display:block}.mdc-text-field__icon--leading{margin-left:16px;margin-right:8px}[dir=rtl] .mdc-text-field__icon--leading,.mdc-text-field__icon--leading[dir=rtl]{margin-left:8px;margin-right:16px}.mdc-text-field__icon--trailing{padding:12px;margin-left:0px;margin-right:0px}[dir=rtl] .mdc-text-field__icon--trailing,.mdc-text-field__icon--trailing[dir=rtl]{margin-left:0px;margin-right:0px}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-flex;flex-direction:column;outline:none}.mdc-text-field{width:100%}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42))}.mdc-text-field:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-text-field-hover-line-color, rgba(0, 0, 0, 0.87))}.mdc-text-field.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06);border-bottom-color:var(--mdc-text-field-disabled-line-color, rgba(0, 0, 0, 0.06))}.mdc-text-field.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field__input{direction:inherit}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-idle-border-color, rgba(0, 0, 0, 0.38) )}:host(:not([disabled]):hover) :not(.mdc-text-field--invalid):not(.mdc-text-field--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-error-color, var(--mdc-theme-error, #b00020) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-character-counter,:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid .mdc-text-field__icon{color:var(--mdc-text-field-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input{color:var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg),:host(:not([disabled])) .mdc-text-field-helper-line:not(.mdc-text-field--invalid) .mdc-text-field-character-counter{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-text-field.mdc-text-field--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field .mdc-text-field__input,:host([disabled]) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-helper-text,:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-character-counter{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}`,zv={"mwc-textfield":class extends Hv{static get styles(){return Uv}},"mwc-notched-outline":class extends Lm{static get styles(){return Fm}}};let Fv=class extends( -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function(t){return class extends t{createRenderRoot(){const t=this.constructor,{registry:e,elementDefinitions:i,shadowRootOptions:s}=t;i&&!e&&(t.registry=new CustomElementRegistry,Object.entries(i).forEach(([e,i])=>t.registry.define(e,i)));const n=this.renderOptions.creationScope=this.attachShadow({...s,customElements:t.registry});return p(n,this.constructor.elementStyles),n}}}(ht)){constructor(){super(...arguments),this._initialized=!1}setConfig(t){this._config=t,this.loadCardHelpers()}shouldUpdate(){return this._initialized||this._initialize(),!0}get _name(){var t;return(null===(t=this._config)||void 0===t?void 0:t.name)||""}get _hub(){var t;return(null===(t=this._config)||void 0===t?void 0:t.hub)||""}get _selected_schedule(){var t;return(null===(t=this._config)||void 0===t?void 0:t.selected_schedule)||""}get _theme_colors(){var t;return(null===(t=this._config)||void 0===t?void 0:t.theme_colors)||!1}get _show_badges(){var t;return(null===(t=this._config)||void 0===t?void 0:t.show_badges)||!1}get _show_schedule_id(){var t;return(null===(t=this._config)||void 0===t?void 0:t.show_schedule_id)||!1}get _display_only(){var t;return(null===(t=this._config)||void 0===t?void 0:t.display_only)||!1}get _hide_hw_schedule(){var t;return(null===(t=this._config)||void 0===t?void 0:t.hide_hw_schedule)||!1}get _admin_only(){var t;return(null===(t=this._config)||void 0===t?void 0:t.admin_only)||!1}get _view_type(){var t,e;return(null===(t=this._config)||void 0===t?void 0:t.view_type)?null===(e=this._config)||void 0===e?void 0:e.view_type:"default"}get _hide_card_borders(){var t;return(null===(t=this._config)||void 0===t?void 0:t.hide_card_borders)||!1}async loadData(){var t;this.hass&&(this._hubs=await(t=this.hass,t.callWS({type:"wiser/hubs"})),this._schedules=await qt(this.hass,this._hub?this._hub:this._hubs[0]))}render(){return this.hass&&this._helpers&&this._config&&this._hubs&&this._schedules?X` - - ${this.hubSelector()} - t.stopPropagation()} - > - - ${this._schedules.map(t=>X`${t.Name}`)} - - t.stopPropagation()} - > - ${["default","list"].map(t=>{return X`${e=t,e.charAt(0).toUpperCase()+e.slice(1)}`;var e})} - - - - - - - -
- - - - - - - - - -
-

Default View Options

- - - - - - -
-
Version: ${$t}
- `:X``}hubSelector(){var t;const e=this._hubs?this._hubs:[];return e.length>1?X` - t.stopPropagation()} - > - ${null===(t=this._hubs)||void 0===t?void 0:t.map(t=>X`${t}`)} - - `:X``}_initialize(){void 0!==this.hass&&void 0!==this._config&&void 0!==this._helpers&&(this._initialized=!0)}async loadCardHelpers(){this._helpers=await window.loadCardHelpers(),await this.loadData()}_valueChanged(t){if(!this._config||!this.hass)return;const e=t.target;if(this[`_${e.configValue}`]!==e.value){if(e.configValue)if(""===e.value){const t=Object.assign({},this._config);delete t[e.configValue],this._config=t}else this._config=Object.assign(Object.assign({},this._config),{[e.configValue]:void 0!==e.checked?e.checked:e.value});"hub"===e.configValue&&(this._config.selected_schedule=""),yt(this,"config-changed",{config:this._config})}}};return Fv.elementDefinitions=Object.assign(Object.assign(Object.assign(Object.assign({},zv),Bm),ag),$n),Fv.styles=h` - mwc-select, - mwc-textfield { - margin-bottom: 16px; - display: block; - } - mwc-formfield { - padding-bottom: 20px; - display: flex; - } - mwc-switch { - --mdc-theme-secondary: var(--switch-checked-color); - } - `,n([gt({attribute:!1})],Fv.prototype,"hass",void 0),n([vt()],Fv.prototype,"_config",void 0),n([vt()],Fv.prototype,"_helpers",void 0),n([vt()],Fv.prototype,"_hubs",void 0),n([vt()],Fv.prototype,"_schedules",void 0),Fv=n([ut("wiser-schedule-card-editor")],Fv),console.info(`%c WISER-SCHEDULE-CARD \n%c ${Vt("common.version")} ${$t} `,"color: orange; font-weight: bold; background: black","color: white; font-weight: bold; background: dimgray"),window.customCards=window.customCards||[],window.customCards.push({type:"wiser-schedule-card",name:"Wiser Schedule Card",description:"A card to manage Wiser schedules",preview:!1}),t.WiserScheduleCard=class extends ht{constructor(){super(),this._view=Et.Overview,this.translationsLoaded=!0,this.component_loaded=!1,this._schedule_id=0,this._schedule_type="heating",this.initialise()}static async getConfigElement(){return document.createElement("wiser-schedule-card-editor")}static getStubConfig(){return{}}setConfig(t){if(!t)throw new Error(Vt("common.invalid_configuration"));t.test_gui&&function(){var t=document.querySelector("home-assistant");if(t=(t=(t=(t=(t=(t=(t=(t=t&&t.shadowRoot)&&t.querySelector("home-assistant-main"))&&t.shadowRoot)&&t.querySelector("app-drawer-layout partial-panel-resolver"))&&t.shadowRoot||t)&&t.querySelector("ha-panel-lovelace"))&&t.shadowRoot)&&t.querySelector("hui-root")){var e=t.lovelace;return e.current_view=t.___curView,e}return null}().setEditMode(!0),this.config=Object.assign({name:"Wiser Schedule"},t)}set hass(t){this._hass=t}async initialise(){return await this.isComponentLoaded()&&(this.component_loaded=!0),this.processConfigSchedule(),!0}async isComponentLoaded(){for(;!this._hass||!this._hass.config.components.includes("wiser");)await new Promise(t=>setTimeout(t,100));return!0}processConfigSchedule(){this.config.selected_schedule?(this._schedule_type=this.config.selected_schedule.split("|")[0],this._schedule_id=parseInt(this.config.selected_schedule.split("|")[1]),this._view=Et.ScheduleEdit):(this._schedule_type="",this._schedule_id=0,this._view=Et.Overview)}getCardSize(){return 9}shouldUpdate(t){return!(!this.config||!this.component_loaded)&&(t.has("config")&&this.processConfigSchedule(),!!t.has("component_loaded")||(!!t.has("_view")||(!!t.has("_schedule_list")||xt(this,t,!1))))}async _handleEvent(t){if("wiser_update_received"===t.event_type){const t=new CustomEvent("wiser-update",{});this.dispatchEvent(t)}}render(){if(!this._hass||!this.config||!this.translationsLoaded)return X``;const t=this.config.hide_card_borders?"border-width: 0px":"";return this._view==Et.Overview?X` - -
- -
-
- `:this._view==Et.ScheduleEdit&&this._schedule_id?X` - -
- -
-
- `:this._view==Et.ScheduleAdd?X` - -
- -
-
- `:this._view==Et.ScheduleCopy?X` - -
- -
-
- `:this._view==Et.ScheduleRename?X` - -
- -
-
- `:X``}_scheduleClick(t){this._schedule_type=t.detail.schedule_type,this._schedule_id=t.detail.schedule_id,this._view=Et.ScheduleEdit}_addScheduleClick(){this._view=Et.ScheduleAdd}_renameClick(){this._view=Et.ScheduleRename}_editClick(){this._view=Et.ScheduleEdit}_copyClick(){this._view=Et.ScheduleCopy}_backClick(t){t.detail?this._view=t.detail:this._view=Et.Overview}_scheduleDeleted(){this._schedule_id=0,this._schedule_type="",this._view=Et.Overview}_scheduleAdded(){this._view=Et.Overview}_scheduleCopied(){this._view=Et.ScheduleEdit}_scheduleRenamed(){this._view=Et.ScheduleEdit}},n([gt({attribute:!1})],t.WiserScheduleCard.prototype,"_hass",void 0),n([vt()],t.WiserScheduleCard.prototype,"config",void 0),n([vt()],t.WiserScheduleCard.prototype,"_view",void 0),n([vt()],t.WiserScheduleCard.prototype,"translationsLoaded",void 0),n([vt()],t.WiserScheduleCard.prototype,"component_loaded",void 0),n([vt()],t.WiserScheduleCard.prototype,"_schedule_id",void 0),n([vt()],t.WiserScheduleCard.prototype,"_schedule_type",void 0),t.WiserScheduleCard=n([ut("wiser-schedule-card")],t.WiserScheduleCard),t}({}); diff --git a/custom_components/wiser/frontend/wiser-zigbee-card.js b/custom_components/wiser/frontend/wiser-zigbee-card.js deleted file mode 100644 index 6d299dca..00000000 --- a/custom_components/wiser/frontend/wiser-zigbee-card.js +++ /dev/null @@ -1,1102 +0,0 @@ -var wiserzigbeecard=function(t){"use strict";var e=function(t,i){return e=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(t,e){t.__proto__=e}||function(t,e){for(var i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=e[i])},e(t,i)};function i(t,i){if("function"!=typeof i&&null!==i)throw new TypeError("Class extends value "+String(i)+" is not a constructor or null");function n(){this.constructor=t}e(t,i),t.prototype=null===i?Object.create(i):(n.prototype=i.prototype,new n)}var n=function(){return n=Object.assign||function(t){for(var e,i=1,n=arguments.length;i=0;a--)(o=t[a])&&(s=(r<3?o(s):r>3?o(e,i,s):o(e,i))||s);return r>3&&s&&Object.defineProperty(e,i,s),s}function r(t){var e="function"==typeof Symbol&&Symbol.iterator,i=e&&t[e],n=0;if(i)return i.call(t);if(t&&"number"==typeof t.length)return{next:function(){return t&&n>=t.length&&(t=void 0),{value:t&&t[n++],done:!t}}};throw new TypeError(e?"Object is not iterable.":"Symbol.iterator is not defined.")}"function"==typeof SuppressedError&&SuppressedError; -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const s=window,a=s.ShadowRoot&&(void 0===s.ShadyCSS||s.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,d=Symbol(),l=new WeakMap;let c=class{constructor(t,e,i){if(this._$cssResult$=!0,i!==d)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e}get styleSheet(){let t=this.o;const e=this.t;if(a&&void 0===t){const i=void 0!==e&&1===e.length;i&&(t=l.get(e)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),i&&l.set(e,t))}return t}toString(){return this.cssText}};const h=a?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new c("string"==typeof t?t:t+"",void 0,d))(e)})(t):t -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */;var u;const p=window,f=p.trustedTypes,m=f?f.emptyScript:"",v=p.reactiveElementPolyfillSupport,g={toAttribute(t,e){switch(e){case Boolean:t=t?m:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},y=(t,e)=>e!==t&&(e==e||t==t),b={attribute:!0,type:String,converter:g,reflect:!1,hasChanged:y},x="finalized";let _=class extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const n=this._$Ep(i,e);void 0!==n&&(this._$Ev.set(n,i),t.push(n))})),t}static createProperty(t,e=b){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,n=this.getPropertyDescriptor(t,i,e);void 0!==n&&Object.defineProperty(this.prototype,t,n)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(n){const o=this[t];this[e]=n,this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||b}static finalize(){if(this.hasOwnProperty(x))return!1;this[x]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(h(t))}else void 0!==t&&e.push(h(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{a?t.adoptedStyleSheets=e.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):e.forEach((e=>{const i=document.createElement("style"),n=s.litNonce;void 0!==n&&i.setAttribute("nonce",n),i.textContent=e.cssText,t.appendChild(i)}))})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=b){var n;const o=this.constructor._$Ep(t,i);if(void 0!==o&&!0===i.reflect){const r=(void 0!==(null===(n=i.converter)||void 0===n?void 0:n.toAttribute)?i.converter:g).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(o):this.setAttribute(o,r),this._$El=null}}_$AK(t,e){var i;const n=this.constructor,o=n._$Ev.get(t);if(void 0!==o&&this._$El!==o){const t=n.getPropertyOptions(o),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:g;this._$El=o,this[o]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let n=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||y)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):n=!1),!this.isUpdatePending&&n&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}}; -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var w;_[x]=!0,_.elementProperties=new Map,_.elementStyles=[],_.shadowRootOptions={mode:"open"},null==v||v({ReactiveElement:_}),(null!==(u=p.reactiveElementVersions)&&void 0!==u?u:p.reactiveElementVersions=[]).push("1.6.3");const E=window,k=E.trustedTypes,O=k?k.createPolicy("lit-html",{createHTML:t=>t}):void 0,C="$lit$",S=`lit$${(Math.random()+"").slice(9)}$`,T="?"+S,I=`<${T}>`,A=document,R=()=>A.createComment(""),P=t=>null===t||"object"!=typeof t&&"function"!=typeof t,D=Array.isArray,M="[ \t\n\f\r]",F=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,N=/-->/g,B=/>/g,L=RegExp(`>|${M}(?:([^\\s"'>=/]+)(${M}*=${M}*(?:[^ \t\n\f\r"'\`<>=]|("|')|))|$)`,"g"),z=/'/g,j=/"/g,H=/^(?:script|style|textarea|title)$/i,$=(t=>(e,...i)=>({_$litType$:t,strings:e,values:i}))(1),U=Symbol.for("lit-noChange"),W=Symbol.for("lit-nothing"),V=new WeakMap,q=A.createTreeWalker(A,129,null,!1);function X(t,e){if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return void 0!==O?O.createHTML(e):e}const Y=(t,e)=>{const i=t.length-1,n=[];let o,r=2===e?"":"",s=F;for(let e=0;e"===d[0]?(s=null!=o?o:F,l=-1):void 0===d[1]?l=-2:(l=s.lastIndex-d[2].length,a=d[1],s=void 0===d[3]?L:'"'===d[3]?j:z):s===j||s===z?s=L:s===N||s===B?s=F:(s=L,o=void 0);const h=s===L&&t[e+1].startsWith("/>")?" ":"";r+=s===F?i+I:l>=0?(n.push(a),i.slice(0,l)+C+i.slice(l)+S+h):i+S+(-2===l?(n.push(void 0),e):h)}return[X(t,r+(t[i]||"")+(2===e?"":"")),n]};class G{constructor({strings:t,_$litType$:e},i){let n;this.parts=[];let o=0,r=0;const s=t.length-1,a=this.parts,[d,l]=Y(t,e);if(this.el=G.createElement(d,i),q.currentNode=this.el.content,2===e){const t=this.el.content,e=t.firstChild;e.remove(),t.append(...e.childNodes)}for(;null!==(n=q.nextNode())&&a.length0){n.textContent=k?k.emptyScript:"";for(let i=0;iD(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]))(t)?this.T(t):this._(t)}k(t){return this._$AA.parentNode.insertBefore(t,this._$AB)}$(t){this._$AH!==t&&(this._$AR(),this._$AH=this.k(t))}_(t){this._$AH!==W&&P(this._$AH)?this._$AA.nextSibling.data=t:this.$(A.createTextNode(t)),this._$AH=t}g(t){var e;const{values:i,_$litType$:n}=t,o="number"==typeof n?this._$AC(t):(void 0===n.el&&(n.el=G.createElement(X(n.h,n.h[0]),this.options)),n);if((null===(e=this._$AH)||void 0===e?void 0:e._$AD)===o)this._$AH.v(i);else{const t=new Z(o,this),e=t.u(this.options);t.v(i),this.$(e),this._$AH=t}}_$AC(t){let e=V.get(t.strings);return void 0===e&&V.set(t.strings,e=new G(t)),e}T(t){D(this._$AH)||(this._$AH=[],this._$AR());const e=this._$AH;let i,n=0;for(const o of t)n===e.length?e.push(i=new Q(this.k(R()),this.k(R()),this,this.options)):i=e[n],i._$AI(o),n++;n2||""!==i[0]||""!==i[1]?(this._$AH=Array(i.length-1).fill(new String),this.strings=i):this._$AH=W}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,e=this,i,n){const o=this.strings;let r=!1;if(void 0===o)t=K(this,t,e,0),r=!P(t)||t!==this._$AH&&t!==U,r&&(this._$AH=t);else{const n=t;let s,a;for(t=o[0],s=0;s{const i=1===t.length?t[0]:e.reduce(((e,i,n)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(i)+t[n+1]),t[0]);return new ct(i,t,dt)},ut=at?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const i of t.cssRules)e+=i.cssText;return(t=>new ct("string"==typeof t?t:t+"",void 0,dt))(e)})(t):t -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */;var pt;const ft=window,mt=ft.trustedTypes,vt=mt?mt.emptyScript:"",gt=ft.reactiveElementPolyfillSupport,yt={toAttribute(t,e){switch(e){case Boolean:t=t?vt:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){let i=t;switch(e){case Boolean:i=null!==t;break;case Number:i=null===t?null:Number(t);break;case Object:case Array:try{i=JSON.parse(t)}catch(t){i=null}}return i}},bt=(t,e)=>e!==t&&(e==e||t==t),xt={attribute:!0,type:String,converter:yt,reflect:!1,hasChanged:bt},_t="finalized";class wt extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this._$Eu()}static addInitializer(t){var e;this.finalize(),(null!==(e=this.h)&&void 0!==e?e:this.h=[]).push(t)}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((e,i)=>{const n=this._$Ep(i,e);void 0!==n&&(this._$Ev.set(n,i),t.push(n))})),t}static createProperty(t,e=xt){if(e.state&&(e.attribute=!1),this.finalize(),this.elementProperties.set(t,e),!e.noAccessor&&!this.prototype.hasOwnProperty(t)){const i="symbol"==typeof t?Symbol():"__"+t,n=this.getPropertyDescriptor(t,i,e);void 0!==n&&Object.defineProperty(this.prototype,t,n)}}static getPropertyDescriptor(t,e,i){return{get(){return this[e]},set(n){const o=this[t];this[e]=n,this.requestUpdate(t,o,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this.elementProperties.get(t)||xt}static finalize(){if(this.hasOwnProperty(_t))return!1;this[_t]=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,e=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const i of e)this.createProperty(i,t[i])}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(t){const e=[];if(Array.isArray(t)){const i=new Set(t.flat(1/0).reverse());for(const t of i)e.unshift(ut(t))}else void 0!==t&&e.push(ut(t));return e}static _$Ep(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}_$Eu(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)))}addController(t){var e,i;(null!==(e=this._$ES)&&void 0!==e?e:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(i=t.hostConnected)||void 0===i||i.call(t))}removeController(t){var e;null===(e=this._$ES)||void 0===e||e.splice(this._$ES.indexOf(t)>>>0,1)}_$Eg(){this.constructor.elementProperties.forEach(((t,e)=>{this.hasOwnProperty(e)&&(this._$Ei.set(e,this[e]),delete this[e])}))}createRenderRoot(){var t;const e=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootOptions);return((t,e)=>{at?t.adoptedStyleSheets=e.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):e.forEach((e=>{const i=document.createElement("style"),n=st.litNonce;void 0!==n&&i.setAttribute("nonce",n),i.textContent=e.cssText,t.appendChild(i)}))})(e,this.constructor.elementStyles),e}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostConnected)||void 0===e?void 0:e.call(t)}))}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostDisconnected)||void 0===e?void 0:e.call(t)}))}attributeChangedCallback(t,e,i){this._$AK(t,i)}_$EO(t,e,i=xt){var n;const o=this.constructor._$Ep(t,i);if(void 0!==o&&!0===i.reflect){const r=(void 0!==(null===(n=i.converter)||void 0===n?void 0:n.toAttribute)?i.converter:yt).toAttribute(e,i.type);this._$El=t,null==r?this.removeAttribute(o):this.setAttribute(o,r),this._$El=null}}_$AK(t,e){var i;const n=this.constructor,o=n._$Ev.get(t);if(void 0!==o&&this._$El!==o){const t=n.getPropertyOptions(o),r="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(i=t.converter)||void 0===i?void 0:i.fromAttribute)?t.converter:yt;this._$El=o,this[o]=r.fromAttribute(e,t.type),this._$El=null}}requestUpdate(t,e,i){let n=!0;void 0!==t&&(((i=i||this.constructor.getPropertyOptions(t)).hasChanged||bt)(this[t],e)?(this._$AL.has(t)||this._$AL.set(t,e),!0===i.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,i))):n=!1),!this.isUpdatePending&&n&&(this._$E_=this._$Ej())}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_}catch(t){Promise.reject(t)}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,e)=>this[e]=t)),this._$Ei=void 0);let e=!1;const i=this._$AL;try{e=this.shouldUpdate(i),e?(this.willUpdate(i),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var e;return null===(e=t.hostUpdate)||void 0===e?void 0:e.call(t)})),this.update(i)):this._$Ek()}catch(t){throw e=!1,this._$Ek(),t}e&&this._$AE(i)}willUpdate(t){}_$AE(t){var e;null===(e=this._$ES)||void 0===e||e.forEach((t=>{var e;return null===(e=t.hostUpdated)||void 0===e?void 0:e.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t)}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return!0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,e)=>this._$EO(e,this[e],t))),this._$EC=void 0),this._$Ek()}updated(t){}firstUpdated(t){}} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -var Et,kt;wt[_t]=!0,wt.elementProperties=new Map,wt.elementStyles=[],wt.shadowRootOptions={mode:"open"},null==gt||gt({ReactiveElement:wt}),(null!==(pt=ft.reactiveElementVersions)&&void 0!==pt?pt:ft.reactiveElementVersions=[]).push("1.6.3");let Ot=class extends wt{constructor(){super(...arguments),this.renderOptions={host:this},this._$Do=void 0}createRenderRoot(){var t,e;const i=super.createRenderRoot();return null!==(t=(e=this.renderOptions).renderBefore)&&void 0!==t||(e.renderBefore=i.firstChild),i}update(t){const e=this.render();this.hasUpdated||(this.renderOptions.isConnected=this.isConnected),super.update(t),this._$Do=((t,e,i)=>{var n,o;const r=null!==(n=null==i?void 0:i.renderBefore)&&void 0!==n?n:e;let s=r._$litPart$;if(void 0===s){const t=null!==(o=null==i?void 0:i.renderBefore)&&void 0!==o?o:null;r._$litPart$=s=new Q(e.insertBefore(R(),t),t,void 0,null!=i?i:{})}return s._$AI(t),s})(e,this.renderRoot,this.renderOptions)}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1)}render(){return U}};Ot.finalized=!0,Ot._$litElement$=!0,null===(Et=globalThis.litElementHydrateSupport)||void 0===Et||Et.call(globalThis,{LitElement:Ot});const Ct=globalThis.litElementPolyfillSupport;null==Ct||Ct({LitElement:Ot}),(null!==(kt=globalThis.litElementVersions)&&void 0!==kt?kt:globalThis.litElementVersions=[]).push("3.3.3"); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const St=t=>e=>"function"==typeof e?((t,e)=>(customElements.define(t,e),e))(t,e):((t,e)=>{const{kind:i,elements:n}=e;return{kind:i,elements:n,finisher(e){customElements.define(t,e)}}})(t,e) -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */,Tt=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?{...e,finisher(i){i.createProperty(e.key,t)}}:{kind:"field",key:Symbol(),placement:"own",descriptor:{},originalKey:e.key,initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}},It=(t,e,i)=>{e.constructor.createProperty(i,t)};function At(t){return(e,i)=>void 0!==i?It(t,e,i):Tt(t,e) -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */}function Rt(t){return At({...t,state:!0})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Pt=({finisher:t,descriptor:e})=>(i,n)=>{var o;if(void 0===n){const n=null!==(o=i.originalKey)&&void 0!==o?o:i.key,r=null!=e?{kind:"method",placement:"prototype",key:n,descriptor:e(i.key)}:{...i,key:n};return null!=t&&(r.finisher=function(e){t(e,n)}),r}{const o=i.constructor;void 0!==e&&Object.defineProperty(i,n,e(n)),null==t||t(o,n)}} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */;function Dt(t){return Pt({finisher:(e,i)=>{Object.assign(e.prototype[i],t)}})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function Mt(t,e){return Pt({descriptor:e=>{const i={get(){var e,i;return null!==(i=null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t))&&void 0!==i?i:null},enumerable:!0,configurable:!0};return i}})} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */function Ft(t){return Pt({descriptor:e=>({async get(){var e;return await this.updateComplete,null===(e=this.renderRoot)||void 0===e?void 0:e.querySelector(t)},enumerable:!0,configurable:!0})})} -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */var Nt;const Bt=null!=(null===(Nt=window.HTMLSlotElement)||void 0===Nt?void 0:Nt.prototype.assignedElements)?(t,e)=>t.assignedElements(e):(t,e)=>t.assignedNodes(e).filter((t=>t.nodeType===Node.ELEMENT_NODE)); -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function Lt(t,e,i){let n;return n={flatten:e},i?function(t){const{slot:e,selector:i}=null!=t?t:{};return Pt({descriptor:n=>({get(){var n;const o="slot"+(e?`[name=${e}]`:":not([name])"),r=null===(n=this.renderRoot)||void 0===n?void 0:n.querySelector(o),s=null!=r?Bt(r,t):[];return i?s.filter((t=>t.matches(i))):s},enumerable:!0,configurable:!0})})}({slot:t,flatten:e,selector:i}):Pt({descriptor:t=>({get(){var t,e;const i=null===(t=this.renderRoot)||void 0===t?void 0:t.querySelector("slot:not([name])");return null!==(e=null==i?void 0:i.assignedNodes(n))&&void 0!==e?e:[]},enumerable:!0,configurable:!0})})}var zt,jt;!function(t){t.language="language",t.system="system",t.comma_decimal="comma_decimal",t.decimal_comma="decimal_comma",t.space_comma="space_comma",t.none="none"}(zt||(zt={})),function(t){t.language="language",t.system="system",t.am_pm="12",t.twenty_four="24"}(jt||(jt={}));var Ht=function(t,e,i,n){n=n||{},i=null==i?{}:i;var o=new Event(e,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed});return o.detail=i,t.dispatchEvent(o),o};const $t="2.1.2",Ut={autoResize:!0,height:"500px",configure:{enabled:!1},edges:{arrows:{to:{enabled:!0,scaleFactor:1},middle:{enabled:!1},from:{enabled:!1}},color:"#03a9f4",smooth:!0,physics:!1,width:2,labelHighlightBold:!1,font:{align:"top",color:"var(--primary-text-color, #fff)",strokeWidth:0,size:14}},groups:{Controller:{color:{background:"#518C43"},shape:"circle",margin:{top:15,right:10,bottom:15,left:10}},RoomStat:{color:{background:"#B1345C"}},iTRV:{color:{background:"#E48629"}},SmartPlug:{color:{background:"#3B808E"}},HeatingActuator:{color:{background:"#5A87FA"}},UnderFloorHeating:{color:{background:"#0D47A1"}},Shutter:{color:{background:"#4A5963"}},OnOffLight:{color:{background:"#E4B62B"}},DimmableLight:{color:{background:"#E4B62B"}}},interaction:{selectable:!1,selectConnectedEdges:!1},layout:{randomSeed:"1:1"},nodes:{shape:"box",size:25,font:{size:14,color:"#fff"},margin:{top:10,bottom:10,left:10,right:10},borderWidth:0,mass:1.3,chosen:!1},physics:{enabled:!1}};var Wt={version:"Version",invalid_configuration:"Invalid configuration"},Vt={actions:{copy:"Copy",files:"Files",add_before:"Add Before",add_after:"Add After"},headings:{schedule_actions:"Schedule Actions",schedule_type:"Schedule Type",schedule_id:"Schedule Id",schedule_name:"Schedule Name",schedule_assignment:"Schedule Assignment",not_assigned:"(Not Assigned)"}},qt={common:Wt,wiser:Vt},Xt={version:"Déclinaison",invalid_configuration:"Configuration Invalide"},Yt={actions:{copy:"Copie",files:"Fichier",add_before:"Ajouter Avant",add_after:"Ajouter Après"},headings:{schedule_actions:"Schedule Actions",schedule_type:"Schedule Type",schedule_id:"Schedule Id",schedule_name:"Schedule Name",schedule_assignment:"Schedule Assignment",not_assigned:"(Not Assigned)"}},Gt={common:Xt,wiser:Yt};const Kt={en:Object.freeze({__proto__:null,common:Wt,default:qt,wiser:Vt}),fr:Object.freeze({__proto__:null,common:Xt,default:Gt,wiser:Yt})};function Zt(t,e="",i=""){const n=(localStorage.getItem("selectedLanguage")||"en").replace(/['"]+/g,"").replace("-","_");let o;try{o=t.split(".").reduce(((t,e)=>t[e]),Kt[n]),o||(o=t.split(".").reduce(((t,e)=>t[e]),Kt.en))}catch(e){try{o=t.split(".").reduce(((t,e)=>t[e]),Kt.en)}catch(t){o=""}}return void 0===o&&(o=t.split(".").reduce(((t,e)=>t[e]),Kt.en)),""!==e&&""!==i&&(o=o.replace(e,i)),o} -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const Qt=globalThis,Jt=Qt.ShadowRoot&&(void 0===Qt.ShadyCSS||Qt.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype; -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -function te(t){return class extends t{createRenderRoot(){const t=this.constructor,{registry:e,elementDefinitions:i,shadowRootOptions:n}=t;i&&!e&&(t.registry=new CustomElementRegistry,Object.entries(i).forEach((([e,i])=>t.registry.define(e,i))));const o=this.renderOptions.creationScope=this.attachShadow({...n,customElements:t.registry});return((t,e)=>{if(Jt)t.adoptedStyleSheets=e.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet));else for(const i of e){const e=document.createElement("style"),n=Qt.litNonce;void 0!==n&&e.setAttribute("nonce",n),e.textContent=i.cssText,t.appendChild(e)}})(o,this.constructor.elementStyles),o}}} -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var ee=function(){function t(t){void 0===t&&(t={}),this.adapter=t}return Object.defineProperty(t,"cssClasses",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"strings",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"numbers",{get:function(){return{}},enumerable:!1,configurable:!0}),Object.defineProperty(t,"defaultAdapter",{get:function(){return{}},enumerable:!1,configurable:!0}),t.prototype.init=function(){},t.prototype.destroy=function(){},t}(),ie={ROOT:"mdc-form-field"},ne={LABEL_SELECTOR:".mdc-form-field > label"},oe=function(t){function e(i){var o=t.call(this,n(n({},e.defaultAdapter),i))||this;return o.click=function(){o.handleClick()},o}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return ie},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return ne},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{activateInputRipple:function(){},deactivateInputRipple:function(){},deregisterInteractionHandler:function(){},registerInteractionHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerInteractionHandler("click",this.click)},e.prototype.destroy=function(){this.adapter.deregisterInteractionHandler("click",this.click)},e.prototype.handleClick=function(){var t=this;this.adapter.activateInputRipple(),requestAnimationFrame((function(){t.adapter.deactivateInputRipple()}))},e}(ee); -/** - * @license - * Copyright 2017 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -const re=t=>t.nodeType===Node.ELEMENT_NODE;function se(t){return{addClass:e=>{t.classList.add(e)},removeClass:e=>{t.classList.remove(e)},hasClass:e=>t.classList.contains(e)}}const ae=()=>{},de={get passive(){return!1}};document.addEventListener("x",ae,de),document.removeEventListener("x",ae);const le=(t=window.document)=>{let e=t.activeElement;const i=[];if(!e)return i;for(;e&&(i.push(e),e.shadowRoot);)e=e.shadowRoot.activeElement;return i},ce=t=>{const e=le();if(!e.length)return!1;const i=e[e.length-1],n=new Event("check-if-focused",{bubbles:!0,composed:!0});let o=[];const r=t=>{o=t.composedPath()};return document.body.addEventListener("check-if-focused",r),i.dispatchEvent(n),document.body.removeEventListener("check-if-focused",r),-1!==o.indexOf(t)}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class he extends Ot{click(){if(this.mdcRoot)return this.mdcRoot.focus(),void this.mdcRoot.click();super.click()}createFoundation(){void 0!==this.mdcFoundation&&this.mdcFoundation.destroy(),this.mdcFoundationClass&&(this.mdcFoundation=new this.mdcFoundationClass(this.createAdapter()),this.mdcFoundation.init())}firstUpdated(){this.createFoundation()}} -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */var ue,pe;const fe=null!==(pe=null===(ue=window.ShadyDOM)||void 0===ue?void 0:ue.inUse)&&void 0!==pe&&pe;class me extends he{constructor(){super(...arguments),this.disabled=!1,this.containingForm=null,this.formDataListener=t=>{this.disabled||this.setFormData(t.formData)}}findFormElement(){if(!this.shadowRoot||fe)return null;const t=this.getRootNode().querySelectorAll("form");for(const e of Array.from(t))if(e.contains(this))return e;return null}connectedCallback(){var t;super.connectedCallback(),this.containingForm=this.findFormElement(),null===(t=this.containingForm)||void 0===t||t.addEventListener("formdata",this.formDataListener)}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this.containingForm)||void 0===t||t.removeEventListener("formdata",this.formDataListener),this.containingForm=null}click(){this.formElement&&!this.disabled&&(this.formElement.focus(),this.formElement.click())}firstUpdated(){super.firstUpdated(),this.shadowRoot&&this.mdcRoot.addEventListener("change",(t=>{this.dispatchEvent(new Event("change",t))}))}}me.shadowRootOptions={mode:"open",delegatesFocus:!0},o([At({type:Boolean})],me.prototype,"disabled",void 0); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -const ve=t=>(e,i)=>{if(e.constructor._observers){if(!e.constructor.hasOwnProperty("_observers")){const t=e.constructor._observers;e.constructor._observers=new Map,t.forEach(((t,i)=>e.constructor._observers.set(i,t)))}}else{e.constructor._observers=new Map;const t=e.updated;e.updated=function(e){t.call(this,e),e.forEach(((t,e)=>{const i=this.constructor._observers.get(e);void 0!==i&&i.call(this,this[e],t)}))}}e.constructor._observers.set(i,t)} -/** - * @license - * Copyright 2017 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */,ge=1,ye=3,be=4,xe=t=>(...e)=>({_$litDirective$:t,values:e});let _e=class{constructor(t){}get _$AU(){return this._$AM._$AU}_$AT(t,e,i){this._$Ct=t,this._$AM=e,this._$Ci=i}_$AS(t,e){return this.update(t,e)}update(t,e){return this.render(...e)}}; -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */const we=xe(class extends _e{constructor(t){var e;if(super(t),t.type!==ge||"class"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("`classMap()` can only be used in the `class` attribute and must be the only part in the attribute.")}render(t){return" "+Object.keys(t).filter((e=>t[e])).join(" ")+" "}update(t,[e]){var i,n;if(void 0===this.it){this.it=new Set,void 0!==t.strings&&(this.nt=new Set(t.strings.join(" ").split(/\s/).filter((t=>""!==t))));for(const t in e)e[t]&&!(null===(i=this.nt)||void 0===i?void 0:i.has(t))&&this.it.add(t);return this.render(e)}const o=t.element.classList;this.it.forEach((t=>{t in e||(o.remove(t),this.it.delete(t))}));for(const t in e){const i=!!e[t];i===this.it.has(t)||(null===(n=this.nt)||void 0===n?void 0:n.has(t))||(i?(o.add(t),this.it.add(t)):(o.remove(t),this.it.delete(t)))}return U}}); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class Ee extends he{constructor(){super(...arguments),this.alignEnd=!1,this.spaceBetween=!1,this.nowrap=!1,this.label="",this.mdcFoundationClass=oe}createAdapter(){return{registerInteractionHandler:(t,e)=>{this.labelEl.addEventListener(t,e)},deregisterInteractionHandler:(t,e)=>{this.labelEl.removeEventListener(t,e)},activateInputRipple:async()=>{const t=this.input;if(t instanceof me){const e=await t.ripple;e&&e.startPress()}},deactivateInputRipple:async()=>{const t=this.input;if(t instanceof me){const e=await t.ripple;e&&e.endPress()}}}}get input(){var t,e;return null!==(e=null===(t=this.slottedInputs)||void 0===t?void 0:t[0])&&void 0!==e?e:null}render(){const t={"mdc-form-field--align-end":this.alignEnd,"mdc-form-field--space-between":this.spaceBetween,"mdc-form-field--nowrap":this.nowrap};return $` -
- - -
`}click(){this._labelClick()}_labelClick(){const t=this.input;t&&(t.focus(),t.click())}}o([At({type:Boolean})],Ee.prototype,"alignEnd",void 0),o([At({type:Boolean})],Ee.prototype,"spaceBetween",void 0),o([At({type:Boolean})],Ee.prototype,"nowrap",void 0),o([At({type:String}),ve((async function(t){var e;null===(e=this.input)||void 0===e||e.setAttribute("aria-label",t)}))],Ee.prototype,"label",void 0),o([Mt(".mdc-form-field")],Ee.prototype,"mdcRoot",void 0),o([Lt("",!0,"*")],Ee.prototype,"slottedInputs",void 0),o([Mt("label")],Ee.prototype,"labelEl",void 0); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const ke=ht`.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}`,Oe={"mwc-formfield":class extends Ee{static get styles(){return ke}}}; -/** - * @license - * Copyright 2020 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Ce={UNKNOWN:"Unknown",BACKSPACE:"Backspace",ENTER:"Enter",SPACEBAR:"Spacebar",PAGE_UP:"PageUp",PAGE_DOWN:"PageDown",END:"End",HOME:"Home",ARROW_LEFT:"ArrowLeft",ARROW_UP:"ArrowUp",ARROW_RIGHT:"ArrowRight",ARROW_DOWN:"ArrowDown",DELETE:"Delete",ESCAPE:"Escape",TAB:"Tab"},Se=new Set;Se.add(Ce.BACKSPACE),Se.add(Ce.ENTER),Se.add(Ce.SPACEBAR),Se.add(Ce.PAGE_UP),Se.add(Ce.PAGE_DOWN),Se.add(Ce.END),Se.add(Ce.HOME),Se.add(Ce.ARROW_LEFT),Se.add(Ce.ARROW_UP),Se.add(Ce.ARROW_RIGHT),Se.add(Ce.ARROW_DOWN),Se.add(Ce.DELETE),Se.add(Ce.ESCAPE),Se.add(Ce.TAB);var Te=8,Ie=13,Ae=32,Re=33,Pe=34,De=35,Me=36,Fe=37,Ne=38,Be=39,Le=40,ze=46,je=27,He=9,$e=new Map;$e.set(Te,Ce.BACKSPACE),$e.set(Ie,Ce.ENTER),$e.set(Ae,Ce.SPACEBAR),$e.set(Re,Ce.PAGE_UP),$e.set(Pe,Ce.PAGE_DOWN),$e.set(De,Ce.END),$e.set(Me,Ce.HOME),$e.set(Fe,Ce.ARROW_LEFT),$e.set(Ne,Ce.ARROW_UP),$e.set(Be,Ce.ARROW_RIGHT),$e.set(Le,Ce.ARROW_DOWN),$e.set(ze,Ce.DELETE),$e.set(je,Ce.ESCAPE),$e.set(He,Ce.TAB);var Ue,We,Ve=new Set;function qe(t){var e=t.key;if(Se.has(e))return e;var i=$e.get(t.keyCode);return i||Ce.UNKNOWN} -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */Ve.add(Ce.PAGE_UP),Ve.add(Ce.PAGE_DOWN),Ve.add(Ce.END),Ve.add(Ce.HOME),Ve.add(Ce.ARROW_LEFT),Ve.add(Ce.ARROW_UP),Ve.add(Ce.ARROW_RIGHT),Ve.add(Ce.ARROW_DOWN);var Xe="mdc-list-item--activated",Ye="mdc-list-item",Ge="mdc-list-item--disabled",Ke="mdc-list-item--selected",Ze="mdc-list-item__text",Qe="mdc-list-item__primary-text",Je="mdc-list";(Ue={})[""+Xe]="mdc-list-item--activated",Ue[""+Ye]="mdc-list-item",Ue[""+Ge]="mdc-list-item--disabled",Ue[""+Ke]="mdc-list-item--selected",Ue[""+Qe]="mdc-list-item__primary-text",Ue[""+Je]="mdc-list";var ti=((We={})[""+Xe]="mdc-deprecated-list-item--activated",We[""+Ye]="mdc-deprecated-list-item",We[""+Ge]="mdc-deprecated-list-item--disabled",We[""+Ke]="mdc-deprecated-list-item--selected",We[""+Ze]="mdc-deprecated-list-item__text",We[""+Qe]="mdc-deprecated-list-item__primary-text",We[""+Je]="mdc-deprecated-list",We),ei={ACTION_EVENT:"MDCList:action",SELECTION_CHANGE_EVENT:"MDCList:selectionChange",ARIA_CHECKED:"aria-checked",ARIA_CHECKED_CHECKBOX_SELECTOR:'[role="checkbox"][aria-checked="true"]',ARIA_CHECKED_RADIO_SELECTOR:'[role="radio"][aria-checked="true"]',ARIA_CURRENT:"aria-current",ARIA_DISABLED:"aria-disabled",ARIA_ORIENTATION:"aria-orientation",ARIA_ORIENTATION_HORIZONTAL:"horizontal",ARIA_ROLE_CHECKBOX_SELECTOR:'[role="checkbox"]',ARIA_SELECTED:"aria-selected",ARIA_INTERACTIVE_ROLES_SELECTOR:'[role="listbox"], [role="menu"]',ARIA_MULTI_SELECTABLE_SELECTOR:'[aria-multiselectable="true"]',CHECKBOX_RADIO_SELECTOR:'input[type="checkbox"], input[type="radio"]',CHECKBOX_SELECTOR:'input[type="checkbox"]',CHILD_ELEMENTS_TO_TOGGLE_TABINDEX:"\n ."+Ye+" button:not(:disabled),\n ."+Ye+" a,\n ."+ti[Ye]+" button:not(:disabled),\n ."+ti[Ye]+" a\n ",DEPRECATED_SELECTOR:".mdc-deprecated-list",FOCUSABLE_CHILD_ELEMENTS:"\n ."+Ye+" button:not(:disabled),\n ."+Ye+" a,\n ."+Ye+' input[type="radio"]:not(:disabled),\n .'+Ye+' input[type="checkbox"]:not(:disabled),\n .'+ti[Ye]+" button:not(:disabled),\n ."+ti[Ye]+" a,\n ."+ti[Ye]+' input[type="radio"]:not(:disabled),\n .'+ti[Ye]+' input[type="checkbox"]:not(:disabled)\n ',RADIO_SELECTOR:'input[type="radio"]',SELECTED_ITEM_SELECTOR:'[aria-selected="true"], [aria-current="true"]'},ii={UNSET_INDEX:-1,TYPEAHEAD_BUFFER_CLEAR_TIMEOUT_MS:300},ni=["input","button","textarea","select"],oi=function(t){var e=t.target;if(e){var i=(""+e.tagName).toLowerCase();-1===ni.indexOf(i)&&t.preventDefault()}};function ri(t,e){for(var i=new Map,n=0;ne&&!i(r[a].index)){d=a;break}if(-1!==d)return n.sortedIndexCursor=d,r[n.sortedIndexCursor].index;return-1}(r,s,d,e):function(t,e,i){var n=i.typeaheadBuffer[0],o=t.get(n);if(!o)return-1;var r=o[i.sortedIndexCursor];if(0===r.text.lastIndexOf(i.typeaheadBuffer,0)&&!e(r.index))return r.index;var s=(i.sortedIndexCursor+1)%o.length,a=-1;for(;s!==i.sortedIndexCursor;){var d=o[s],l=0===d.text.lastIndexOf(i.typeaheadBuffer,0),c=!e(d.index);if(l&&c){a=s;break}s=(s+1)%o.length}if(-1!==a)return i.sortedIndexCursor=a,o[i.sortedIndexCursor].index;return-1}(r,d,e),-1===i||a||o(i),i}function ai(t){return t.typeaheadBuffer.length>0} -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var di={LABEL_FLOAT_ABOVE:"mdc-floating-label--float-above",LABEL_REQUIRED:"mdc-floating-label--required",LABEL_SHAKE:"mdc-floating-label--shake",ROOT:"mdc-floating-label"},li=function(t){function e(i){var o=t.call(this,n(n({},e.defaultAdapter),i))||this;return o.shakeAnimationEndHandler=function(){o.handleShakeAnimationEnd()},o}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return di},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},getWidth:function(){return 0},registerInteractionHandler:function(){},deregisterInteractionHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterInteractionHandler("animationend",this.shakeAnimationEndHandler)},e.prototype.getWidth=function(){return this.adapter.getWidth()},e.prototype.shake=function(t){var i=e.cssClasses.LABEL_SHAKE;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.float=function(t){var i=e.cssClasses,n=i.LABEL_FLOAT_ABOVE,o=i.LABEL_SHAKE;t?this.adapter.addClass(n):(this.adapter.removeClass(n),this.adapter.removeClass(o))},e.prototype.setRequired=function(t){var i=e.cssClasses.LABEL_REQUIRED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.handleShakeAnimationEnd=function(){var t=e.cssClasses.LABEL_SHAKE;this.adapter.removeClass(t)},e}(ee); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */const ci=xe(class extends _e{constructor(t){switch(super(t),this.foundation=null,this.previousPart=null,t.type){case ge:case ye:break;default:throw new Error("FloatingLabel directive only support attribute and property parts")}}update(t,[e]){if(t!==this.previousPart){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-floating-label");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),getWidth:()=>t.scrollWidth,registerInteractionHandler:(e,i)=>{t.addEventListener(e,i)},deregisterInteractionHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new li(i),this.foundation.init()}return this.render(e)}render(t){return this.foundation}}); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var hi={LINE_RIPPLE_ACTIVE:"mdc-line-ripple--active",LINE_RIPPLE_DEACTIVATING:"mdc-line-ripple--deactivating"},ui=function(t){function e(i){var o=t.call(this,n(n({},e.defaultAdapter),i))||this;return o.transitionEndHandler=function(t){o.handleTransitionEnd(t)},o}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return hi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},setStyle:function(){},registerEventHandler:function(){},deregisterEventHandler:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){this.adapter.registerEventHandler("transitionend",this.transitionEndHandler)},e.prototype.destroy=function(){this.adapter.deregisterEventHandler("transitionend",this.transitionEndHandler)},e.prototype.activate=function(){this.adapter.removeClass(hi.LINE_RIPPLE_DEACTIVATING),this.adapter.addClass(hi.LINE_RIPPLE_ACTIVE)},e.prototype.setRippleCenter=function(t){this.adapter.setStyle("transform-origin",t+"px center")},e.prototype.deactivate=function(){this.adapter.addClass(hi.LINE_RIPPLE_DEACTIVATING)},e.prototype.handleTransitionEnd=function(t){var e=this.adapter.hasClass(hi.LINE_RIPPLE_DEACTIVATING);"opacity"===t.propertyName&&e&&(this.adapter.removeClass(hi.LINE_RIPPLE_ACTIVE),this.adapter.removeClass(hi.LINE_RIPPLE_DEACTIVATING))},e}(ee); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */const pi=xe(class extends _e{constructor(t){switch(super(t),this.previousPart=null,this.foundation=null,t.type){case ge:case ye:return;default:throw new Error("LineRipple only support attribute and property parts.")}}update(t,e){if(this.previousPart!==t){this.foundation&&this.foundation.destroy(),this.previousPart=t;const e=t.element;e.classList.add("mdc-line-ripple");const i=(t=>({addClass:e=>t.classList.add(e),removeClass:e=>t.classList.remove(e),hasClass:e=>t.classList.contains(e),setStyle:(e,i)=>t.style.setProperty(e,i),registerEventHandler:(e,i)=>{t.addEventListener(e,i)},deregisterEventHandler:(e,i)=>{t.removeEventListener(e,i)}}))(e);this.foundation=new ui(i),this.foundation.init()}return this.render()}render(){return this.foundation}}); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var fi,mi,vi={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"},gi={CLOSED_EVENT:"MDCMenuSurface:closed",CLOSING_EVENT:"MDCMenuSurface:closing",OPENED_EVENT:"MDCMenuSurface:opened",OPENING_EVENT:"MDCMenuSurface:opening",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(", ")},yi={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(t){t[t.BOTTOM=1]="BOTTOM",t[t.CENTER=2]="CENTER",t[t.RIGHT=4]="RIGHT",t[t.FLIP_RTL=8]="FLIP_RTL"}(fi||(fi={})),function(t){t[t.TOP_LEFT=0]="TOP_LEFT",t[t.TOP_RIGHT=4]="TOP_RIGHT",t[t.BOTTOM_LEFT=1]="BOTTOM_LEFT",t[t.BOTTOM_RIGHT=5]="BOTTOM_RIGHT",t[t.TOP_START=8]="TOP_START",t[t.TOP_END=12]="TOP_END",t[t.BOTTOM_START=9]="BOTTOM_START",t[t.BOTTOM_END=13]="BOTTOM_END"}(mi||(mi={})); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var bi={ACTIVATED:"mdc-select--activated",DISABLED:"mdc-select--disabled",FOCUSED:"mdc-select--focused",INVALID:"mdc-select--invalid",MENU_INVALID:"mdc-select__menu--invalid",OUTLINED:"mdc-select--outlined",REQUIRED:"mdc-select--required",ROOT:"mdc-select",WITH_LEADING_ICON:"mdc-select--with-leading-icon"},xi={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",ARIA_SELECTED_ATTR:"aria-selected",CHANGE_EVENT:"MDCSelect:change",HIDDEN_INPUT_SELECTOR:'input[type="hidden"]',LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-select__icon",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",MENU_SELECTOR:".mdc-select__menu",OUTLINE_SELECTOR:".mdc-notched-outline",SELECTED_TEXT_SELECTOR:".mdc-select__selected-text",SELECT_ANCHOR_SELECTOR:".mdc-select__anchor",VALUE_ATTR:"data-value"},_i={LABEL_SCALE:.75,UNSET_INDEX:-1,CLICK_DEBOUNCE_TIMEOUT_MS:330},wi=function(t){function e(i,o){void 0===o&&(o={});var r=t.call(this,n(n({},e.defaultAdapter),i))||this;return r.disabled=!1,r.isMenuOpen=!1,r.useDefaultValidation=!0,r.customValidity=!0,r.lastSelectedIndex=_i.UNSET_INDEX,r.clickDebounceTimeout=0,r.recentlyClicked=!1,r.leadingIcon=o.leadingIcon,r.helperText=o.helperText,r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return bi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return _i},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return xi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!1},activateBottomLine:function(){},deactivateBottomLine:function(){},getSelectedIndex:function(){return-1},setSelectedIndex:function(){},hasLabel:function(){return!1},floatLabel:function(){},getLabelWidth:function(){return 0},setLabelRequired:function(){},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){},setRippleCenter:function(){},notifyChange:function(){},setSelectedText:function(){},isSelectAnchorFocused:function(){return!1},getSelectAnchorAttr:function(){return""},setSelectAnchorAttr:function(){},removeSelectAnchorAttr:function(){},addMenuClass:function(){},removeMenuClass:function(){},openMenu:function(){},closeMenu:function(){},getAnchorElement:function(){return null},setMenuAnchorElement:function(){},setMenuAnchorCorner:function(){},setMenuWrapFocus:function(){},focusMenuItemAtIndex:function(){},getMenuItemCount:function(){return 0},getMenuItemValues:function(){return[]},getMenuItemTextAtIndex:function(){return""},isTypeaheadInProgress:function(){return!1},typeaheadMatchItem:function(){return-1}}},enumerable:!1,configurable:!0}),e.prototype.getSelectedIndex=function(){return this.adapter.getSelectedIndex()},e.prototype.setSelectedIndex=function(t,e,i){void 0===e&&(e=!1),void 0===i&&(i=!1),t>=this.adapter.getMenuItemCount()||(t===_i.UNSET_INDEX?this.adapter.setSelectedText(""):this.adapter.setSelectedText(this.adapter.getMenuItemTextAtIndex(t).trim()),this.adapter.setSelectedIndex(t),e&&this.adapter.closeMenu(),i||this.lastSelectedIndex===t||this.handleChange(),this.lastSelectedIndex=t)},e.prototype.setValue=function(t,e){void 0===e&&(e=!1);var i=this.adapter.getMenuItemValues().indexOf(t);this.setSelectedIndex(i,!1,e)},e.prototype.getValue=function(){var t=this.adapter.getSelectedIndex(),e=this.adapter.getMenuItemValues();return t!==_i.UNSET_INDEX?e[t]:""},e.prototype.getDisabled=function(){return this.disabled},e.prototype.setDisabled=function(t){this.disabled=t,this.disabled?(this.adapter.addClass(bi.DISABLED),this.adapter.closeMenu()):this.adapter.removeClass(bi.DISABLED),this.leadingIcon&&this.leadingIcon.setDisabled(this.disabled),this.disabled?this.adapter.removeSelectAnchorAttr("tabindex"):this.adapter.setSelectAnchorAttr("tabindex","0"),this.adapter.setSelectAnchorAttr("aria-disabled",this.disabled.toString())},e.prototype.openMenu=function(){this.adapter.addClass(bi.ACTIVATED),this.adapter.openMenu(),this.isMenuOpen=!0,this.adapter.setSelectAnchorAttr("aria-expanded","true")},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.layout=function(){if(this.adapter.hasLabel()){var t=this.getValue().length>0,e=this.adapter.hasClass(bi.FOCUSED),i=t||e,n=this.adapter.hasClass(bi.REQUIRED);this.notchOutline(i),this.adapter.floatLabel(i),this.adapter.setLabelRequired(n)}},e.prototype.layoutOptions=function(){var t=this.adapter.getMenuItemValues().indexOf(this.getValue());this.setSelectedIndex(t,!1,!0)},e.prototype.handleMenuOpened=function(){if(0!==this.adapter.getMenuItemValues().length){var t=this.getSelectedIndex(),e=t>=0?t:0;this.adapter.focusMenuItemAtIndex(e)}},e.prototype.handleMenuClosing=function(){this.adapter.setSelectAnchorAttr("aria-expanded","false")},e.prototype.handleMenuClosed=function(){this.adapter.removeClass(bi.ACTIVATED),this.isMenuOpen=!1,this.adapter.isSelectAnchorFocused()||this.blur()},e.prototype.handleChange=function(){this.layout(),this.adapter.notifyChange(this.getValue()),this.adapter.hasClass(bi.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.handleMenuItemAction=function(t){this.setSelectedIndex(t,!0)},e.prototype.handleFocus=function(){this.adapter.addClass(bi.FOCUSED),this.layout(),this.adapter.activateBottomLine()},e.prototype.handleBlur=function(){this.isMenuOpen||this.blur()},e.prototype.handleClick=function(t){this.disabled||this.recentlyClicked||(this.setClickDebounceTimeout(),this.isMenuOpen?this.adapter.closeMenu():(this.adapter.setRippleCenter(t),this.openMenu()))},e.prototype.handleKeydown=function(t){if(!this.isMenuOpen&&this.adapter.hasClass(bi.FOCUSED)){var e=qe(t)===Ce.ENTER,i=qe(t)===Ce.SPACEBAR,n=qe(t)===Ce.ARROW_UP,o=qe(t)===Ce.ARROW_DOWN;if(!(t.ctrlKey||t.metaKey)&&(!i&&t.key&&1===t.key.length||i&&this.adapter.isTypeaheadInProgress())){var r=i?" ":t.key,s=this.adapter.typeaheadMatchItem(r,this.getSelectedIndex());return s>=0&&this.setSelectedIndex(s),void t.preventDefault()}(e||i||n||o)&&(this.openMenu(),t.preventDefault())}},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()){var e=this.adapter.hasClass(bi.FOCUSED);if(t){var i=_i.LABEL_SCALE,n=this.adapter.getLabelWidth()*i;this.adapter.notchOutline(n)}else e||this.adapter.closeOutline()}},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.getUseDefaultValidation=function(){return this.useDefaultValidation},e.prototype.setUseDefaultValidation=function(t){this.useDefaultValidation=t},e.prototype.setValid=function(t){this.useDefaultValidation||(this.customValidity=t),this.adapter.setSelectAnchorAttr("aria-invalid",(!t).toString()),t?(this.adapter.removeClass(bi.INVALID),this.adapter.removeMenuClass(bi.MENU_INVALID)):(this.adapter.addClass(bi.INVALID),this.adapter.addMenuClass(bi.MENU_INVALID)),this.syncHelperTextValidity(t)},e.prototype.isValid=function(){return this.useDefaultValidation&&this.adapter.hasClass(bi.REQUIRED)&&!this.adapter.hasClass(bi.DISABLED)?this.getSelectedIndex()!==_i.UNSET_INDEX&&(0!==this.getSelectedIndex()||Boolean(this.getValue())):this.customValidity},e.prototype.setRequired=function(t){t?this.adapter.addClass(bi.REQUIRED):this.adapter.removeClass(bi.REQUIRED),this.adapter.setSelectAnchorAttr("aria-required",t.toString()),this.adapter.setLabelRequired(t)},e.prototype.getRequired=function(){return"true"===this.adapter.getSelectAnchorAttr("aria-required")},e.prototype.init=function(){var t=this.adapter.getAnchorElement();t&&(this.adapter.setMenuAnchorElement(t),this.adapter.setMenuAnchorCorner(mi.BOTTOM_START)),this.adapter.setMenuWrapFocus(!1),this.setDisabled(this.adapter.hasClass(bi.DISABLED)),this.syncHelperTextValidity(!this.adapter.hasClass(bi.INVALID)),this.layout(),this.layoutOptions()},e.prototype.blur=function(){this.adapter.removeClass(bi.FOCUSED),this.layout(),this.adapter.deactivateBottomLine(),this.adapter.hasClass(bi.REQUIRED)&&this.useDefaultValidation&&this.setValid(this.isValid())},e.prototype.syncHelperTextValidity=function(t){if(this.helperText){this.helperText.setValidity(t);var e=this.helperText.isVisible(),i=this.helperText.getId();e&&i?this.adapter.setSelectAnchorAttr(xi.ARIA_DESCRIBEDBY,i):this.adapter.removeSelectAnchorAttr(xi.ARIA_DESCRIBEDBY)}},e.prototype.setClickDebounceTimeout=function(){var t=this;clearTimeout(this.clickDebounceTimeout),this.clickDebounceTimeout=setTimeout((function(){t.recentlyClicked=!1}),_i.CLICK_DEBOUNCE_TIMEOUT_MS),this.recentlyClicked=!0},e}(ee); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Ei=t=>null!=t?t:W -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */,ki=(t={})=>{const e={};for(const i in t)e[i]=t[i];return Object.assign({badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:!0,valueMissing:!1},e)};class Oi extends me{constructor(){super(...arguments),this.mdcFoundationClass=wi,this.disabled=!1,this.outlined=!1,this.label="",this.outlineOpen=!1,this.outlineWidth=0,this.value="",this.name="",this.selectedText="",this.icon="",this.menuOpen=!1,this.helper="",this.validateOnInitialRender=!1,this.validationMessage="",this.required=!1,this.naturalMenuWidth=!1,this.isUiValid=!0,this.fixedMenuPosition=!1,this.typeaheadState={bufferClearTimeout:0,currentFirstChar:"",sortedIndexCursor:0,typeaheadBuffer:""},this.sortedIndexByFirstChar=new Map,this.menuElement_=null,this.listeners=[],this.onBodyClickBound=()=>{},this._menuUpdateComplete=null,this.valueSetDirectly=!1,this.validityTransform=null,this._validity=ki()}get items(){return this.menuElement_||(this.menuElement_=this.menuElement),this.menuElement_?this.menuElement_.items:[]}get selected(){const t=this.menuElement;return t?t.selected:null}get index(){const t=this.menuElement;return t?t.index:-1}get shouldRenderHelperText(){return!!this.helper||!!this.validationMessage}get validity(){return this._checkValidity(this.value),this._validity}render(){const t={"mdc-select--disabled":this.disabled,"mdc-select--no-label":!this.label,"mdc-select--filled":!this.outlined,"mdc-select--outlined":this.outlined,"mdc-select--with-leading-icon":!!this.icon,"mdc-select--required":this.required,"mdc-select--invalid":!this.isUiValid},e=this.label?"label":void 0,i=this.shouldRenderHelperText?"helper-text":void 0;return $` -
- - -
- ${this.renderRipple()} - ${this.outlined?this.renderOutline():this.renderLabel()} - ${this.renderLeadingIcon()} - - ${this.selectedText} - - - - - - - - - - ${this.renderLineRipple()} -
- ${this.renderMenu()} -
- ${this.renderHelperText()}`}renderMenu(){const t=this.getMenuClasses();return $` - - ${this.renderMenuContent()} - `}getMenuClasses(){return{"mdc-select__menu":!0,"mdc-menu":!0,"mdc-menu-surface":!0,"mdc-select__menu--invalid":!this.isUiValid}}renderMenuContent(){return $``}renderRipple(){return this.outlined?W:$` - - `}renderOutline(){return this.outlined?$` - - ${this.renderLabel()} - `:W}renderLabel(){return this.label?$` - ${this.label} - `:W}renderLeadingIcon(){return this.icon?$`
${this.icon}
`:W}renderLineRipple(){return this.outlined?W:$` - - `}renderHelperText(){if(!this.shouldRenderHelperText)return W;const t=this.validationMessage&&!this.isUiValid;return $` -

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

`}createAdapter(){return Object.assign(Object.assign({},se(this.mdcRoot)),{activateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.activate()},deactivateBottomLine:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.deactivate()},hasLabel:()=>!!this.label,floatLabel:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.float(t)},getLabelWidth:()=>this.labelElement?this.labelElement.floatingLabelFoundation.getWidth():0,setLabelRequired:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.setRequired(t)},hasOutline:()=>this.outlined,notchOutline:t=>{this.outlineElement&&!this.outlineOpen&&(this.outlineWidth=t,this.outlineOpen=!0)},closeOutline:()=>{this.outlineElement&&(this.outlineOpen=!1)},setRippleCenter:t=>{if(this.lineRippleElement){this.lineRippleElement.lineRippleFoundation.setRippleCenter(t)}},notifyChange:async t=>{if(!this.valueSetDirectly&&t===this.value)return;this.valueSetDirectly=!1,this.value=t,await this.updateComplete;const e=new Event("change",{bubbles:!0});this.dispatchEvent(e)},setSelectedText:t=>this.selectedText=t,isSelectAnchorFocused:()=>{const t=this.anchorElement;if(!t)return!1;return t.getRootNode().activeElement===t},getSelectAnchorAttr:t=>{const e=this.anchorElement;return e?e.getAttribute(t):null},setSelectAnchorAttr:(t,e)=>{const i=this.anchorElement;i&&i.setAttribute(t,e)},removeSelectAnchorAttr:t=>{const e=this.anchorElement;e&&e.removeAttribute(t)},openMenu:()=>{this.menuOpen=!0},closeMenu:()=>{this.menuOpen=!1},addMenuClass:()=>{},removeMenuClass:()=>{},getAnchorElement:()=>this.anchorElement,setMenuAnchorElement:()=>{},setMenuAnchorCorner:()=>{const t=this.menuElement;t&&(t.corner="BOTTOM_START")},setMenuWrapFocus:t=>{const e=this.menuElement;e&&(e.wrapFocus=t)},focusMenuItemAtIndex:t=>{const e=this.menuElement;if(!e)return;const i=e.items[t];i&&i.focus()},getMenuItemCount:()=>{const t=this.menuElement;return t?t.items.length:0},getMenuItemValues:()=>{const t=this.menuElement;if(!t)return[];return t.items.map((t=>t.value))},getMenuItemTextAtIndex:t=>{const e=this.menuElement;if(!e)return"";const i=e.items[t];return i?i.text:""},getSelectedIndex:()=>this.index,setSelectedIndex:()=>{},isTypeaheadInProgress:()=>ai(this.typeaheadState),typeaheadMatchItem:(t,e)=>{if(!this.menuElement)return-1;const i={focusItemAtIndex:t=>{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e||this.menuElement.getFocusedItemIndex(),nextChar:t,sortedIndexByFirstChar:this.sortedIndexByFirstChar,skipFocus:!1,isItemAtIndexDisabled:t=>this.items[t].disabled},n=si(i,this.typeaheadState);return-1!==n&&this.select(n),n}})}checkValidity(){const t=this._checkValidity(this.value);if(!t){const t=new Event("invalid",{bubbles:!1,cancelable:!0});this.dispatchEvent(t)}return t}reportValidity(){const t=this.checkValidity();return this.isUiValid=t,t}_checkValidity(t){const e=this.formElement.validity;let i=ki(e);if(this.validityTransform){const e=this.validityTransform(t,i);i=Object.assign(Object.assign({},i),e)}return this._validity=i,this._validity.valid}setCustomValidity(t){this.validationMessage=t,this.formElement.setCustomValidity(t)}async getUpdateComplete(){await this._menuUpdateComplete;return await super.getUpdateComplete()}async firstUpdated(){const t=this.menuElement;if(t&&(this._menuUpdateComplete=t.updateComplete,await this._menuUpdateComplete),super.firstUpdated(),this.mdcFoundation.isValid=()=>!0,this.mdcFoundation.setValid=()=>{},this.mdcFoundation.setDisabled(this.disabled),this.validateOnInitialRender&&this.reportValidity(),!this.selected){!this.items.length&&this.slotElement&&this.slotElement.assignedNodes({flatten:!0}).length&&(await new Promise((t=>requestAnimationFrame(t))),await this.layout());const t=this.items.length&&""===this.items[0].value;if(!this.value&&t)return void this.select(0);this.selectByValue(this.value)}this.sortedIndexByFirstChar=ri(this.items.length,(t=>this.items[t].text))}onItemsUpdated(){this.sortedIndexByFirstChar=ri(this.items.length,(t=>this.items[t].text))}select(t){const e=this.menuElement;e&&e.select(t)}selectByValue(t){let e=-1;for(let i=0;i0,o=i&&this.index{this.menuElement.focusItemAtIndex(t)},focusedItemIndex:e,isTargetListItem:!!i&&i.hasAttribute("mwc-list-item"),sortedIndexByFirstChar:this.sortedIndexByFirstChar,isItemAtIndexDisabled:t=>this.items[t].disabled};!function(t,e){var i=t.event,n=t.isTargetListItem,o=t.focusedItemIndex,r=t.focusItemAtIndex,s=t.sortedIndexByFirstChar,a=t.isItemAtIndexDisabled,d="ArrowLeft"===qe(i),l="ArrowUp"===qe(i),c="ArrowRight"===qe(i),h="ArrowDown"===qe(i),u="Home"===qe(i),p="End"===qe(i),f="Enter"===qe(i),m="Spacebar"===qe(i);i.altKey||i.ctrlKey||i.metaKey||d||l||c||h||u||p||f||(m||1!==i.key.length?m&&(n&&oi(i),n&&ai(e)&&si({focusItemAtIndex:r,focusedItemIndex:o,nextChar:" ",sortedIndexByFirstChar:s,skipFocus:!1,isItemAtIndexDisabled:a},e)):(oi(i),si({focusItemAtIndex:r,focusedItemIndex:o,nextChar:i.key.toLowerCase(),sortedIndexByFirstChar:s,skipFocus:!1,isItemAtIndexDisabled:a},e)))}(n,this.typeaheadState)}async onSelected(t){this.mdcFoundation||await this.updateComplete,this.mdcFoundation.handleMenuItemAction(t.detail.index);const e=this.items[t.detail.index];e&&(this.value=e.value)}onOpened(){this.mdcFoundation&&(this.menuOpen=!0,this.mdcFoundation.handleMenuOpened())}onClosed(){this.mdcFoundation&&(this.menuOpen=!1,this.mdcFoundation.handleMenuClosed())}setFormData(t){this.name&&null!==this.selected&&t.append(this.name,this.value)}async layout(t=!0){this.mdcFoundation&&this.mdcFoundation.layout(),await this.updateComplete;const e=this.menuElement;e&&e.layout(t);const i=this.labelElement;if(!i)return void(this.outlineOpen=!1);const n=!!this.label&&!!this.value;if(i.floatingLabelFoundation.float(n),!this.outlined)return;this.outlineOpen=n,await this.updateComplete;const o=i.floatingLabelFoundation.getWidth();this.outlineOpen&&(this.outlineWidth=o)}async layoutOptions(){this.mdcFoundation&&this.mdcFoundation.layoutOptions()}}o([Mt(".mdc-select")],Oi.prototype,"mdcRoot",void 0),o([Mt(".formElement")],Oi.prototype,"formElement",void 0),o([Mt("slot")],Oi.prototype,"slotElement",void 0),o([Mt("select")],Oi.prototype,"nativeSelectElement",void 0),o([Mt("input")],Oi.prototype,"nativeInputElement",void 0),o([Mt(".mdc-line-ripple")],Oi.prototype,"lineRippleElement",void 0),o([Mt(".mdc-floating-label")],Oi.prototype,"labelElement",void 0),o([Mt("mwc-notched-outline")],Oi.prototype,"outlineElement",void 0),o([Mt(".mdc-menu")],Oi.prototype,"menuElement",void 0),o([Mt(".mdc-select__anchor")],Oi.prototype,"anchorElement",void 0),o([At({type:Boolean,attribute:"disabled",reflect:!0}),ve((function(t){this.mdcFoundation&&this.mdcFoundation.setDisabled(t)}))],Oi.prototype,"disabled",void 0),o([At({type:Boolean}),ve((function(t,e){void 0!==e&&this.outlined!==e&&this.layout(!1)}))],Oi.prototype,"outlined",void 0),o([At({type:String}),ve((function(t,e){void 0!==e&&this.label!==e&&this.layout(!1)}))],Oi.prototype,"label",void 0),o([Rt()],Oi.prototype,"outlineOpen",void 0),o([Rt()],Oi.prototype,"outlineWidth",void 0),o([At({type:String}),ve((function(t){if(this.mdcFoundation){const e=null===this.selected&&!!t,i=this.selected&&this.selected.value!==t;(e||i)&&this.selectByValue(t),this.reportValidity()}}))],Oi.prototype,"value",void 0),o([At()],Oi.prototype,"name",void 0),o([Rt()],Oi.prototype,"selectedText",void 0),o([At({type:String})],Oi.prototype,"icon",void 0),o([Rt()],Oi.prototype,"menuOpen",void 0),o([At({type:String})],Oi.prototype,"helper",void 0),o([At({type:Boolean})],Oi.prototype,"validateOnInitialRender",void 0),o([At({type:String})],Oi.prototype,"validationMessage",void 0),o([At({type:Boolean})],Oi.prototype,"required",void 0),o([At({type:Boolean})],Oi.prototype,"naturalMenuWidth",void 0),o([Rt()],Oi.prototype,"isUiValid",void 0),o([At({type:Boolean})],Oi.prototype,"fixedMenuPosition",void 0),o([Dt({capture:!0})],Oi.prototype,"handleTypeahead",null); -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -const Ci=(t,e)=>t-e,Si=["input","button","textarea","select"];function Ti(t){return t instanceof Set}const Ii=t=>{const e=t===ii.UNSET_INDEX?new Set:t;return Ti(e)?new Set(e):new Set([e])};class Ai extends ee{constructor(t){super(Object.assign(Object.assign({},Ai.defaultAdapter),t)),this.isMulti_=!1,this.wrapFocus_=!1,this.isVertical_=!0,this.selectedIndex_=ii.UNSET_INDEX,this.focusedItemIndex_=ii.UNSET_INDEX,this.useActivatedClass_=!1,this.ariaCurrentAttrValue_=null}static get strings(){return ei}static get numbers(){return ii}static get defaultAdapter(){return{focusItemAtIndex:()=>{},getFocusedElementIndex:()=>0,getListItemCount:()=>0,isFocusInsideList:()=>!1,isRootFocused:()=>!1,notifyAction:()=>{},notifySelected:()=>{},getSelectedStateForElementIndex:()=>!1,setDisabledStateForElementIndex:()=>{},getDisabledStateForElementIndex:()=>!1,setSelectedStateForElementIndex:()=>{},setActivatedStateForElementIndex:()=>{},setTabIndexForElementIndex:()=>{},setAttributeForElementIndex:()=>{},getAttributeForElementIndex:()=>null}}setWrapFocus(t){this.wrapFocus_=t}setMulti(t){this.isMulti_=t;const e=this.selectedIndex_;if(t){if(!Ti(e)){const t=e===ii.UNSET_INDEX;this.selectedIndex_=t?new Set:new Set([e])}}else if(Ti(e))if(e.size){const t=Array.from(e).sort(Ci);this.selectedIndex_=t[0]}else this.selectedIndex_=ii.UNSET_INDEX}setVerticalOrientation(t){this.isVertical_=t}setUseActivatedClass(t){this.useActivatedClass_=t}getSelectedIndex(){return this.selectedIndex_}setSelectedIndex(t){this.isIndexValid_(t)&&(this.isMulti_?this.setMultiSelectionAtIndex_(Ii(t)):this.setSingleSelectionAtIndex_(t))}handleFocusIn(t,e){e>=0&&this.adapter.setTabIndexForElementIndex(e,0)}handleFocusOut(t,e){e>=0&&this.adapter.setTabIndexForElementIndex(e,-1),setTimeout((()=>{this.adapter.isFocusInsideList()||this.setTabindexToFirstSelectedItem_()}),0)}handleKeydown(t,e,i){const n="ArrowLeft"===qe(t),o="ArrowUp"===qe(t),r="ArrowRight"===qe(t),s="ArrowDown"===qe(t),a="Home"===qe(t),d="End"===qe(t),l="Enter"===qe(t),c="Spacebar"===qe(t);if(this.adapter.isRootFocused())return void(o||d?(t.preventDefault(),this.focusLastElement()):(s||a)&&(t.preventDefault(),this.focusFirstElement()));let h,u=this.adapter.getFocusedElementIndex();if(!(-1===u&&(u=i,u<0))){if(this.isVertical_&&s||!this.isVertical_&&r)this.preventDefaultEvent(t),h=this.focusNextElement(u);else if(this.isVertical_&&o||!this.isVertical_&&n)this.preventDefaultEvent(t),h=this.focusPrevElement(u);else if(a)this.preventDefaultEvent(t),h=this.focusFirstElement();else if(d)this.preventDefaultEvent(t),h=this.focusLastElement();else if((l||c)&&e){const e=t.target;if(e&&"A"===e.tagName&&l)return;this.preventDefaultEvent(t),this.setSelectedIndexOnAction_(u,!0)}this.focusedItemIndex_=u,void 0!==h&&(this.setTabindexAtIndex_(h),this.focusedItemIndex_=h)}}handleSingleSelection(t,e,i){t!==ii.UNSET_INDEX&&(this.setSelectedIndexOnAction_(t,e,i),this.setTabindexAtIndex_(t),this.focusedItemIndex_=t)}focusNextElement(t){let e=t+1;if(e>=this.adapter.getListItemCount()){if(!this.wrapFocus_)return t;e=0}return this.adapter.focusItemAtIndex(e),e}focusPrevElement(t){let e=t-1;if(e<0){if(!this.wrapFocus_)return t;e=this.adapter.getListItemCount()-1}return this.adapter.focusItemAtIndex(e),e}focusFirstElement(){return this.adapter.focusItemAtIndex(0),0}focusLastElement(){const t=this.adapter.getListItemCount()-1;return this.adapter.focusItemAtIndex(t),t}setEnabled(t,e){this.isIndexValid_(t)&&this.adapter.setDisabledStateForElementIndex(t,!e)}preventDefaultEvent(t){const e=`${t.target.tagName}`.toLowerCase();-1===Si.indexOf(e)&&t.preventDefault()}setSingleSelectionAtIndex_(t,e=!0){this.selectedIndex_!==t&&(this.selectedIndex_!==ii.UNSET_INDEX&&(this.adapter.setSelectedStateForElementIndex(this.selectedIndex_,!1),this.useActivatedClass_&&this.adapter.setActivatedStateForElementIndex(this.selectedIndex_,!1)),e&&this.adapter.setSelectedStateForElementIndex(t,!0),this.useActivatedClass_&&this.adapter.setActivatedStateForElementIndex(t,!0),this.setAriaForSingleSelectionAtIndex_(t),this.selectedIndex_=t,this.adapter.notifySelected(t))}setMultiSelectionAtIndex_(t,e=!0){const i=((t,e)=>{const i=Array.from(t),n=Array.from(e),o={added:[],removed:[]},r=i.sort(Ci),s=n.sort(Ci);let a=0,d=0;for(;a=0&&this.focusedItemIndex_!==t&&this.adapter.setTabIndexForElementIndex(this.focusedItemIndex_,-1),this.adapter.setTabIndexForElementIndex(t,0)}setTabindexToFirstSelectedItem_(){let t=0;"number"==typeof this.selectedIndex_&&this.selectedIndex_!==ii.UNSET_INDEX?t=this.selectedIndex_:Ti(this.selectedIndex_)&&this.selectedIndex_.size>0&&(t=Math.min(...this.selectedIndex_)),this.setTabindexAtIndex_(t)}isIndexValid_(t){if(t instanceof Set){if(!this.isMulti_)throw new Error("MDCListFoundation: Array of index is only supported for checkbox based list");if(0===t.size)return!0;{let e=!1;for(const i of t)if(e=this.isIndexInRange_(i),e)break;return e}}if("number"==typeof t){if(this.isMulti_)throw new Error("MDCListFoundation: Expected array of index for checkbox based list but got number: "+t);return t===ii.UNSET_INDEX||this.isIndexInRange_(t)}return!1}isIndexInRange_(t){const e=this.adapter.getListItemCount();return t>=0&&tt.hasAttribute("mwc-list-item");function Pi(){const t=this.itemsReadyResolver;this.itemsReady=new Promise((t=>this.itemsReadyResolver=t)),t()}class Di extends he{constructor(){super(),this.mdcAdapter=null,this.mdcFoundationClass=Ai,this.activatable=!1,this.multi=!1,this.wrapFocus=!1,this.itemRoles=null,this.innerRole=null,this.innerAriaLabel=null,this.rootTabbable=!1,this.previousTabindex=null,this.noninteractive=!1,this.itemsReadyResolver=()=>{},this.itemsReady=Promise.resolve([]),this.items_=[];const t=function(t,e=50){let i;return function(n=!0){clearTimeout(i),i=setTimeout((()=>{t(n)}),e)}}(this.layout.bind(this));this.debouncedLayout=(e=!0)=>{Pi.call(this),t(e)}}async getUpdateComplete(){const t=await super.getUpdateComplete();return await this.itemsReady,t}get items(){return this.items_}updateItems(){var t;const e=null!==(t=this.assignedElements)&&void 0!==t?t:[],i=[];for(const t of e)Ri(t)&&(i.push(t),t._managingList=this),t.hasAttribute("divider")&&!t.hasAttribute("role")&&t.setAttribute("role","separator");this.items_=i;const n=new Set;if(this.items_.forEach(((t,e)=>{this.itemRoles?t.setAttribute("role",this.itemRoles):t.removeAttribute("role"),t.selected&&n.add(e)})),this.multi)this.select(n);else{const t=n.size?n.entries().next().value[1]:-1;this.select(t)}const o=new Event("items-updated",{bubbles:!0,composed:!0});this.dispatchEvent(o)}get selected(){const t=this.index;if(!Ti(t))return-1===t?null:this.items[t];const e=[];for(const i of t)e.push(this.items[i]);return e}get index(){return this.mdcFoundation?this.mdcFoundation.getSelectedIndex():-1}render(){const t=null===this.innerRole?void 0:this.innerRole,e=null===this.innerAriaLabel?void 0:this.innerAriaLabel,i=this.rootTabbable?"0":"-1";return $` - -
    - - ${this.renderPlaceholder()} -
- `}renderPlaceholder(){var t;const e=null!==(t=this.assignedElements)&&void 0!==t?t:[];return void 0!==this.emptyMessage&&0===e.length?$` - ${this.emptyMessage} - `:null}firstUpdated(){super.firstUpdated(),this.items.length||(this.mdcFoundation.setMulti(this.multi),this.layout())}onFocusIn(t){if(this.mdcFoundation&&this.mdcRoot){const e=this.getIndexOfTarget(t);this.mdcFoundation.handleFocusIn(t,e)}}onFocusOut(t){if(this.mdcFoundation&&this.mdcRoot){const e=this.getIndexOfTarget(t);this.mdcFoundation.handleFocusOut(t,e)}}onKeydown(t){if(this.mdcFoundation&&this.mdcRoot){const e=this.getIndexOfTarget(t),i=t.target,n=Ri(i);this.mdcFoundation.handleKeydown(t,n,e)}}onRequestSelected(t){if(this.mdcFoundation){let e=this.getIndexOfTarget(t);if(-1===e&&(this.layout(),e=this.getIndexOfTarget(t),-1===e))return;if(this.items[e].disabled)return;const i=t.detail.selected,n=t.detail.source;this.mdcFoundation.handleSingleSelection(e,"interaction"===n,i),t.stopPropagation()}}getIndexOfTarget(t){const e=this.items,i=t.composedPath();for(const t of i){let i=-1;if(re(t)&&Ri(t)&&(i=e.indexOf(t)),-1!==i)return i}return-1}createAdapter(){return this.mdcAdapter={getListItemCount:()=>this.mdcRoot?this.items.length:0,getFocusedElementIndex:this.getFocusedItemIndex,getAttributeForElementIndex:(t,e)=>{if(!this.mdcRoot)return"";const i=this.items[t];return i?i.getAttribute(e):""},setAttributeForElementIndex:(t,e,i)=>{if(!this.mdcRoot)return;const n=this.items[t];n&&n.setAttribute(e,i)},focusItemAtIndex:t=>{const e=this.items[t];e&&e.focus()},setTabIndexForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.tabindex=e)},notifyAction:t=>{const e={bubbles:!0,composed:!0};e.detail={index:t};const i=new CustomEvent("action",e);this.dispatchEvent(i)},notifySelected:(t,e)=>{const i={bubbles:!0,composed:!0};i.detail={index:t,diff:e};const n=new CustomEvent("selected",i);this.dispatchEvent(n)},isFocusInsideList:()=>ce(this),isRootFocused:()=>{const t=this.mdcRoot;return t.getRootNode().activeElement===t},setDisabledStateForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.disabled=e)},getDisabledStateForElementIndex:t=>{const e=this.items[t];return!!e&&e.disabled},setSelectedStateForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.selected=e)},getSelectedStateForElementIndex:t=>{const e=this.items[t];return!!e&&e.selected},setActivatedStateForElementIndex:(t,e)=>{const i=this.items[t];i&&(i.activated=e)}},this.mdcAdapter}selectUi(t,e=!1){const i=this.items[t];i&&(i.selected=!0,i.activated=e)}deselectUi(t){const e=this.items[t];e&&(e.selected=!1,e.activated=!1)}select(t){this.mdcFoundation&&this.mdcFoundation.setSelectedIndex(t)}toggle(t,e){this.multi&&this.mdcFoundation.toggleMultiAtIndex(t,e)}onListItemConnected(t){const e=t.target;this.layout(-1===this.items.indexOf(e))}layout(t=!0){t&&this.updateItems();const e=this.items[0];for(const t of this.items)t.tabindex=-1;e&&(this.noninteractive?this.previousTabindex||(this.previousTabindex=e):e.tabindex=0),this.itemsReadyResolver()}getFocusedItemIndex(){if(!this.mdcRoot)return-1;if(!this.items.length)return-1;const t=le();if(!t.length)return-1;for(let e=t.length-1;e>=0;e--){const i=t[e];if(Ri(i))return this.items.indexOf(i)}return-1}focusItemAtIndex(t){for(const t of this.items)if(0===t.tabindex){t.tabindex=-1;break}this.items[t].tabindex=0,this.items[t].focus()}focus(){const t=this.mdcRoot;t&&t.focus()}blur(){const t=this.mdcRoot;t&&t.blur()}}o([At({type:String})],Di.prototype,"emptyMessage",void 0),o([Mt(".mdc-deprecated-list")],Di.prototype,"mdcRoot",void 0),o([Lt("",!0,"*")],Di.prototype,"assignedElements",void 0),o([Lt("",!0,'[tabindex="0"]')],Di.prototype,"tabbableElements",void 0),o([At({type:Boolean}),ve((function(t){this.mdcFoundation&&this.mdcFoundation.setUseActivatedClass(t)}))],Di.prototype,"activatable",void 0),o([At({type:Boolean}),ve((function(t,e){this.mdcFoundation&&this.mdcFoundation.setMulti(t),void 0!==e&&this.layout()}))],Di.prototype,"multi",void 0),o([At({type:Boolean}),ve((function(t){this.mdcFoundation&&this.mdcFoundation.setWrapFocus(t)}))],Di.prototype,"wrapFocus",void 0),o([At({type:String}),ve((function(t,e){void 0!==e&&this.updateItems()}))],Di.prototype,"itemRoles",void 0),o([At({type:String})],Di.prototype,"innerRole",void 0),o([At({type:String})],Di.prototype,"innerAriaLabel",void 0),o([At({type:Boolean})],Di.prototype,"rootTabbable",void 0),o([At({type:Boolean,reflect:!0}),ve((function(t){var e,i;if(t){const t=null!==(i=null===(e=this.tabbableElements)||void 0===e?void 0:e[0])&&void 0!==i?i:null;this.previousTabindex=t,t&&t.setAttribute("tabindex","-1")}else!t&&this.previousTabindex&&(this.previousTabindex.setAttribute("tabindex","0"),this.previousTabindex=null)}))],Di.prototype,"noninteractive",void 0); -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class Mi{constructor(t){this.startPress=e=>{t().then((t=>{t&&t.startPress(e)}))},this.endPress=()=>{t().then((t=>{t&&t.endPress()}))},this.startFocus=()=>{t().then((t=>{t&&t.startFocus()}))},this.endFocus=()=>{t().then((t=>{t&&t.endFocus()}))},this.startHover=()=>{t().then((t=>{t&&t.startHover()}))},this.endHover=()=>{t().then((t=>{t&&t.endHover()}))}}} -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class Fi extends Ot{constructor(){super(...arguments),this.value="",this.group=null,this.tabindex=-1,this.disabled=!1,this.twoline=!1,this.activated=!1,this.graphic=null,this.multipleGraphics=!1,this.hasMeta=!1,this.noninteractive=!1,this.selected=!1,this.shouldRenderRipple=!1,this._managingList=null,this.boundOnClick=this.onClick.bind(this),this._firstChanged=!0,this._skipPropRequest=!1,this.rippleHandlers=new Mi((()=>(this.shouldRenderRipple=!0,this.ripple))),this.listeners=[{target:this,eventNames:["click"],cb:()=>{this.onClick()}},{target:this,eventNames:["mouseenter"],cb:this.rippleHandlers.startHover},{target:this,eventNames:["mouseleave"],cb:this.rippleHandlers.endHover},{target:this,eventNames:["focus"],cb:this.rippleHandlers.startFocus},{target:this,eventNames:["blur"],cb:this.rippleHandlers.endFocus},{target:this,eventNames:["mousedown","touchstart"],cb:t=>{const e=t.type;this.onDown("mousedown"===e?"mouseup":"touchend",t)}}]}get text(){const t=this.textContent;return t?t.trim():""}render(){const t=this.renderText(),e=this.graphic?this.renderGraphic():$``,i=this.hasMeta?this.renderMeta():$``;return $` - ${this.renderRipple()} - ${e} - ${t} - ${i}`}renderRipple(){return this.shouldRenderRipple?$` - - `:this.activated?$`
`:""}renderGraphic(){const t={multi:this.multipleGraphics};return $` - - - `}renderMeta(){return $` - - - `}renderText(){const t=this.twoline?this.renderTwoline():this.renderSingleLine();return $` - - ${t} - `}renderSingleLine(){return $``}renderTwoline(){return $` - - - - - - - `}onClick(){this.fireRequestSelected(!this.selected,"interaction")}onDown(t,e){const i=()=>{window.removeEventListener(t,i),this.rippleHandlers.endPress()};window.addEventListener(t,i),this.rippleHandlers.startPress(e)}fireRequestSelected(t,e){if(this.noninteractive)return;const i=new CustomEvent("request-selected",{bubbles:!0,composed:!0,detail:{source:e,selected:t}});this.dispatchEvent(i)}connectedCallback(){super.connectedCallback(),this.noninteractive||this.setAttribute("mwc-list-item","");for(const t of this.listeners)for(const e of t.eventNames)t.target.addEventListener(e,t.cb,{passive:!0})}disconnectedCallback(){super.disconnectedCallback();for(const t of this.listeners)for(const e of t.eventNames)t.target.removeEventListener(e,t.cb);this._managingList&&(this._managingList.debouncedLayout?this._managingList.debouncedLayout(!0):this._managingList.layout(!0))}firstUpdated(){const t=new Event("list-item-rendered",{bubbles:!0,composed:!0});this.dispatchEvent(t)}}o([Mt("slot")],Fi.prototype,"slotElement",void 0),o([Ft("mwc-ripple")],Fi.prototype,"ripple",void 0),o([At({type:String})],Fi.prototype,"value",void 0),o([At({type:String,reflect:!0})],Fi.prototype,"group",void 0),o([At({type:Number,reflect:!0})],Fi.prototype,"tabindex",void 0),o([At({type:Boolean,reflect:!0}),ve((function(t){t?this.setAttribute("aria-disabled","true"):this.setAttribute("aria-disabled","false")}))],Fi.prototype,"disabled",void 0),o([At({type:Boolean,reflect:!0})],Fi.prototype,"twoline",void 0),o([At({type:Boolean,reflect:!0})],Fi.prototype,"activated",void 0),o([At({type:String,reflect:!0})],Fi.prototype,"graphic",void 0),o([At({type:Boolean})],Fi.prototype,"multipleGraphics",void 0),o([At({type:Boolean})],Fi.prototype,"hasMeta",void 0),o([At({type:Boolean,reflect:!0}),ve((function(t){t?(this.removeAttribute("aria-checked"),this.removeAttribute("mwc-list-item"),this.selected=!1,this.activated=!1,this.tabIndex=-1):this.setAttribute("mwc-list-item","")}))],Fi.prototype,"noninteractive",void 0),o([At({type:Boolean,reflect:!0}),ve((function(t){const e=this.getAttribute("role"),i="gridcell"===e||"option"===e||"row"===e||"tab"===e;i&&t?this.setAttribute("aria-selected","true"):i&&this.setAttribute("aria-selected","false"),this._firstChanged?this._firstChanged=!1:this._skipPropRequest||this.fireRequestSelected(t,"property")}))],Fi.prototype,"selected",void 0),o([Rt()],Fi.prototype,"shouldRenderRipple",void 0),o([Rt()],Fi.prototype,"_managingList",void 0); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Ni,Bi={MENU_SELECTED_LIST_ITEM:"mdc-menu-item--selected",MENU_SELECTION_GROUP:"mdc-menu__selection-group",ROOT:"mdc-menu"},Li={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"},zi={FOCUS_ROOT_INDEX:-1};!function(t){t[t.NONE=0]="NONE",t[t.LIST_ROOT=1]="LIST_ROOT",t[t.FIRST_ITEM=2]="FIRST_ITEM",t[t.LAST_ITEM=3]="LAST_ITEM"}(Ni||(Ni={})); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var ji=function(t){function e(i){var o=t.call(this,n(n({},e.defaultAdapter),i))||this;return o.isSurfaceOpen=!1,o.isQuickOpen=!1,o.isHoistedElement=!1,o.isFixedPosition=!1,o.isHorizontallyCenteredOnViewport=!1,o.maxHeight=0,o.openBottomBias=0,o.openAnimationEndTimerId=0,o.closeAnimationEndTimerId=0,o.animationRequestId=0,o.anchorCorner=mi.TOP_START,o.originCorner=mi.TOP_START,o.anchorMargin={top:0,right:0,bottom:0,left:0},o.position={x:0,y:0},o}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return vi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return gi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return yi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"Corner",{get:function(){return mi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"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(){},notifyClosing:function(){},notifyOpen:function(){},notifyOpening:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t=e.cssClasses,i=t.ROOT,n=t.OPEN;if(!this.adapter.hasClass(i))throw new Error(i+" class required in root element.");this.adapter.hasClass(n)&&(this.isSurfaceOpen=!0)},e.prototype.destroy=function(){clearTimeout(this.openAnimationEndTimerId),clearTimeout(this.closeAnimationEndTimerId),cancelAnimationFrame(this.animationRequestId)},e.prototype.setAnchorCorner=function(t){this.anchorCorner=t},e.prototype.flipCornerHorizontally=function(){this.originCorner=this.originCorner^fi.RIGHT},e.prototype.setAnchorMargin=function(t){this.anchorMargin.top=t.top||0,this.anchorMargin.right=t.right||0,this.anchorMargin.bottom=t.bottom||0,this.anchorMargin.left=t.left||0},e.prototype.setIsHoisted=function(t){this.isHoistedElement=t},e.prototype.setFixedPosition=function(t){this.isFixedPosition=t},e.prototype.isFixed=function(){return this.isFixedPosition},e.prototype.setAbsolutePosition=function(t,e){this.position.x=this.isFinite(t)?t:0,this.position.y=this.isFinite(e)?e:0},e.prototype.setIsHorizontallyCenteredOnViewport=function(t){this.isHorizontallyCenteredOnViewport=t},e.prototype.setQuickOpen=function(t){this.isQuickOpen=t},e.prototype.setMaxHeight=function(t){this.maxHeight=t},e.prototype.setOpenBottomBias=function(t){this.openBottomBias=t},e.prototype.isOpen=function(){return this.isSurfaceOpen},e.prototype.open=function(){var t=this;this.isSurfaceOpen||(this.adapter.notifyOpening(),this.adapter.saveFocus(),this.isQuickOpen?(this.isSurfaceOpen=!0,this.adapter.addClass(e.cssClasses.OPEN),this.dimensions=this.adapter.getInnerDimensions(),this.autoposition(),this.adapter.notifyOpen()):(this.adapter.addClass(e.cssClasses.ANIMATING_OPEN),this.animationRequestId=requestAnimationFrame((function(){t.dimensions=t.adapter.getInnerDimensions(),t.autoposition(),t.adapter.addClass(e.cssClasses.OPEN),t.openAnimationEndTimerId=setTimeout((function(){t.openAnimationEndTimerId=0,t.adapter.removeClass(e.cssClasses.ANIMATING_OPEN),t.adapter.notifyOpen()}),yi.TRANSITION_OPEN_DURATION)})),this.isSurfaceOpen=!0))},e.prototype.close=function(t){var i=this;if(void 0===t&&(t=!1),this.isSurfaceOpen){if(this.adapter.notifyClosing(),this.isQuickOpen)return this.isSurfaceOpen=!1,t||this.maybeRestoreFocus(),this.adapter.removeClass(e.cssClasses.OPEN),this.adapter.removeClass(e.cssClasses.IS_OPEN_BELOW),void this.adapter.notifyClose();this.adapter.addClass(e.cssClasses.ANIMATING_CLOSED),requestAnimationFrame((function(){i.adapter.removeClass(e.cssClasses.OPEN),i.adapter.removeClass(e.cssClasses.IS_OPEN_BELOW),i.closeAnimationEndTimerId=setTimeout((function(){i.closeAnimationEndTimerId=0,i.adapter.removeClass(e.cssClasses.ANIMATING_CLOSED),i.adapter.notifyClose()}),yi.TRANSITION_CLOSE_DURATION)})),this.isSurfaceOpen=!1,t||this.maybeRestoreFocus()}},e.prototype.handleBodyClick=function(t){var e=t.target;this.adapter.isElementInContainer(e)||this.close()},e.prototype.handleKeydown=function(t){var e=t.keyCode;("Escape"===t.key||27===e)&&this.close()},e.prototype.autoposition=function(){var t;this.measurements=this.getAutoLayoutmeasurements();var i=this.getoriginCorner(),n=this.getMenuSurfaceMaxHeight(i),o=this.hasBit(i,fi.BOTTOM)?"bottom":"top",r=this.hasBit(i,fi.RIGHT)?"right":"left",s=this.getHorizontalOriginOffset(i),a=this.getVerticalOriginOffset(i),d=this.measurements,l=d.anchorSize,c=d.surfaceSize,h=((t={})[r]=s,t[o]=a,t);l.width/c.width>yi.ANCHOR_TO_MENU_SURFACE_WIDTH_RATIO&&(r="center"),(this.isHoistedElement||this.isFixedPosition)&&this.adjustPositionForHoistedElement(h),this.adapter.setTransformOrigin(r+" "+o),this.adapter.setPosition(h),this.adapter.setMaxHeight(n?n+"px":""),this.hasBit(i,fi.BOTTOM)||this.adapter.addClass(e.cssClasses.IS_OPEN_BELOW)},e.prototype.getAutoLayoutmeasurements=function(){var t=this.adapter.getAnchorDimensions(),e=this.adapter.getBodyDimensions(),i=this.adapter.getWindowDimensions(),n=this.adapter.getWindowScroll();return t||(t={top:this.position.y,right:this.position.x,bottom:this.position.y,left:this.position.x,width:0,height:0}),{anchorSize:t,bodySize:e,surfaceSize:this.dimensions,viewportDistance:{top:t.top,right:i.width-t.right,bottom:i.height-t.bottom,left:t.left},viewportSize:i,windowScroll:n}},e.prototype.getoriginCorner=function(){var t,i,n=this.originCorner,o=this.measurements,r=o.viewportDistance,s=o.anchorSize,a=o.surfaceSize,d=e.numbers.MARGIN_TO_EDGE;this.hasBit(this.anchorCorner,fi.BOTTOM)?(t=r.top-d+this.anchorMargin.bottom,i=r.bottom-d-this.anchorMargin.bottom):(t=r.top-d+this.anchorMargin.top,i=r.bottom-d+s.height-this.anchorMargin.top),!(i-a.height>0)&&t>i+this.openBottomBias&&(n=this.setBit(n,fi.BOTTOM));var l,c,h=this.adapter.isRtl(),u=this.hasBit(this.anchorCorner,fi.FLIP_RTL),p=this.hasBit(this.anchorCorner,fi.RIGHT)||this.hasBit(n,fi.RIGHT),f=!1;(f=h&&u?!p:p)?(l=r.left+s.width+this.anchorMargin.right,c=r.right-this.anchorMargin.right):(l=r.left+this.anchorMargin.left,c=r.right+s.width-this.anchorMargin.left);var m=l-a.width>0,v=c-a.width>0,g=this.hasBit(n,fi.FLIP_RTL)&&this.hasBit(n,fi.RIGHT);return v&&g&&h||!m&&g?n=this.unsetBit(n,fi.RIGHT):(m&&f&&h||m&&!f&&p||!v&&l>=c)&&(n=this.setBit(n,fi.RIGHT)),n},e.prototype.getMenuSurfaceMaxHeight=function(t){if(this.maxHeight>0)return this.maxHeight;var i=this.measurements.viewportDistance,n=0,o=this.hasBit(t,fi.BOTTOM),r=this.hasBit(this.anchorCorner,fi.BOTTOM),s=e.numbers.MARGIN_TO_EDGE;return o?(n=i.top+this.anchorMargin.top-s,r||(n+=this.measurements.anchorSize.height)):(n=i.bottom-this.anchorMargin.bottom+this.measurements.anchorSize.height-s,r&&(n-=this.measurements.anchorSize.height)),n},e.prototype.getHorizontalOriginOffset=function(t){var e=this.measurements.anchorSize,i=this.hasBit(t,fi.RIGHT),n=this.hasBit(this.anchorCorner,fi.RIGHT);if(i){var o=n?e.width-this.anchorMargin.left:this.anchorMargin.right;return this.isHoistedElement||this.isFixedPosition?o-(this.measurements.viewportSize.width-this.measurements.bodySize.width):o}return n?e.width-this.anchorMargin.right:this.anchorMargin.left},e.prototype.getVerticalOriginOffset=function(t){var e=this.measurements.anchorSize,i=this.hasBit(t,fi.BOTTOM),n=this.hasBit(this.anchorCorner,fi.BOTTOM);return i?n?e.height-this.anchorMargin.top:-this.anchorMargin.bottom:n?e.height+this.anchorMargin.bottom:this.anchorMargin.top},e.prototype.adjustPositionForHoistedElement=function(t){var e,i,n=this.measurements,o=n.windowScroll,s=n.viewportDistance,a=n.surfaceSize,d=n.viewportSize,l=Object.keys(t);try{for(var c=r(l),h=c.next();!h.done;h=c.next()){var u=h.value,p=t[u]||0;!this.isHorizontallyCenteredOnViewport||"left"!==u&&"right"!==u?(p+=s[u],this.isFixedPosition||("top"===u?p+=o.y:"bottom"===u?p-=o.y:"left"===u?p+=o.x:p-=o.x),t[u]=p):t[u]=(d.width-a.width)/2}}catch(t){e={error:t}}finally{try{h&&!h.done&&(i=c.return)&&i.call(c)}finally{if(e)throw e.error}}},e.prototype.maybeRestoreFocus=function(){var t=this,e=this.adapter.isFocused(),i=this.adapter.getOwnerDocument?this.adapter.getOwnerDocument():document,n=i.activeElement&&this.adapter.isElementInContainer(i.activeElement);(e||n)&&setTimeout((function(){t.adapter.restoreFocus()}),yi.TOUCH_EVENT_WAIT_MS)},e.prototype.hasBit=function(t,e){return Boolean(t&e)},e.prototype.setBit=function(t,e){return t|e},e.prototype.unsetBit=function(t,e){return t^e},e.prototype.isFinite=function(t){return"number"==typeof t&&isFinite(t)},e}(ee),Hi=function(t){function e(i){var o=t.call(this,n(n({},e.defaultAdapter),i))||this;return o.closeAnimationEndTimerId=0,o.defaultFocusState=Ni.LIST_ROOT,o.selectedIndex=-1,o}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Bi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Li},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return zi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"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}),e.prototype.destroy=function(){this.closeAnimationEndTimerId&&clearTimeout(this.closeAnimationEndTimerId),this.adapter.closeSurface()},e.prototype.handleKeydown=function(t){var e=t.key,i=t.keyCode;("Tab"===e||9===i)&&this.adapter.closeSurface(!0)},e.prototype.handleItemAction=function(t){var e=this,i=this.adapter.getElementIndex(t);if(!(i<0)){this.adapter.notifySelected({index:i});var n="true"===this.adapter.getAttributeFromElementAtIndex(i,Li.SKIP_RESTORE_FOCUS);this.adapter.closeSurface(n),this.closeAnimationEndTimerId=setTimeout((function(){var i=e.adapter.getElementIndex(t);i>=0&&e.adapter.isSelectableItemAtIndex(i)&&e.setSelectedIndex(i)}),ji.numbers.TRANSITION_CLOSE_DURATION)}},e.prototype.handleMenuSurfaceOpened=function(){switch(this.defaultFocusState){case Ni.FIRST_ITEM:this.adapter.focusItemAtIndex(0);break;case Ni.LAST_ITEM:this.adapter.focusItemAtIndex(this.adapter.getMenuItemCount()-1);break;case Ni.NONE:break;default:this.adapter.focusListRoot()}},e.prototype.setDefaultFocusState=function(t){this.defaultFocusState=t},e.prototype.getSelectedIndex=function(){return this.selectedIndex},e.prototype.setSelectedIndex=function(t){if(this.validatedIndex(t),!this.adapter.isSelectableItemAtIndex(t))throw new Error("MDCMenuFoundation: No selection group at specified index.");var e=this.adapter.getSelectedSiblingOfItemAtIndex(t);e>=0&&(this.adapter.removeAttributeFromElementAtIndex(e,Li.ARIA_CHECKED_ATTR),this.adapter.removeClassFromElementAtIndex(e,Bi.MENU_SELECTED_LIST_ITEM)),this.adapter.addClassToElementAtIndex(t,Bi.MENU_SELECTED_LIST_ITEM),this.adapter.addAttributeToElementAtIndex(t,Li.ARIA_CHECKED_ATTR,"true"),this.selectedIndex=t},e.prototype.setEnabled=function(t,e){this.validatedIndex(t),e?(this.adapter.removeClassFromElementAtIndex(t,Ge),this.adapter.addAttributeToElementAtIndex(t,Li.ARIA_DISABLED_ATTR,"false")):(this.adapter.addClassToElementAtIndex(t,Ge),this.adapter.addAttributeToElementAtIndex(t,Li.ARIA_DISABLED_ATTR,"true"))},e.prototype.validatedIndex=function(t){var e=this.adapter.getMenuItemCount();if(!(t>=0&&t - ${this.renderList()} - `}getSurfaceClasses(){return{"mdc-menu":!0,"mdc-menu-surface":!0}}renderList(){const t="menu"===this.innerRole?"menuitem":"option",e=this.renderListClasses();return $` - - - `}renderListClasses(){return{"mdc-deprecated-list":!0}}createAdapter(){return{addClassToElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return;const n=i.items[t];n&&("mdc-menu-item--selected"===e?this.forceGroupSelection&&!n.selected&&i.toggle(t,!0):n.classList.add(e))},removeClassFromElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return;const n=i.items[t];n&&("mdc-menu-item--selected"===e?n.selected&&i.toggle(t,!1):n.classList.remove(e))},addAttributeToElementAtIndex:(t,e,i)=>{const n=this.listElement;if(!n)return;const o=n.items[t];o&&o.setAttribute(e,i)},removeAttributeFromElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return;const n=i.items[t];n&&n.removeAttribute(e)},getAttributeFromElementAtIndex:(t,e)=>{const i=this.listElement;if(!i)return null;const n=i.items[t];return n?n.getAttribute(e):null},elementContainsClass:(t,e)=>t.classList.contains(e),closeSurface:()=>{this.open=!1},getElementIndex:t=>{const e=this.listElement;return e?e.items.indexOf(t):-1},notifySelected:()=>{},getMenuItemCount:()=>{const t=this.listElement;return t?t.items.length:0},focusItemAtIndex:t=>{const e=this.listElement;if(!e)return;const i=e.items[t];i&&i.focus()},focusListRoot:()=>{this.listElement&&this.listElement.focus()},getSelectedSiblingOfItemAtIndex:t=>{const e=this.listElement;if(!e)return-1;const i=e.items[t];if(!i||!i.group)return-1;for(let n=0;n{const e=this.listElement;if(!e)return!1;const i=e.items[t];return!!i&&i.hasAttribute("group")}}}onKeydown(t){this.mdcFoundation&&this.mdcFoundation.handleKeydown(t)}onAction(t){const e=this.listElement;if(this.mdcFoundation&&e){const i=t.detail.index,n=e.items[i];n&&this.mdcFoundation.handleItemAction(n)}}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 t=this.listElement;t&&(this._listUpdateComplete=t.updateComplete,await this._listUpdateComplete)}select(t){const e=this.listElement;e&&e.select(t)}close(){this.open=!1}show(){this.open=!0}getFocusedItemIndex(){const t=this.listElement;return t?t.getFocusedItemIndex():-1}focusItemAtIndex(t){const e=this.listElement;e&&e.focusItemAtIndex(t)}layout(t=!0){const e=this.listElement;e&&e.layout(t)}}o([Mt(".mdc-menu")],$i.prototype,"mdcRoot",void 0),o([Mt("slot")],$i.prototype,"slotElement",void 0),o([At({type:Object})],$i.prototype,"anchor",void 0),o([At({type:Boolean,reflect:!0})],$i.prototype,"open",void 0),o([At({type:Boolean})],$i.prototype,"quick",void 0),o([At({type:Boolean})],$i.prototype,"wrapFocus",void 0),o([At({type:String})],$i.prototype,"innerRole",void 0),o([At({type:String})],$i.prototype,"innerAriaLabel",void 0),o([At({type:String})],$i.prototype,"corner",void 0),o([At({type:Number})],$i.prototype,"x",void 0),o([At({type:Number})],$i.prototype,"y",void 0),o([At({type:Boolean})],$i.prototype,"absolute",void 0),o([At({type:Boolean})],$i.prototype,"multi",void 0),o([At({type:Boolean})],$i.prototype,"activatable",void 0),o([At({type:Boolean})],$i.prototype,"fixed",void 0),o([At({type:Boolean})],$i.prototype,"forceGroupSelection",void 0),o([At({type:Boolean})],$i.prototype,"fullwidth",void 0),o([At({type:String})],$i.prototype,"menuCorner",void 0),o([At({type:Boolean})],$i.prototype,"stayOpenOnBodyClick",void 0),o([At({type:String}),ve((function(t){this.mdcFoundation&&this.mdcFoundation.setDefaultFocusState(Ni[t])}))],$i.prototype,"defaultFocus",void 0); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Ui="important",Wi=" !"+Ui,Vi=xe(class extends _e{constructor(t){var e;if(super(t),t.type!==ge||"style"!==t.name||(null===(e=t.strings)||void 0===e?void 0:e.length)>2)throw Error("The `styleMap` directive must be used in the `style` attribute and must be the only part in the attribute.")}render(t){return Object.keys(t).reduce(((e,i)=>{const n=t[i];return null==n?e:e+`${i=i.includes("-")?i:i.replace(/(?:^(webkit|moz|ms|o)|)(?=[A-Z])/g,"-$&").toLowerCase()}:${n};`}),"")}update(t,[e]){const{style:i}=t.element;if(void 0===this.ht){this.ht=new Set;for(const t in e)this.ht.add(t);return this.render(e)}this.ht.forEach((t=>{null==e[t]&&(this.ht.delete(t),t.includes("-")?i.removeProperty(t):i[t]="")}));for(const t in e){const n=e[t];if(null!=n){this.ht.add(t);const e="string"==typeof n&&n.endsWith(Wi);t.includes("-")||e?i.setProperty(t,e?n.slice(0,-11):n,e?Ui:""):i[t]=n}}return U}}),qi={TOP_LEFT:mi.TOP_LEFT,TOP_RIGHT:mi.TOP_RIGHT,BOTTOM_LEFT:mi.BOTTOM_LEFT,BOTTOM_RIGHT:mi.BOTTOM_RIGHT,TOP_START:mi.TOP_START,TOP_END:mi.TOP_END,BOTTOM_START:mi.BOTTOM_START,BOTTOM_END:mi.BOTTOM_END}; -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */class Xi extends he{constructor(){super(...arguments),this.mdcFoundationClass=ji,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=mi.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(){return this.renderSurface()}renderSurface(){const t=this.getRootClasses(),e=this.getRootStyles();return $` -
- ${this.renderContent()} -
`}getRootClasses(){return{"mdc-menu-surface":!0,"mdc-menu-surface--fixed":this.fixed,"mdc-menu-surface--fullwidth":this.fullwidth}}getRootStyles(){return{top:this.styleTop,left:this.styleLeft,right:this.styleRight,bottom:this.styleBottom,"max-height":this.styleMaxHeight,"transform-origin":this.styleTransformOrigin}}renderContent(){return $``}createAdapter(){return Object.assign(Object.assign({},se(this.mdcRoot)),{hasAnchor:()=>!!this.anchor,notifyClose:()=>{const t=new CustomEvent("closed",{bubbles:!0,composed:!0});this.open=!1,this.mdcRoot.dispatchEvent(t)},notifyClosing:()=>{const t=new CustomEvent("closing",{bubbles:!0,composed:!0});this.mdcRoot.dispatchEvent(t)},notifyOpen:()=>{const t=new CustomEvent("opened",{bubbles:!0,composed:!0});this.open=!0,this.mdcRoot.dispatchEvent(t)},notifyOpening:()=>{const t=new CustomEvent("opening",{bubbles:!0,composed:!0});this.mdcRoot.dispatchEvent(t)},isElementInContainer:()=>!1,isRtl:()=>!!this.mdcRoot&&"rtl"===getComputedStyle(this.mdcRoot).direction,setTransformOrigin:t=>{this.mdcRoot&&(this.styleTransformOrigin=t)},isFocused:()=>ce(this),saveFocus:()=>{const t=le(),e=t.length;e||(this.previouslyFocused=null),this.previouslyFocused=t[e-1]},restoreFocus:()=>{this.previouslyFocused&&"focus"in this.previouslyFocused&&this.previouslyFocused.focus()},getInnerDimensions:()=>{const t=this.mdcRoot;return t?{width:t.offsetWidth,height:t.offsetHeight}:{width:0,height:0}},getAnchorDimensions:()=>{const t=this.anchor;return t?t.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:t=>{this.mdcRoot&&(this.styleLeft="left"in t?`${t.left}px`:"",this.styleRight="right"in t?`${t.right}px`:"",this.styleTop="top"in t?`${t.top}px`:"",this.styleBottom="bottom"in t?`${t.bottom}px`:"")},setMaxHeight:async t=>{this.mdcRoot&&(this.styleMaxHeight=t,await this.updateComplete,this.styleMaxHeight=`var(--mdc-menu-max-height, ${t})`)}})}onKeydown(t){this.mdcFoundation&&this.mdcFoundation.handleKeydown(t)}onBodyClick(t){if(this.stayOpenOnBodyClick)return;-1===t.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})}onOpenChanged(t,e){this.mdcFoundation&&(t?this.mdcFoundation.open():void 0!==e&&this.mdcFoundation.close())}close(){this.open=!1}show(){this.open=!0}}o([Mt(".mdc-menu-surface")],Xi.prototype,"mdcRoot",void 0),o([Mt("slot")],Xi.prototype,"slotElement",void 0),o([At({type:Boolean}),ve((function(t){this.mdcFoundation&&!this.fixed&&this.mdcFoundation.setIsHoisted(t)}))],Xi.prototype,"absolute",void 0),o([At({type:Boolean})],Xi.prototype,"fullwidth",void 0),o([At({type:Boolean}),ve((function(t){this.mdcFoundation&&!this.absolute&&this.mdcFoundation.setFixedPosition(t)}))],Xi.prototype,"fixed",void 0),o([At({type:Number}),ve((function(t){this.mdcFoundation&&null!==this.y&&null!==t&&(this.mdcFoundation.setAbsolutePosition(t,this.y),this.mdcFoundation.setAnchorMargin({left:t,top:this.y,right:-t,bottom:this.y}))}))],Xi.prototype,"x",void 0),o([At({type:Number}),ve((function(t){this.mdcFoundation&&null!==this.x&&null!==t&&(this.mdcFoundation.setAbsolutePosition(this.x,t),this.mdcFoundation.setAnchorMargin({left:this.x,top:t,right:-this.x,bottom:t}))}))],Xi.prototype,"y",void 0),o([At({type:Boolean}),ve((function(t){this.mdcFoundation&&this.mdcFoundation.setQuickOpen(t)}))],Xi.prototype,"quick",void 0),o([At({type:Boolean,reflect:!0}),ve((function(t,e){this.onOpenChanged(t,e)}))],Xi.prototype,"open",void 0),o([At({type:Boolean})],Xi.prototype,"stayOpenOnBodyClick",void 0),o([Rt(),ve((function(t){this.mdcFoundation&&this.mdcFoundation.setAnchorCorner(t)}))],Xi.prototype,"bitwiseCorner",void 0),o([At({type:String}),ve((function(t){if(this.mdcFoundation){const e="START"===t||"END"===t,i=null===this.previousMenuCorner,n=!i&&t!==this.previousMenuCorner;e&&(n||i&&"END"===t)&&(this.bitwiseCorner=this.bitwiseCorner^fi.RIGHT,this.mdcFoundation.flipCornerHorizontally(),this.previousMenuCorner=t)}}))],Xi.prototype,"menuCorner",void 0),o([At({type:String}),ve((function(t){if(this.mdcFoundation&&t){let e=qi[t];"END"===this.menuCorner&&(e^=fi.RIGHT),this.bitwiseCorner=e}}))],Xi.prototype,"corner",void 0),o([Rt()],Xi.prototype,"styleTop",void 0),o([Rt()],Xi.prototype,"styleLeft",void 0),o([Rt()],Xi.prototype,"styleRight",void 0),o([Rt()],Xi.prototype,"styleBottom",void 0),o([Rt()],Xi.prototype,"styleMaxHeight",void 0),o([Rt()],Xi.prototype,"styleTransformOrigin",void 0); -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Yi={BG_FOCUSED:"mdc-ripple-upgraded--background-focused",FG_ACTIVATION:"mdc-ripple-upgraded--foreground-activation",FG_DEACTIVATION:"mdc-ripple-upgraded--foreground-deactivation",ROOT:"mdc-ripple-upgraded",UNBOUNDED:"mdc-ripple-upgraded--unbounded"},Gi={VAR_FG_SCALE:"--mdc-ripple-fg-scale",VAR_FG_SIZE:"--mdc-ripple-fg-size",VAR_FG_TRANSLATE_END:"--mdc-ripple-fg-translate-end",VAR_FG_TRANSLATE_START:"--mdc-ripple-fg-translate-start",VAR_LEFT:"--mdc-ripple-left",VAR_TOP:"--mdc-ripple-top"},Ki={DEACTIVATION_TIMEOUT_MS:225,FG_DEACTIVATION_MS:150,INITIAL_ORIGIN_SCALE:.6,PADDING:10,TAP_DELAY_MS:300}; -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var Zi=["touchstart","pointerdown","mousedown","keydown"],Qi=["touchend","pointerup","mouseup","contextmenu"],Ji=[],tn=function(t){function e(i){var o=t.call(this,n(n({},e.defaultAdapter),i))||this;return o.activationAnimationHasEnded=!1,o.activationTimer=0,o.fgDeactivationRemovalTimer=0,o.fgScale="0",o.frame={width:0,height:0},o.initialSize=0,o.layoutFrame=0,o.maxRadius=0,o.unboundedCoords={left:0,top:0},o.activationState=o.defaultActivationState(),o.activationTimerCallback=function(){o.activationAnimationHasEnded=!0,o.runDeactivationUXLogicIfReady()},o.activateHandler=function(t){o.activateImpl(t)},o.deactivateHandler=function(){o.deactivateImpl()},o.focusHandler=function(){o.handleFocus()},o.blurHandler=function(){o.handleBlur()},o.resizeHandler=function(){o.layout()},o}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return Yi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return Gi},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return Ki},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},browserSupportsCssVars:function(){return!0},computeBoundingRect:function(){return{top:0,right:0,bottom:0,left:0,width:0,height:0}},containsEventTarget:function(){return!0},deregisterDocumentInteractionHandler:function(){},deregisterInteractionHandler:function(){},deregisterResizeHandler:function(){},getWindowPageOffset:function(){return{x:0,y:0}},isSurfaceActive:function(){return!0},isSurfaceDisabled:function(){return!0},isUnbounded:function(){return!0},registerDocumentInteractionHandler:function(){},registerInteractionHandler:function(){},registerResizeHandler:function(){},removeClass:function(){},updateCssVariable:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t=this,i=this.supportsPressRipple();if(this.registerRootHandlers(i),i){var n=e.cssClasses,o=n.ROOT,r=n.UNBOUNDED;requestAnimationFrame((function(){t.adapter.addClass(o),t.adapter.isUnbounded()&&(t.adapter.addClass(r),t.layoutInternal())}))}},e.prototype.destroy=function(){var t=this;if(this.supportsPressRipple()){this.activationTimer&&(clearTimeout(this.activationTimer),this.activationTimer=0,this.adapter.removeClass(e.cssClasses.FG_ACTIVATION)),this.fgDeactivationRemovalTimer&&(clearTimeout(this.fgDeactivationRemovalTimer),this.fgDeactivationRemovalTimer=0,this.adapter.removeClass(e.cssClasses.FG_DEACTIVATION));var i=e.cssClasses,n=i.ROOT,o=i.UNBOUNDED;requestAnimationFrame((function(){t.adapter.removeClass(n),t.adapter.removeClass(o),t.removeCssVars()}))}this.deregisterRootHandlers(),this.deregisterDeactivationHandlers()},e.prototype.activate=function(t){this.activateImpl(t)},e.prototype.deactivate=function(){this.deactivateImpl()},e.prototype.layout=function(){var t=this;this.layoutFrame&&cancelAnimationFrame(this.layoutFrame),this.layoutFrame=requestAnimationFrame((function(){t.layoutInternal(),t.layoutFrame=0}))},e.prototype.setUnbounded=function(t){var i=e.cssClasses.UNBOUNDED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.handleFocus=function(){var t=this;requestAnimationFrame((function(){return t.adapter.addClass(e.cssClasses.BG_FOCUSED)}))},e.prototype.handleBlur=function(){var t=this;requestAnimationFrame((function(){return t.adapter.removeClass(e.cssClasses.BG_FOCUSED)}))},e.prototype.supportsPressRipple=function(){return this.adapter.browserSupportsCssVars()},e.prototype.defaultActivationState=function(){return{activationEvent:void 0,hasDeactivationUXRun:!1,isActivated:!1,isProgrammatic:!1,wasActivatedByPointer:!1,wasElementMadeActive:!1}},e.prototype.registerRootHandlers=function(t){var e,i;if(t){try{for(var n=r(Zi),o=n.next();!o.done;o=n.next()){var s=o.value;this.adapter.registerInteractionHandler(s,this.activateHandler)}}catch(t){e={error:t}}finally{try{o&&!o.done&&(i=n.return)&&i.call(n)}finally{if(e)throw e.error}}this.adapter.isUnbounded()&&this.adapter.registerResizeHandler(this.resizeHandler)}this.adapter.registerInteractionHandler("focus",this.focusHandler),this.adapter.registerInteractionHandler("blur",this.blurHandler)},e.prototype.registerDeactivationHandlers=function(t){var e,i;if("keydown"===t.type)this.adapter.registerInteractionHandler("keyup",this.deactivateHandler);else try{for(var n=r(Qi),o=n.next();!o.done;o=n.next()){var s=o.value;this.adapter.registerDocumentInteractionHandler(s,this.deactivateHandler)}}catch(t){e={error:t}}finally{try{o&&!o.done&&(i=n.return)&&i.call(n)}finally{if(e)throw e.error}}},e.prototype.deregisterRootHandlers=function(){var t,e;try{for(var i=r(Zi),n=i.next();!n.done;n=i.next()){var o=n.value;this.adapter.deregisterInteractionHandler(o,this.activateHandler)}}catch(e){t={error:e}}finally{try{n&&!n.done&&(e=i.return)&&e.call(i)}finally{if(t)throw t.error}}this.adapter.deregisterInteractionHandler("focus",this.focusHandler),this.adapter.deregisterInteractionHandler("blur",this.blurHandler),this.adapter.isUnbounded()&&this.adapter.deregisterResizeHandler(this.resizeHandler)},e.prototype.deregisterDeactivationHandlers=function(){var t,e;this.adapter.deregisterInteractionHandler("keyup",this.deactivateHandler);try{for(var i=r(Qi),n=i.next();!n.done;n=i.next()){var o=n.value;this.adapter.deregisterDocumentInteractionHandler(o,this.deactivateHandler)}}catch(e){t={error:e}}finally{try{n&&!n.done&&(e=i.return)&&e.call(i)}finally{if(t)throw t.error}}},e.prototype.removeCssVars=function(){var t=this,i=e.strings;Object.keys(i).forEach((function(e){0===e.indexOf("VAR_")&&t.adapter.updateCssVariable(i[e],null)}))},e.prototype.activateImpl=function(t){var e=this;if(!this.adapter.isSurfaceDisabled()){var i=this.activationState;if(!i.isActivated){var n=this.previousActivationEvent;if(!(n&&void 0!==t&&n.type!==t.type))i.isActivated=!0,i.isProgrammatic=void 0===t,i.activationEvent=t,i.wasActivatedByPointer=!i.isProgrammatic&&(void 0!==t&&("mousedown"===t.type||"touchstart"===t.type||"pointerdown"===t.type)),void 0!==t&&Ji.length>0&&Ji.some((function(t){return e.adapter.containsEventTarget(t)}))?this.resetActivationState():(void 0!==t&&(Ji.push(t.target),this.registerDeactivationHandlers(t)),i.wasElementMadeActive=this.checkElementMadeActive(t),i.wasElementMadeActive&&this.animateActivation(),requestAnimationFrame((function(){Ji=[],i.wasElementMadeActive||void 0===t||" "!==t.key&&32!==t.keyCode||(i.wasElementMadeActive=e.checkElementMadeActive(t),i.wasElementMadeActive&&e.animateActivation()),i.wasElementMadeActive||(e.activationState=e.defaultActivationState())})))}}},e.prototype.checkElementMadeActive=function(t){return void 0===t||"keydown"!==t.type||this.adapter.isSurfaceActive()},e.prototype.animateActivation=function(){var t=this,i=e.strings,n=i.VAR_FG_TRANSLATE_START,o=i.VAR_FG_TRANSLATE_END,r=e.cssClasses,s=r.FG_DEACTIVATION,a=r.FG_ACTIVATION,d=e.numbers.DEACTIVATION_TIMEOUT_MS;this.layoutInternal();var l="",c="";if(!this.adapter.isUnbounded()){var h=this.getFgTranslationCoordinates(),u=h.startPoint,p=h.endPoint;l=u.x+"px, "+u.y+"px",c=p.x+"px, "+p.y+"px"}this.adapter.updateCssVariable(n,l),this.adapter.updateCssVariable(o,c),clearTimeout(this.activationTimer),clearTimeout(this.fgDeactivationRemovalTimer),this.rmBoundedActivationClasses(),this.adapter.removeClass(s),this.adapter.computeBoundingRect(),this.adapter.addClass(a),this.activationTimer=setTimeout((function(){t.activationTimerCallback()}),d)},e.prototype.getFgTranslationCoordinates=function(){var t,e=this.activationState,i=e.activationEvent;return t=e.wasActivatedByPointer?function(t,e,i){if(!t)return{x:0,y:0};var n,o,r=e.x,s=e.y,a=r+i.left,d=s+i.top;if("touchstart"===t.type){var l=t;n=l.changedTouches[0].pageX-a,o=l.changedTouches[0].pageY-d}else{var c=t;n=c.pageX-a,o=c.pageY-d}return{x:n,y:o}}(i,this.adapter.getWindowPageOffset(),this.adapter.computeBoundingRect()):{x:this.frame.width/2,y:this.frame.height/2},{startPoint:t={x:t.x-this.initialSize/2,y:t.y-this.initialSize/2},endPoint:{x:this.frame.width/2-this.initialSize/2,y:this.frame.height/2-this.initialSize/2}}},e.prototype.runDeactivationUXLogicIfReady=function(){var t=this,i=e.cssClasses.FG_DEACTIVATION,n=this.activationState,o=n.hasDeactivationUXRun,r=n.isActivated;(o||!r)&&this.activationAnimationHasEnded&&(this.rmBoundedActivationClasses(),this.adapter.addClass(i),this.fgDeactivationRemovalTimer=setTimeout((function(){t.adapter.removeClass(i)}),Ki.FG_DEACTIVATION_MS))},e.prototype.rmBoundedActivationClasses=function(){var t=e.cssClasses.FG_ACTIVATION;this.adapter.removeClass(t),this.activationAnimationHasEnded=!1,this.adapter.computeBoundingRect()},e.prototype.resetActivationState=function(){var t=this;this.previousActivationEvent=this.activationState.activationEvent,this.activationState=this.defaultActivationState(),setTimeout((function(){return t.previousActivationEvent=void 0}),e.numbers.TAP_DELAY_MS)},e.prototype.deactivateImpl=function(){var t=this,e=this.activationState;if(e.isActivated){var i=n({},e);e.isProgrammatic?(requestAnimationFrame((function(){t.animateDeactivation(i)})),this.resetActivationState()):(this.deregisterDeactivationHandlers(),requestAnimationFrame((function(){t.activationState.hasDeactivationUXRun=!0,t.animateDeactivation(i),t.resetActivationState()})))}},e.prototype.animateDeactivation=function(t){var e=t.wasActivatedByPointer,i=t.wasElementMadeActive;(e||i)&&this.runDeactivationUXLogicIfReady()},e.prototype.layoutInternal=function(){var t=this;this.frame=this.adapter.computeBoundingRect();var i=Math.max(this.frame.height,this.frame.width);this.maxRadius=this.adapter.isUnbounded()?i:Math.sqrt(Math.pow(t.frame.width,2)+Math.pow(t.frame.height,2))+e.numbers.PADDING;var n=Math.floor(i*e.numbers.INITIAL_ORIGIN_SCALE);this.adapter.isUnbounded()&&n%2!=0?this.initialSize=n-1:this.initialSize=n,this.fgScale=""+this.maxRadius/this.initialSize,this.updateLayoutCssVars()},e.prototype.updateLayoutCssVars=function(){var t=e.strings,i=t.VAR_FG_SIZE,n=t.VAR_LEFT,o=t.VAR_TOP,r=t.VAR_FG_SCALE;this.adapter.updateCssVariable(i,this.initialSize+"px"),this.adapter.updateCssVariable(r,this.fgScale),this.adapter.isUnbounded()&&(this.unboundedCoords={left:Math.round(this.frame.width/2-this.initialSize/2),top:Math.round(this.frame.height/2-this.initialSize/2)},this.adapter.updateCssVariable(n,this.unboundedCoords.left+"px"),this.adapter.updateCssVariable(o,this.unboundedCoords.top+"px"))},e}(ee); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class en extends he{constructor(){super(...arguments),this.primary=!1,this.accent=!1,this.unbounded=!1,this.disabled=!1,this.activated=!1,this.selected=!1,this.internalUseStateLayerCustomProperties=!1,this.hovering=!1,this.bgFocused=!1,this.fgActivation=!1,this.fgDeactivation=!1,this.fgScale="",this.fgSize="",this.translateStart="",this.translateEnd="",this.leftPos="",this.topPos="",this.mdcFoundationClass=tn}get isActive(){return t=this.parentElement||this,e=":active",(t.matches||t.webkitMatchesSelector||t.msMatchesSelector).call(t,e); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var t,e}createAdapter(){return{browserSupportsCssVars:()=>!0,isUnbounded:()=>this.unbounded,isSurfaceActive:()=>this.isActive,isSurfaceDisabled:()=>this.disabled,addClass:t=>{switch(t){case"mdc-ripple-upgraded--background-focused":this.bgFocused=!0;break;case"mdc-ripple-upgraded--foreground-activation":this.fgActivation=!0;break;case"mdc-ripple-upgraded--foreground-deactivation":this.fgDeactivation=!0}},removeClass:t=>{switch(t){case"mdc-ripple-upgraded--background-focused":this.bgFocused=!1;break;case"mdc-ripple-upgraded--foreground-activation":this.fgActivation=!1;break;case"mdc-ripple-upgraded--foreground-deactivation":this.fgDeactivation=!1}},containsEventTarget:()=>!0,registerInteractionHandler:()=>{},deregisterInteractionHandler:()=>{},registerDocumentInteractionHandler:()=>{},deregisterDocumentInteractionHandler:()=>{},registerResizeHandler:()=>{},deregisterResizeHandler:()=>{},updateCssVariable:(t,e)=>{switch(t){case"--mdc-ripple-fg-scale":this.fgScale=e;break;case"--mdc-ripple-fg-size":this.fgSize=e;break;case"--mdc-ripple-fg-translate-end":this.translateEnd=e;break;case"--mdc-ripple-fg-translate-start":this.translateStart=e;break;case"--mdc-ripple-left":this.leftPos=e;break;case"--mdc-ripple-top":this.topPos=e}},computeBoundingRect:()=>(this.parentElement||this).getBoundingClientRect(),getWindowPageOffset:()=>({x:window.pageXOffset,y:window.pageYOffset})}}startPress(t){this.waitForFoundation((()=>{this.mdcFoundation.activate(t)}))}endPress(){this.waitForFoundation((()=>{this.mdcFoundation.deactivate()}))}startFocus(){this.waitForFoundation((()=>{this.mdcFoundation.handleFocus()}))}endFocus(){this.waitForFoundation((()=>{this.mdcFoundation.handleBlur()}))}startHover(){this.hovering=!0}endHover(){this.hovering=!1}waitForFoundation(t){this.mdcFoundation?t():this.updateComplete.then(t)}update(t){t.has("disabled")&&this.disabled&&this.endHover(),super.update(t)}render(){const t=this.activated&&(this.primary||!this.accent),e=this.selected&&(this.primary||!this.accent),i={"mdc-ripple-surface--accent":this.accent,"mdc-ripple-surface--primary--activated":t,"mdc-ripple-surface--accent--activated":this.accent&&this.activated,"mdc-ripple-surface--primary--selected":e,"mdc-ripple-surface--accent--selected":this.accent&&this.selected,"mdc-ripple-surface--disabled":this.disabled,"mdc-ripple-surface--hover":this.hovering,"mdc-ripple-surface--primary":this.primary,"mdc-ripple-surface--selected":this.selected,"mdc-ripple-upgraded--background-focused":this.bgFocused,"mdc-ripple-upgraded--foreground-activation":this.fgActivation,"mdc-ripple-upgraded--foreground-deactivation":this.fgDeactivation,"mdc-ripple-upgraded--unbounded":this.unbounded,"mdc-ripple-surface--internal-use-state-layer-custom-properties":this.internalUseStateLayerCustomProperties};return $` -
`}}o([Mt(".mdc-ripple-surface")],en.prototype,"mdcRoot",void 0),o([At({type:Boolean})],en.prototype,"primary",void 0),o([At({type:Boolean})],en.prototype,"accent",void 0),o([At({type:Boolean})],en.prototype,"unbounded",void 0),o([At({type:Boolean})],en.prototype,"disabled",void 0),o([At({type:Boolean})],en.prototype,"activated",void 0),o([At({type:Boolean})],en.prototype,"selected",void 0),o([At({type:Boolean})],en.prototype,"internalUseStateLayerCustomProperties",void 0),o([Rt()],en.prototype,"hovering",void 0),o([Rt()],en.prototype,"bgFocused",void 0),o([Rt()],en.prototype,"fgActivation",void 0),o([Rt()],en.prototype,"fgDeactivation",void 0),o([Rt()],en.prototype,"fgScale",void 0),o([Rt()],en.prototype,"fgSize",void 0),o([Rt()],en.prototype,"translateStart",void 0),o([Rt()],en.prototype,"translateEnd",void 0),o([Rt()],en.prototype,"leftPos",void 0),o([Rt()],en.prototype,"topPos",void 0); -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var nn={NOTCH_ELEMENT_SELECTOR:".mdc-notched-outline__notch"},on={NOTCH_ELEMENT_PADDING:8},rn={NO_LABEL:"mdc-notched-outline--no-label",OUTLINE_NOTCHED:"mdc-notched-outline--notched",OUTLINE_UPGRADED:"mdc-notched-outline--upgraded"},sn=function(t){function e(i){return t.call(this,n(n({},e.defaultAdapter),i))||this}return i(e,t),Object.defineProperty(e,"strings",{get:function(){return nn},enumerable:!1,configurable:!0}),Object.defineProperty(e,"cssClasses",{get:function(){return rn},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return on},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},setNotchWidthProperty:function(){},removeNotchWidthProperty:function(){}}},enumerable:!1,configurable:!0}),e.prototype.notch=function(t){var i=e.cssClasses.OUTLINE_NOTCHED;t>0&&(t+=on.NOTCH_ELEMENT_PADDING),this.adapter.setNotchWidthProperty(t),this.adapter.addClass(i)},e.prototype.closeNotch=function(){var t=e.cssClasses.OUTLINE_NOTCHED;this.adapter.removeClass(t),this.adapter.removeNotchWidthProperty()},e}(ee); -/** - * @license - * Copyright 2019 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class an extends he{constructor(){super(...arguments),this.mdcFoundationClass=sn,this.width=0,this.open=!1,this.lastOpen=this.open}createAdapter(){return{addClass:t=>this.mdcRoot.classList.add(t),removeClass:t=>this.mdcRoot.classList.remove(t),setNotchWidthProperty:t=>this.notchElement.style.setProperty("width",`${t}px`),removeNotchWidthProperty:()=>this.notchElement.style.removeProperty("width")}}openOrClose(t,e){this.mdcFoundation&&(t&&void 0!==e?this.mdcFoundation.notch(e):this.mdcFoundation.closeNotch())}render(){this.openOrClose(this.open,this.width);const t=we({"mdc-notched-outline--notched":this.open});return $` - - - - - - - `}}o([Mt(".mdc-notched-outline")],an.prototype,"mdcRoot",void 0),o([At({type:Number})],an.prototype,"width",void 0),o([At({type:Boolean,reflect:!0})],an.prototype,"open",void 0),o([Mt(".mdc-notched-outline__notch")],an.prototype,"notchElement",void 0); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const dn=ht`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}.mdc-select{display:inline-flex;position:relative}.mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87)}.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-select.mdc-select--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54)}.mdc-select:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#6200ee;fill:var(--mdc-theme-primary, #6200ee)}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled)+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-select:not(.mdc-select--disabled) .mdc-select__icon{color:rgba(0, 0, 0, 0.54)}.mdc-select.mdc-select--disabled .mdc-select__icon{color:rgba(0, 0, 0, 0.38)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select.mdc-select--disabled .mdc-select__selected-text{color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__dropdown-icon{fill:red}.mdc-select.mdc-select--disabled .mdc-floating-label{color:GrayText}.mdc-select.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}.mdc-select.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select.mdc-select--disabled .mdc-notched-outline__trailing{border-color:GrayText}.mdc-select.mdc-select--disabled .mdc-select__icon{color:GrayText}.mdc-select.mdc-select--disabled+.mdc-select-helper-text{color:GrayText}}.mdc-select .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-select .mdc-select__anchor{padding-left:16px;padding-right:0}[dir=rtl] .mdc-select .mdc-select__anchor,.mdc-select .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:16px}.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor{padding-left:0;padding-right:0}[dir=rtl] .mdc-select.mdc-select--with-leading-icon .mdc-select__anchor,.mdc-select.mdc-select--with-leading-icon .mdc-select__anchor[dir=rtl]{padding-left:0;padding-right:0}.mdc-select .mdc-select__icon{width:24px;height:24px;font-size:24px}.mdc-select .mdc-select__dropdown-icon{width:24px;height:24px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item,.mdc-select .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic{margin-left:0;margin-right:12px}[dir=rtl] .mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic,.mdc-select .mdc-select__menu .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:12px;margin-right:0}.mdc-select__dropdown-icon{margin-left:12px;margin-right:12px;display:inline-flex;position:relative;align-self:center;align-items:center;justify-content:center;flex-shrink:0;pointer-events:none}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active,.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{position:absolute;top:0;left:0}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-graphic{width:41.6666666667%;height:20.8333333333%}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:1;transition:opacity 75ms linear 75ms}.mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:0;transition:opacity 75ms linear}[dir=rtl] .mdc-select__dropdown-icon,.mdc-select__dropdown-icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-inactive{opacity:0;transition:opacity 49.5ms linear}.mdc-select--activated .mdc-select__dropdown-icon .mdc-select__dropdown-icon-active{opacity:1;transition:opacity 100.5ms linear 49.5ms}.mdc-select__anchor{width:200px;min-width:0;flex:1 1 auto;position:relative;box-sizing:border-box;overflow:hidden;outline:none;cursor:pointer}.mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-select__selected-text-container{display:flex;appearance:none;pointer-events:none;box-sizing:border-box;width:auto;min-width:0;flex-grow:1;height:28px;border:none;outline:none;padding:0;background-color:transparent;color:inherit}.mdc-select__selected-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);line-height:1.75rem;line-height:var(--mdc-typography-subtitle1-line-height, 1.75rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;width:100%;text-align:left}[dir=rtl] .mdc-select__selected-text,.mdc-select__selected-text[dir=rtl]{text-align:right}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--invalid+.mdc-select-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-select__dropdown-icon{fill:#b00020;fill:var(--mdc-theme-error, #b00020)}.mdc-select--disabled{cursor:default;pointer-events:none}.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item{padding-left:12px;padding-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item,.mdc-select--with-leading-icon .mdc-select__menu .mdc-deprecated-list-item[dir=rtl]{padding-left:12px;padding-right:12px}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-select__menu::before{position:absolute;box-sizing:border-box;width:100%;height:100%;top:0;left:0;border:1px solid transparent;border-radius:inherit;content:"";pointer-events:none}}@media screen and (forced-colors: active)and (forced-colors: active),screen and (-ms-high-contrast: active)and (forced-colors: active){.mdc-select__menu::before{border-color:CanvasText}}.mdc-select__menu .mdc-deprecated-list .mdc-select__icon,.mdc-select__menu .mdc-list .mdc-select__icon{margin-left:0;margin-right:0}[dir=rtl] .mdc-select__menu .mdc-deprecated-list .mdc-select__icon,[dir=rtl] .mdc-select__menu .mdc-list .mdc-select__icon,.mdc-select__menu .mdc-deprecated-list .mdc-select__icon[dir=rtl],.mdc-select__menu .mdc-list .mdc-select__icon[dir=rtl]{margin-left:0;margin-right:0}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__graphic,.mdc-select__menu .mdc-list .mdc-deprecated-list-item--activated .mdc-deprecated-list-item__graphic{color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-select__menu .mdc-list-item__start{display:inline-flex;align-items:center}.mdc-select__option{padding-left:16px;padding-right:16px}[dir=rtl] .mdc-select__option,.mdc-select__option[dir=rtl]{padding-left:16px;padding-right:16px}.mdc-select__one-line-option.mdc-list-item--with-one-line{height:48px}.mdc-select__two-line-option.mdc-list-item--with-two-lines{height:64px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__start{margin-top:20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text{display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::before{display:inline-block;width:0;height:28px;content:"";vertical-align:0}.mdc-select__two-line-option.mdc-list-item--with-two-lines .mdc-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end{display:block;margin-top:0;line-height:normal}.mdc-select__two-line-option.mdc-list-item--with-two-lines.mdc-list-item--with-trailing-meta .mdc-list-item__end::before{display:inline-block;width:0;height:36px;content:"";vertical-align:0}.mdc-select__option-with-leading-content{padding-left:0;padding-right:12px}.mdc-select__option-with-leading-content.mdc-list-item{padding-left:0;padding-right:auto}[dir=rtl] .mdc-select__option-with-leading-content.mdc-list-item,.mdc-select__option-with-leading-content.mdc-list-item[dir=rtl]{padding-left:auto;padding-right:0}.mdc-select__option-with-leading-content .mdc-list-item__start{margin-left:12px;margin-right:0}[dir=rtl] .mdc-select__option-with-leading-content .mdc-list-item__start,.mdc-select__option-with-leading-content .mdc-list-item__start[dir=rtl]{margin-left:0;margin-right:12px}.mdc-select__option-with-leading-content .mdc-list-item__start{width:36px;height:24px}[dir=rtl] .mdc-select__option-with-leading-content,.mdc-select__option-with-leading-content[dir=rtl]{padding-left:12px;padding-right:0}.mdc-select__option-with-meta.mdc-list-item{padding-left:auto;padding-right:0}[dir=rtl] .mdc-select__option-with-meta.mdc-list-item,.mdc-select__option-with-meta.mdc-list-item[dir=rtl]{padding-left:0;padding-right:auto}.mdc-select__option-with-meta .mdc-list-item__end{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select__option-with-meta .mdc-list-item__end,.mdc-select__option-with-meta .mdc-list-item__end[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select--filled .mdc-select__anchor{height:56px;display:flex;align-items:baseline}.mdc-select--filled .mdc-select__anchor::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--filled.mdc-select--no-label .mdc-select__anchor::before{display:none}.mdc-select--filled .mdc-select__anchor{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0}.mdc-select--filled:not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke}.mdc-select--filled.mdc-select--disabled .mdc-select__anchor{background-color:#fafafa}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-select--filled:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-select--filled:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--filled.mdc-select--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-select--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-select--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-select--filled .mdc-menu-surface--is-open-below{border-top-left-radius:0px;border-top-right-radius:0px}.mdc-select--filled.mdc-select--focused.mdc-line-ripple::after{transform:scale(1, 2);opacity:1}.mdc-select--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-select--filled .mdc-floating-label,.mdc-select--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{left:48px;right:initial}[dir=rtl] .mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-select--filled.mdc-select--with-leading-icon .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--invalid:not(.mdc-select--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined{border:none}.mdc-select--outlined .mdc-select__anchor{height:56px}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-56px{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-select--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-select--outlined .mdc-select__anchor{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined .mdc-select__anchor,.mdc-select--outlined .mdc-select__anchor[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-select--outlined+.mdc-select-helper-text{margin-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-select--outlined+.mdc-select-helper-text,.mdc-select--outlined+.mdc-select-helper-text[dir=rtl]{margin-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-select__anchor{background-color:transparent}.mdc-select--outlined.mdc-select--disabled .mdc-select__anchor{background-color:transparent}.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}.mdc-select--outlined .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-select--outlined .mdc-select__anchor{display:flex;align-items:baseline;overflow:visible}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined 250ms 1}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-select--outlined .mdc-select__anchor .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-select--outlined .mdc-select__anchor.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined .mdc-select__anchor .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text::before{content:"​"}.mdc-select--outlined .mdc-select__anchor .mdc-select__selected-text-container{height:100%;display:inline-flex;align-items:center}.mdc-select--outlined .mdc-select__anchor::before{display:none}.mdc-select--outlined .mdc-select__selected-text-container{display:flex;border:none;z-index:1;background-color:transparent}.mdc-select--outlined .mdc-select__icon{z-index:2}.mdc-select--outlined .mdc-floating-label{line-height:1.15rem;left:4px;right:initial}[dir=rtl] .mdc-select--outlined .mdc-floating-label,.mdc-select--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-select--outlined.mdc-select--focused .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled):not(.mdc-select--focused) .mdc-select__anchor:hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-width:2px}.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__leading,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__notch,.mdc-select--outlined.mdc-select--invalid:not(.mdc-select--disabled).mdc-select--focused .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--float-above{font-size:.75rem}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-select--outlined.mdc-select--with-leading-icon.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-select--outlined.mdc-select--with-leading-icon .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-select--outlined.mdc-select--with-leading-icon .mdc-floating-label--shake,.mdc-select--outlined.mdc-select--with-leading-icon[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px 250ms 1}@keyframes mdc-floating-label-shake-float-above-select-outlined-leading-icon-56px-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-select--outlined.mdc-select--with-leading-icon .mdc-select__anchor :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 96px)}.mdc-select--outlined .mdc-menu-surface{margin-bottom:8px}.mdc-select--outlined.mdc-select--no-label .mdc-menu-surface,.mdc-select--outlined .mdc-menu-surface--is-open-below{margin-bottom:0}.mdc-select__anchor{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-select__anchor .mdc-select__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-select__anchor .mdc-select__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-select__anchor.mdc-ripple-upgraded--unbounded .mdc-select__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-select__anchor.mdc-ripple-upgraded--foreground-activation .mdc-select__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-select__anchor.mdc-ripple-upgraded--foreground-deactivation .mdc-select__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-select__anchor.mdc-ripple-upgraded .mdc-select__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-select__anchor .mdc-select__ripple::before,.mdc-select__anchor .mdc-select__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-select__anchor:hover .mdc-select__ripple::before,.mdc-select__anchor.mdc-ripple-surface--hover .mdc-select__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__anchor.mdc-ripple-upgraded--background-focused .mdc-select__ripple::before,.mdc-select__anchor:not(.mdc-ripple-upgraded):focus .mdc-select__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__anchor .mdc-select__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-deprecated-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-deprecated-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-deprecated-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-deprecated-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-deprecated-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-deprecated-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected .mdc-list-item__ripple::after{background-color:#000;background-color:var(--mdc-ripple-color, var(--mdc-theme-on-surface, #000))}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:hover .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-surface--hover .mdc-list-item__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded--background-focused .mdc-list-item__ripple::before,.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):focus .mdc-list-item__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded) .mdc-list-item__ripple::after{transition:opacity 150ms linear}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected:not(.mdc-ripple-upgraded):active .mdc-list-item__ripple::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select__menu .mdc-deprecated-list .mdc-deprecated-list-item--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-select-helper-text{margin:0;margin-left:16px;margin-right:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal}[dir=rtl] .mdc-select-helper-text,.mdc-select-helper-text[dir=rtl]{margin-left:16px;margin-right:16px}.mdc-select-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-select-helper-text--validation-msg{opacity:0;transition:opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-select--invalid+.mdc-select-helper-text--validation-msg,.mdc-select-helper-text--validation-msg-persistent{opacity:1}.mdc-select--with-leading-icon .mdc-select__icon{display:inline-block;box-sizing:border-box;border:none;text-decoration:none;cursor:pointer;user-select:none;flex-shrink:0;align-self:center;background-color:transparent;fill:currentColor}.mdc-select--with-leading-icon .mdc-select__icon{margin-left:12px;margin-right:12px}[dir=rtl] .mdc-select--with-leading-icon .mdc-select__icon,.mdc-select--with-leading-icon .mdc-select__icon[dir=rtl]{margin-left:12px;margin-right:12px}.mdc-select__icon:not([tabindex]),.mdc-select__icon[tabindex="-1"]{cursor:default;pointer-events:none}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-block;vertical-align:top;outline:none}.mdc-select{width:100%}[hidden]{display:none}.mdc-select__icon{z-index:2}.mdc-select--with-leading-icon{--mdc-list-item-graphic-margin: calc( 48px - var(--mdc-list-item-graphic-size, 24px) - var(--mdc-list-side-padding, 16px) )}.mdc-select .mdc-select__anchor .mdc-select__selected-text{overflow:hidden}.mdc-select .mdc-select__anchor *{display:inline-flex}.mdc-select .mdc-select__anchor .mdc-floating-label{display:inline-block}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-idle-border-color, rgba(0, 0, 0, 0.38) );--mdc-notched-outline-notch-offset: 1px}:host(:not([disabled]):hover) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.87);color:var(--mdc-select-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-select-idle-line-color, rgba(0, 0, 0, 0.42))}:host(:not([disabled])) .mdc-select:not(.mdc-select--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-select-hover-line-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-select:not(.mdc-select--outlined):not(.mdc-select--disabled) .mdc-select__anchor{background-color:whitesmoke;background-color:var(--mdc-select-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-select__dropdown-icon{fill:var(--mdc-select-error-dropdown-icon-color, var(--mdc-select-error-color, var(--mdc-theme-error, #b00020)))}:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label,:host(:not([disabled])) .mdc-select.mdc-select--invalid .mdc-floating-label::after{color:var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select.mdc-select--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}.mdc-select__menu--invalid{--mdc-theme-primary: var(--mdc-select-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.6);color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.54);fill:var(--mdc-select-dropdown-icon-color, rgba(0, 0, 0, 0.54))}:host(:not([disabled])) .mdc-select.mdc-select--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px;--mdc-notched-outline-notch-offset: 2px}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-select__dropdown-icon{fill:rgba(98,0,238,.87);fill:var(--mdc-select-focused-dropdown-icon-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)))}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select.mdc-select--focused:not(.mdc-select--invalid) .mdc-floating-label::after{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-select-helper-text:not(.mdc-select-helper-text--validation-msg){color:var(--mdc-select-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]){pointer-events:none}:host([disabled]) .mdc-select:not(.mdc-select--outlined).mdc-select--disabled .mdc-select__anchor{background-color:#fafafa;background-color:var(--mdc-select-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-select.mdc-select--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-select-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-select .mdc-select__dropdown-icon{fill:rgba(0, 0, 0, 0.38);fill:var(--mdc-select-disabled-dropdown-icon-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label,:host([disabled]) .mdc-select:not(.mdc-select--invalid):not(.mdc-select--focused) .mdc-floating-label::after{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select-helper-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-select__selected-text{color:rgba(0, 0, 0, 0.38);color:var(--mdc-select-disabled-ink-color, rgba(0, 0, 0, 0.38))}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,ln=ht`@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}:host{display:block}.mdc-deprecated-list{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);line-height:1.75rem;line-height:var(--mdc-typography-subtitle1-line-height, 1.75rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);line-height:1.5rem;margin:0;padding:8px 0;list-style-type:none;color:rgba(0, 0, 0, 0.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87));padding:var(--mdc-list-vertical-padding, 8px) 0}.mdc-deprecated-list:focus{outline:none}.mdc-deprecated-list-item{height:48px}.mdc-deprecated-list--dense{padding-top:4px;padding-bottom:4px;font-size:.812rem}.mdc-deprecated-list ::slotted([divider]){height:0;margin:0;border:none;border-bottom-width:1px;border-bottom-style:solid;border-bottom-color:rgba(0, 0, 0, 0.12)}.mdc-deprecated-list ::slotted([divider][padded]){margin:0 var(--mdc-list-side-padding, 16px)}.mdc-deprecated-list ::slotted([divider][inset]){margin-left:var(--mdc-list-inset-margin, 72px);margin-right:0;width:calc( 100% - var(--mdc-list-inset-margin, 72px) )}[dir=rtl] .mdc-deprecated-list ::slotted([divider][inset]),.mdc-deprecated-list ::slotted([divider][inset][dir=rtl]){margin-left:0;margin-right:var(--mdc-list-inset-margin, 72px)}.mdc-deprecated-list ::slotted([divider][inset][padded]){width:calc( 100% - var(--mdc-list-inset-margin, 72px) - var(--mdc-list-side-padding, 16px) )}.mdc-deprecated-list--dense ::slotted([mwc-list-item]){height:40px}.mdc-deprecated-list--dense ::slotted([mwc-list]){--mdc-list-item-graphic-size: 20px}.mdc-deprecated-list--two-line.mdc-deprecated-list--dense ::slotted([mwc-list-item]),.mdc-deprecated-list--avatar-list.mdc-deprecated-list--dense ::slotted([mwc-list-item]){height:60px}.mdc-deprecated-list--avatar-list.mdc-deprecated-list--dense ::slotted([mwc-list]){--mdc-list-item-graphic-size: 36px}:host([noninteractive]){pointer-events:none;cursor:default}.mdc-deprecated-list--dense ::slotted(.mdc-deprecated-list-item__primary-text){display:block;margin-top:0;line-height:normal;margin-bottom:-20px}.mdc-deprecated-list--dense ::slotted(.mdc-deprecated-list-item__primary-text)::before{display:inline-block;width:0;height:24px;content:"";vertical-align:0}.mdc-deprecated-list--dense ::slotted(.mdc-deprecated-list-item__primary-text)::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,cn=ht`:host{cursor:pointer;user-select:none;-webkit-tap-highlight-color:transparent;height:48px;display:flex;position:relative;align-items:center;justify-content:flex-start;overflow:hidden;padding:0;padding-left:var(--mdc-list-side-padding, 16px);padding-right:var(--mdc-list-side-padding, 16px);outline:none;height:48px;color:rgba(0,0,0,.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87))}:host:focus{outline:none}:host([activated]){color:#6200ee;color:var(--mdc-theme-primary, #6200ee);--mdc-ripple-color: var( --mdc-theme-primary, #6200ee )}:host([activated]) .mdc-deprecated-list-item__graphic{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host([activated]) .fake-activated-ripple::before{position:absolute;display:block;top:0;bottom:0;left:0;right:0;width:100%;height:100%;pointer-events:none;z-index:1;content:"";opacity:0.12;opacity:var(--mdc-ripple-activated-opacity, 0.12);background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-deprecated-list-item__graphic{flex-shrink:0;align-items:center;justify-content:center;fill:currentColor;display:inline-flex}.mdc-deprecated-list-item__graphic ::slotted(*){flex-shrink:0;align-items:center;justify-content:center;fill:currentColor;width:100%;height:100%;text-align:center}.mdc-deprecated-list-item__meta{width:var(--mdc-list-item-meta-size, 24px);height:var(--mdc-list-item-meta-size, 24px);margin-left:auto;margin-right:0;color:rgba(0, 0, 0, 0.38);color:var(--mdc-theme-text-hint-on-background, rgba(0, 0, 0, 0.38))}.mdc-deprecated-list-item__meta.multi{width:auto}.mdc-deprecated-list-item__meta ::slotted(*){width:var(--mdc-list-item-meta-size, 24px);line-height:var(--mdc-list-item-meta-size, 24px)}.mdc-deprecated-list-item__meta ::slotted(.material-icons),.mdc-deprecated-list-item__meta ::slotted(mwc-icon){line-height:var(--mdc-list-item-meta-size, 24px) !important}.mdc-deprecated-list-item__meta ::slotted(:not(.material-icons):not(mwc-icon)){-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit)}[dir=rtl] .mdc-deprecated-list-item__meta,.mdc-deprecated-list-item__meta[dir=rtl]{margin-left:0;margin-right:auto}.mdc-deprecated-list-item__meta ::slotted(*){width:100%;height:100%}.mdc-deprecated-list-item__text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mdc-deprecated-list-item__text ::slotted([for]),.mdc-deprecated-list-item__text[for]{pointer-events:none}.mdc-deprecated-list-item__primary-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;margin-top:0;line-height:normal;margin-bottom:-20px;display:block}.mdc-deprecated-list-item__primary-text::before{display:inline-block;width:0;height:32px;content:"";vertical-align:0}.mdc-deprecated-list-item__primary-text::after{display:inline-block;width:0;height:20px;content:"";vertical-align:-20px}.mdc-deprecated-list-item__secondary-text{-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);text-overflow:ellipsis;white-space:nowrap;overflow:hidden;display:block;margin-top:0;line-height:normal;display:block}.mdc-deprecated-list-item__secondary-text::before{display:inline-block;width:0;height:20px;content:"";vertical-align:0}.mdc-deprecated-list--dense .mdc-deprecated-list-item__secondary-text{font-size:inherit}* ::slotted(a),a{color:inherit;text-decoration:none}:host([twoline]){height:72px}:host([twoline]) .mdc-deprecated-list-item__text{align-self:flex-start}:host([disabled]),:host([noninteractive]){cursor:default;pointer-events:none}:host([disabled]) .mdc-deprecated-list-item__text ::slotted(*){opacity:.38}:host([disabled]) .mdc-deprecated-list-item__text ::slotted(*),:host([disabled]) .mdc-deprecated-list-item__primary-text ::slotted(*),:host([disabled]) .mdc-deprecated-list-item__secondary-text ::slotted(*){color:#000;color:var(--mdc-theme-on-surface, #000)}.mdc-deprecated-list-item__secondary-text ::slotted(*){color:rgba(0, 0, 0, 0.54);color:var(--mdc-theme-text-secondary-on-background, rgba(0, 0, 0, 0.54))}.mdc-deprecated-list-item__graphic ::slotted(*){background-color:transparent;color:rgba(0, 0, 0, 0.38);color:var(--mdc-theme-text-icon-on-background, rgba(0, 0, 0, 0.38))}.mdc-deprecated-list-group__subheader ::slotted(*){color:rgba(0, 0, 0, 0.87);color:var(--mdc-theme-text-primary-on-background, rgba(0, 0, 0, 0.87))}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic{width:var(--mdc-list-item-graphic-size, 40px);height:var(--mdc-list-item-graphic-size, 40px)}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic.multi{width:auto}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(*){width:var(--mdc-list-item-graphic-size, 40px);line-height:var(--mdc-list-item-graphic-size, 40px)}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon){line-height:var(--mdc-list-item-graphic-size, 40px) !important}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic ::slotted(*){border-radius:50%}:host([graphic=avatar]) .mdc-deprecated-list-item__graphic,:host([graphic=medium]) .mdc-deprecated-list-item__graphic,:host([graphic=large]) .mdc-deprecated-list-item__graphic,:host([graphic=control]) .mdc-deprecated-list-item__graphic{margin-left:0;margin-right:var(--mdc-list-item-graphic-margin, 16px)}[dir=rtl] :host([graphic=avatar]) .mdc-deprecated-list-item__graphic,[dir=rtl] :host([graphic=medium]) .mdc-deprecated-list-item__graphic,[dir=rtl] :host([graphic=large]) .mdc-deprecated-list-item__graphic,[dir=rtl] :host([graphic=control]) .mdc-deprecated-list-item__graphic,:host([graphic=avatar]) .mdc-deprecated-list-item__graphic[dir=rtl],:host([graphic=medium]) .mdc-deprecated-list-item__graphic[dir=rtl],:host([graphic=large]) .mdc-deprecated-list-item__graphic[dir=rtl],:host([graphic=control]) .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:var(--mdc-list-item-graphic-margin, 16px);margin-right:0}:host([graphic=icon]) .mdc-deprecated-list-item__graphic{width:var(--mdc-list-item-graphic-size, 24px);height:var(--mdc-list-item-graphic-size, 24px);margin-left:0;margin-right:var(--mdc-list-item-graphic-margin, 32px)}:host([graphic=icon]) .mdc-deprecated-list-item__graphic.multi{width:auto}:host([graphic=icon]) .mdc-deprecated-list-item__graphic ::slotted(*){width:var(--mdc-list-item-graphic-size, 24px);line-height:var(--mdc-list-item-graphic-size, 24px)}:host([graphic=icon]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=icon]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon){line-height:var(--mdc-list-item-graphic-size, 24px) !important}[dir=rtl] :host([graphic=icon]) .mdc-deprecated-list-item__graphic,:host([graphic=icon]) .mdc-deprecated-list-item__graphic[dir=rtl]{margin-left:var(--mdc-list-item-graphic-margin, 32px);margin-right:0}:host([graphic=avatar]:not([twoLine])),:host([graphic=icon]:not([twoLine])){height:56px}:host([graphic=medium]:not([twoLine])),:host([graphic=large]:not([twoLine])){height:72px}:host([graphic=medium]) .mdc-deprecated-list-item__graphic,:host([graphic=large]) .mdc-deprecated-list-item__graphic{width:var(--mdc-list-item-graphic-size, 56px);height:var(--mdc-list-item-graphic-size, 56px)}:host([graphic=medium]) .mdc-deprecated-list-item__graphic.multi,:host([graphic=large]) .mdc-deprecated-list-item__graphic.multi{width:auto}:host([graphic=medium]) .mdc-deprecated-list-item__graphic ::slotted(*),:host([graphic=large]) .mdc-deprecated-list-item__graphic ::slotted(*){width:var(--mdc-list-item-graphic-size, 56px);line-height:var(--mdc-list-item-graphic-size, 56px)}:host([graphic=medium]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=medium]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon),:host([graphic=large]) .mdc-deprecated-list-item__graphic ::slotted(.material-icons),:host([graphic=large]) .mdc-deprecated-list-item__graphic ::slotted(mwc-icon){line-height:var(--mdc-list-item-graphic-size, 56px) !important}:host([graphic=large]){padding-left:0px}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,hn=ht`.mdc-ripple-surface{--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;position:relative;outline:none;overflow:hidden}.mdc-ripple-surface::before,.mdc-ripple-surface::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-ripple-surface::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-ripple-surface::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-ripple-surface.mdc-ripple-upgraded::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-ripple-surface.mdc-ripple-upgraded::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-ripple-surface.mdc-ripple-upgraded--unbounded::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-ripple-surface.mdc-ripple-upgraded--foreground-activation::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-ripple-surface.mdc-ripple-upgraded--foreground-deactivation::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-ripple-surface::before,.mdc-ripple-surface::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-ripple-surface.mdc-ripple-upgraded::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-ripple-surface[data-mdc-ripple-is-unbounded],.mdc-ripple-upgraded--unbounded{overflow:visible}.mdc-ripple-surface[data-mdc-ripple-is-unbounded]::before,.mdc-ripple-surface[data-mdc-ripple-is-unbounded]::after,.mdc-ripple-upgraded--unbounded::before,.mdc-ripple-upgraded--unbounded::after{top:calc(50% - 50%);left:calc(50% - 50%);width:100%;height:100%}.mdc-ripple-surface[data-mdc-ripple-is-unbounded].mdc-ripple-upgraded::before,.mdc-ripple-surface[data-mdc-ripple-is-unbounded].mdc-ripple-upgraded::after,.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::before,.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::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-ripple-surface[data-mdc-ripple-is-unbounded].mdc-ripple-upgraded::after,.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-ripple-surface::before,.mdc-ripple-surface::after{background-color:#000;background-color:var(--mdc-ripple-color, #000)}.mdc-ripple-surface:hover::before,.mdc-ripple-surface.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-ripple-surface.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-ripple-surface:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}:host{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;display:block}:host .mdc-ripple-surface{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none;will-change:unset}.mdc-ripple-surface--primary::before,.mdc-ripple-surface--primary::after{background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-ripple-surface--primary:hover::before,.mdc-ripple-surface--primary.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-ripple-surface--primary.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--primary:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-ripple-surface--primary:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--primary:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--primary.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--primary--activated::before{opacity:0.12;opacity:var(--mdc-ripple-activated-opacity, 0.12)}.mdc-ripple-surface--primary--activated::before,.mdc-ripple-surface--primary--activated::after{background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-ripple-surface--primary--activated:hover::before,.mdc-ripple-surface--primary--activated.mdc-ripple-surface--hover::before{opacity:0.16;opacity:var(--mdc-ripple-hover-opacity, 0.16)}.mdc-ripple-surface--primary--activated.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--primary--activated:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-focus-opacity, 0.24)}.mdc-ripple-surface--primary--activated:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--primary--activated:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--primary--activated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--primary--selected::before{opacity:0.08;opacity:var(--mdc-ripple-selected-opacity, 0.08)}.mdc-ripple-surface--primary--selected::before,.mdc-ripple-surface--primary--selected::after{background-color:#6200ee;background-color:var(--mdc-ripple-color, var(--mdc-theme-primary, #6200ee))}.mdc-ripple-surface--primary--selected:hover::before,.mdc-ripple-surface--primary--selected.mdc-ripple-surface--hover::before{opacity:0.12;opacity:var(--mdc-ripple-hover-opacity, 0.12)}.mdc-ripple-surface--primary--selected.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--primary--selected:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-focus-opacity, 0.2)}.mdc-ripple-surface--primary--selected:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--primary--selected:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--primary--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--accent::before,.mdc-ripple-surface--accent::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-ripple-surface--accent:hover::before,.mdc-ripple-surface--accent.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-ripple-surface--accent.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--accent:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-ripple-surface--accent:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--accent:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--accent.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.12)}.mdc-ripple-surface--accent--activated::before{opacity:0.12;opacity:var(--mdc-ripple-activated-opacity, 0.12)}.mdc-ripple-surface--accent--activated::before,.mdc-ripple-surface--accent--activated::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-ripple-surface--accent--activated:hover::before,.mdc-ripple-surface--accent--activated.mdc-ripple-surface--hover::before{opacity:0.16;opacity:var(--mdc-ripple-hover-opacity, 0.16)}.mdc-ripple-surface--accent--activated.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--accent--activated:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-focus-opacity, 0.24)}.mdc-ripple-surface--accent--activated:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--accent--activated:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.24;opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--accent--activated.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.24)}.mdc-ripple-surface--accent--selected::before{opacity:0.08;opacity:var(--mdc-ripple-selected-opacity, 0.08)}.mdc-ripple-surface--accent--selected::before,.mdc-ripple-surface--accent--selected::after{background-color:#018786;background-color:var(--mdc-ripple-color, var(--mdc-theme-secondary, #018786))}.mdc-ripple-surface--accent--selected:hover::before,.mdc-ripple-surface--accent--selected.mdc-ripple-surface--hover::before{opacity:0.12;opacity:var(--mdc-ripple-hover-opacity, 0.12)}.mdc-ripple-surface--accent--selected.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--accent--selected:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-focus-opacity, 0.2)}.mdc-ripple-surface--accent--selected:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--accent--selected:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.2;opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--accent--selected.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-press-opacity, 0.2)}.mdc-ripple-surface--disabled{opacity:0}.mdc-ripple-surface--internal-use-state-layer-custom-properties::before,.mdc-ripple-surface--internal-use-state-layer-custom-properties::after{background-color:#000;background-color:var(--mdc-ripple-hover-state-layer-color, #000)}.mdc-ripple-surface--internal-use-state-layer-custom-properties:hover::before,.mdc-ripple-surface--internal-use-state-layer-custom-properties.mdc-ripple-surface--hover::before{opacity:0.04;opacity:var(--mdc-ripple-hover-state-layer-opacity, 0.04)}.mdc-ripple-surface--internal-use-state-layer-custom-properties.mdc-ripple-upgraded--background-focused::before,.mdc-ripple-surface--internal-use-state-layer-custom-properties:not(.mdc-ripple-upgraded):focus::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-state-layer-opacity, 0.12)}.mdc-ripple-surface--internal-use-state-layer-custom-properties:not(.mdc-ripple-upgraded)::after{transition:opacity 150ms linear}.mdc-ripple-surface--internal-use-state-layer-custom-properties:not(.mdc-ripple-upgraded):active::after{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-pressed-state-layer-opacity, 0.12)}.mdc-ripple-surface--internal-use-state-layer-custom-properties.mdc-ripple-upgraded{--mdc-ripple-fg-opacity:var(--mdc-ripple-pressed-state-layer-opacity, 0.12)}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,un=ht`mwc-list ::slotted([mwc-list-item]:not([twoline])),mwc-list ::slotted([noninteractive]:not([twoline])){height:var(--mdc-menu-item-height, 48px)}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,pn=ht`.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)}` -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */,fn=ht`.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}:host{display:block;position:absolute;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] :host,:host([dir=rtl]){text-align:right}::slotted(.mdc-floating-label){display:inline-block;position:relative;top:17px;bottom:auto;max-width:100%}::slotted(.mdc-floating-label--float-above){text-overflow:clip}.mdc-notched-outline--upgraded ::slotted(.mdc-floating-label--float-above){max-width:calc(100% / 0.75)}.mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-notched-outline .mdc-notched-outline__leading,.mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{border-color:var(--mdc-notched-outline-border-color, var(--mdc-theme-primary, #6200ee));border-width:1px;border-width:var(--mdc-notched-outline-stroke-width, 1px)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0;padding-top:var(--mdc-notched-outline-notch-offset, 0)}`,mn={"mwc-select":class extends Oi{static get styles(){return dn}},"mwc-list":class extends Di{static get styles(){return ln}},"mwc-list-item":class extends Fi{static get styles(){return cn}},"mwc-ripple":class extends en{static get styles(){return hn}},"mwc-menu":class extends $i{static get styles(){return un}},"mwc-menu-surface":class extends Xi{static get styles(){return pn}},"mwc-notched-outline":class extends an{static get styles(){return fn}}};function vn(t,e,i){if(void 0!==e) -/** - * @license - * Copyright 2021 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -return function(t,e,i){const n=t.constructor;if(!i){const t=`__${e}`;if(!(i=n.getPropertyDescriptor(e,t)))throw new Error("@ariaProperty must be used after a @property decorator")}const o=i;let r="";if(!o.set)throw new Error(`@ariaProperty requires a setter for ${e}`);if(t.dispatchWizEvent)return i;const s={configurable:!0,enumerable:!0,set(t){if(""===r){const t=n.getPropertyOptions(e);r="string"==typeof t.attribute?t.attribute:e}this.hasAttribute(r)&&this.removeAttribute(r),o.set.call(this,t)}};return o.get&&(s.get=function(){return o.get.call(this)}),s}(t,e,i);throw new Error("@ariaProperty only supports TypeScript Decorators")} -/** - * @license - * Copyright 2018 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */var gn={CHECKED:"mdc-switch--checked",DISABLED:"mdc-switch--disabled"},yn={ARIA_CHECKED_ATTR:"aria-checked",NATIVE_CONTROL_SELECTOR:".mdc-switch__native-control",RIPPLE_SURFACE_SELECTOR:".mdc-switch__thumb-underlay"},bn=function(t){function e(i){return t.call(this,n(n({},e.defaultAdapter),i))||this}return i(e,t),Object.defineProperty(e,"strings",{get:function(){return yn},enumerable:!1,configurable:!0}),Object.defineProperty(e,"cssClasses",{get:function(){return gn},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},setNativeControlChecked:function(){},setNativeControlDisabled:function(){},setNativeControlAttr:function(){}}},enumerable:!1,configurable:!0}),e.prototype.setChecked=function(t){this.adapter.setNativeControlChecked(t),this.updateAriaChecked(t),this.updateCheckedStyling(t)},e.prototype.setDisabled=function(t){this.adapter.setNativeControlDisabled(t),t?this.adapter.addClass(gn.DISABLED):this.adapter.removeClass(gn.DISABLED)},e.prototype.handleChange=function(t){var e=t.target;this.updateAriaChecked(e.checked),this.updateCheckedStyling(e.checked)},e.prototype.updateCheckedStyling=function(t){t?this.adapter.addClass(gn.CHECKED):this.adapter.removeClass(gn.CHECKED)},e.prototype.updateAriaChecked=function(t){this.adapter.setNativeControlAttr(yn.ARIA_CHECKED_ATTR,""+!!t)},e}(ee); -/** - * @license - * Copyright 2018 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ -class xn extends he{constructor(){super(...arguments),this.checked=!1,this.disabled=!1,this.shouldRenderRipple=!1,this.mdcFoundationClass=bn,this.rippleHandlers=new Mi((()=>(this.shouldRenderRipple=!0,this.ripple)))}changeHandler(t){this.mdcFoundation.handleChange(t),this.checked=this.formElement.checked}createAdapter(){return Object.assign(Object.assign({},se(this.mdcRoot)),{setNativeControlChecked:t=>{this.formElement.checked=t},setNativeControlDisabled:t=>{this.formElement.disabled=t},setNativeControlAttr:(t,e)=>{this.formElement.setAttribute(t,e)}})}renderRipple(){return this.shouldRenderRipple?$` - - `:""}focus(){const t=this.formElement;t&&(this.rippleHandlers.startFocus(),t.focus())}blur(){const t=this.formElement;t&&(this.rippleHandlers.endFocus(),t.blur())}click(){this.formElement&&!this.disabled&&(this.formElement.focus(),this.formElement.click())}firstUpdated(){super.firstUpdated(),this.shadowRoot&&this.mdcRoot.addEventListener("change",(t=>{this.dispatchEvent(new Event("change",t))}))}render(){return $` -
-
-
- ${this.renderRipple()} -
- -
-
-
`}handleRippleMouseDown(t){const e=()=>{window.removeEventListener("mouseup",e),this.handleRippleDeactivate()};window.addEventListener("mouseup",e),this.rippleHandlers.startPress(t)}handleRippleTouchStart(t){this.rippleHandlers.startPress(t)}handleRippleDeactivate(){this.rippleHandlers.endPress()}handleRippleMouseEnter(){this.rippleHandlers.startHover()}handleRippleMouseLeave(){this.rippleHandlers.endHover()}handleRippleFocus(){this.rippleHandlers.startFocus()}handleRippleBlur(){this.rippleHandlers.endFocus()}}o([At({type:Boolean}),ve((function(t){this.mdcFoundation.setChecked(t)}))],xn.prototype,"checked",void 0),o([At({type:Boolean}),ve((function(t){this.mdcFoundation.setDisabled(t)}))],xn.prototype,"disabled",void 0),o([vn,At({attribute:"aria-label"})],xn.prototype,"ariaLabel",void 0),o([vn,At({attribute:"aria-labelledby"})],xn.prototype,"ariaLabelledBy",void 0),o([Mt(".mdc-switch")],xn.prototype,"mdcRoot",void 0),o([Mt("input")],xn.prototype,"formElement",void 0),o([Ft("mwc-ripple")],xn.prototype,"ripple",void 0),o([Rt()],xn.prototype,"shouldRenderRipple",void 0),o([Dt({passive:!0})],xn.prototype,"handleRippleMouseDown",null),o([Dt({passive:!0})],xn.prototype,"handleRippleTouchStart",null); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const _n=ht`.mdc-switch__thumb-underlay{left:-14px;right:initial;top:-17px;width:48px;height:48px}[dir=rtl] .mdc-switch__thumb-underlay,.mdc-switch__thumb-underlay[dir=rtl]{left:initial;right:-14px}.mdc-switch__native-control{width:64px;height:48px}.mdc-switch{display:inline-block;position:relative;outline:none;user-select:none}.mdc-switch.mdc-switch--checked .mdc-switch__track{background-color:#018786;background-color:var(--mdc-theme-secondary, #018786)}.mdc-switch.mdc-switch--checked .mdc-switch__thumb{background-color:#018786;background-color:var(--mdc-theme-secondary, #018786);border-color:#018786;border-color:var(--mdc-theme-secondary, #018786)}.mdc-switch:not(.mdc-switch--checked) .mdc-switch__track{background-color:#000;background-color:var(--mdc-theme-on-surface, #000)}.mdc-switch:not(.mdc-switch--checked) .mdc-switch__thumb{background-color:#fff;background-color:var(--mdc-theme-surface, #fff);border-color:#fff;border-color:var(--mdc-theme-surface, #fff)}.mdc-switch__native-control{left:0;right:initial;position:absolute;top:0;margin:0;opacity:0;cursor:pointer;pointer-events:auto;transition:transform 90ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-switch__native-control,.mdc-switch__native-control[dir=rtl]{left:initial;right:0}.mdc-switch__track{box-sizing:border-box;width:36px;height:14px;border:1px solid transparent;border-radius:7px;opacity:.38;transition:opacity 90ms cubic-bezier(0.4, 0, 0.2, 1),background-color 90ms cubic-bezier(0.4, 0, 0.2, 1),border-color 90ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-switch__thumb-underlay{display:flex;position:absolute;align-items:center;justify-content:center;transform:translateX(0);transition:transform 90ms cubic-bezier(0.4, 0, 0.2, 1),background-color 90ms cubic-bezier(0.4, 0, 0.2, 1),border-color 90ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-switch__thumb{box-shadow:0px 3px 1px -2px rgba(0, 0, 0, 0.2),0px 2px 2px 0px rgba(0, 0, 0, 0.14),0px 1px 5px 0px rgba(0,0,0,.12);box-sizing:border-box;width:20px;height:20px;border:10px solid;border-radius:50%;pointer-events:none;z-index:1}.mdc-switch--checked .mdc-switch__track{opacity:.54}.mdc-switch--checked .mdc-switch__thumb-underlay{transform:translateX(16px)}[dir=rtl] .mdc-switch--checked .mdc-switch__thumb-underlay,.mdc-switch--checked .mdc-switch__thumb-underlay[dir=rtl]{transform:translateX(-16px)}.mdc-switch--checked .mdc-switch__native-control{transform:translateX(-16px)}[dir=rtl] .mdc-switch--checked .mdc-switch__native-control,.mdc-switch--checked .mdc-switch__native-control[dir=rtl]{transform:translateX(16px)}.mdc-switch--disabled{opacity:.38;pointer-events:none}.mdc-switch--disabled .mdc-switch__thumb{border-width:1px}.mdc-switch--disabled .mdc-switch__native-control{cursor:default;pointer-events:none}:host{display:inline-flex;outline:none;-webkit-tap-highlight-color:transparent}`,wn={"mwc-switch":class extends xn{static get styles(){return _n}},"mwc-ripple":class extends en{static get styles(){return hn}}}; -/** - * @license - * Copyright 2016 Google Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -var En={ARIA_CONTROLS:"aria-controls",ARIA_DESCRIBEDBY:"aria-describedby",INPUT_SELECTOR:".mdc-text-field__input",LABEL_SELECTOR:".mdc-floating-label",LEADING_ICON_SELECTOR:".mdc-text-field__icon--leading",LINE_RIPPLE_SELECTOR:".mdc-line-ripple",OUTLINE_SELECTOR:".mdc-notched-outline",PREFIX_SELECTOR:".mdc-text-field__affix--prefix",SUFFIX_SELECTOR:".mdc-text-field__affix--suffix",TRAILING_ICON_SELECTOR:".mdc-text-field__icon--trailing"},kn={DISABLED:"mdc-text-field--disabled",FOCUSED:"mdc-text-field--focused",HELPER_LINE:"mdc-text-field-helper-line",INVALID:"mdc-text-field--invalid",LABEL_FLOATING:"mdc-text-field--label-floating",NO_LABEL:"mdc-text-field--no-label",OUTLINED:"mdc-text-field--outlined",ROOT:"mdc-text-field",TEXTAREA:"mdc-text-field--textarea",WITH_LEADING_ICON:"mdc-text-field--with-leading-icon",WITH_TRAILING_ICON:"mdc-text-field--with-trailing-icon",WITH_INTERNAL_COUNTER:"mdc-text-field--with-internal-counter"},On={LABEL_SCALE:.75},Cn=["pattern","min","max","required","step","minlength","maxlength"],Sn=["color","date","datetime-local","month","range","time","week"],Tn=["mousedown","touchstart"],In=["click","keydown"],An=function(t){function e(i,o){void 0===o&&(o={});var r=t.call(this,n(n({},e.defaultAdapter),i))||this;return r.isFocused=!1,r.receivedUserInput=!1,r.valid=!0,r.useNativeValidation=!0,r.validateOnValueChange=!0,r.helperText=o.helperText,r.characterCounter=o.characterCounter,r.leadingIcon=o.leadingIcon,r.trailingIcon=o.trailingIcon,r.inputFocusHandler=function(){r.activateFocus()},r.inputBlurHandler=function(){r.deactivateFocus()},r.inputInputHandler=function(){r.handleInput()},r.setPointerXOffset=function(t){r.setTransformOrigin(t)},r.textFieldInteractionHandler=function(){r.handleTextFieldInteraction()},r.validationAttributeChangeHandler=function(t){r.handleValidationAttributeChange(t)},r}return i(e,t),Object.defineProperty(e,"cssClasses",{get:function(){return kn},enumerable:!1,configurable:!0}),Object.defineProperty(e,"strings",{get:function(){return En},enumerable:!1,configurable:!0}),Object.defineProperty(e,"numbers",{get:function(){return On},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldAlwaysFloat",{get:function(){var t=this.getNativeInput().type;return Sn.indexOf(t)>=0},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldFloat",{get:function(){return this.shouldAlwaysFloat||this.isFocused||!!this.getValue()||this.isBadInput()},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"shouldShake",{get:function(){return!this.isFocused&&!this.isValid()&&!!this.getValue()},enumerable:!1,configurable:!0}),Object.defineProperty(e,"defaultAdapter",{get:function(){return{addClass:function(){},removeClass:function(){},hasClass:function(){return!0},setInputAttr:function(){},removeInputAttr:function(){},registerTextFieldInteractionHandler:function(){},deregisterTextFieldInteractionHandler:function(){},registerInputInteractionHandler:function(){},deregisterInputInteractionHandler:function(){},registerValidationAttributeChangeHandler:function(){return new MutationObserver((function(){}))},deregisterValidationAttributeChangeHandler:function(){},getNativeInput:function(){return null},isFocused:function(){return!1},activateLineRipple:function(){},deactivateLineRipple:function(){},setLineRippleTransformOrigin:function(){},shakeLabel:function(){},floatLabel:function(){},setLabelRequired:function(){},hasLabel:function(){return!1},getLabelWidth:function(){return 0},hasOutline:function(){return!1},notchOutline:function(){},closeOutline:function(){}}},enumerable:!1,configurable:!0}),e.prototype.init=function(){var t,e,i,n;this.adapter.hasLabel()&&this.getNativeInput().required&&this.adapter.setLabelRequired(!0),this.adapter.isFocused()?this.inputFocusHandler():this.adapter.hasLabel()&&this.shouldFloat&&(this.notchOutline(!0),this.adapter.floatLabel(!0),this.styleFloating(!0)),this.adapter.registerInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.registerInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.registerInputInteractionHandler("input",this.inputInputHandler);try{for(var o=r(Tn),s=o.next();!s.done;s=o.next()){var a=s.value;this.adapter.registerInputInteractionHandler(a,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{s&&!s.done&&(e=o.return)&&e.call(o)}finally{if(t)throw t.error}}try{for(var d=r(In),l=d.next();!l.done;l=d.next()){a=l.value;this.adapter.registerTextFieldInteractionHandler(a,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{l&&!l.done&&(n=d.return)&&n.call(d)}finally{if(i)throw i.error}}this.validationObserver=this.adapter.registerValidationAttributeChangeHandler(this.validationAttributeChangeHandler),this.setcharacterCounter(this.getValue().length)},e.prototype.destroy=function(){var t,e,i,n;this.adapter.deregisterInputInteractionHandler("focus",this.inputFocusHandler),this.adapter.deregisterInputInteractionHandler("blur",this.inputBlurHandler),this.adapter.deregisterInputInteractionHandler("input",this.inputInputHandler);try{for(var o=r(Tn),s=o.next();!s.done;s=o.next()){var a=s.value;this.adapter.deregisterInputInteractionHandler(a,this.setPointerXOffset)}}catch(e){t={error:e}}finally{try{s&&!s.done&&(e=o.return)&&e.call(o)}finally{if(t)throw t.error}}try{for(var d=r(In),l=d.next();!l.done;l=d.next()){a=l.value;this.adapter.deregisterTextFieldInteractionHandler(a,this.textFieldInteractionHandler)}}catch(t){i={error:t}}finally{try{l&&!l.done&&(n=d.return)&&n.call(d)}finally{if(i)throw i.error}}this.adapter.deregisterValidationAttributeChangeHandler(this.validationObserver)},e.prototype.handleTextFieldInteraction=function(){var t=this.adapter.getNativeInput();t&&t.disabled||(this.receivedUserInput=!0)},e.prototype.handleValidationAttributeChange=function(t){var e=this;t.some((function(t){return Cn.indexOf(t)>-1&&(e.styleValidity(!0),e.adapter.setLabelRequired(e.getNativeInput().required),!0)})),t.indexOf("maxlength")>-1&&this.setcharacterCounter(this.getValue().length)},e.prototype.notchOutline=function(t){if(this.adapter.hasOutline()&&this.adapter.hasLabel())if(t){var e=this.adapter.getLabelWidth()*On.LABEL_SCALE;this.adapter.notchOutline(e)}else this.adapter.closeOutline()},e.prototype.activateFocus=function(){this.isFocused=!0,this.styleFocused(this.isFocused),this.adapter.activateLineRipple(),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),!this.helperText||!this.helperText.isPersistent()&&this.helperText.isValidation()&&this.valid||this.helperText.showToScreenReader()},e.prototype.setTransformOrigin=function(t){if(!this.isDisabled()&&!this.adapter.hasOutline()){var e=t.touches,i=e?e[0]:t,n=i.target.getBoundingClientRect(),o=i.clientX-n.left;this.adapter.setLineRippleTransformOrigin(o)}},e.prototype.handleInput=function(){this.autoCompleteFocus(),this.setcharacterCounter(this.getValue().length)},e.prototype.autoCompleteFocus=function(){this.receivedUserInput||this.activateFocus()},e.prototype.deactivateFocus=function(){this.isFocused=!1,this.adapter.deactivateLineRipple();var t=this.isValid();this.styleValidity(t),this.styleFocused(this.isFocused),this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.adapter.shakeLabel(this.shouldShake)),this.shouldFloat||(this.receivedUserInput=!1)},e.prototype.getValue=function(){return this.getNativeInput().value},e.prototype.setValue=function(t){if(this.getValue()!==t&&(this.getNativeInput().value=t),this.setcharacterCounter(t.length),this.validateOnValueChange){var e=this.isValid();this.styleValidity(e)}this.adapter.hasLabel()&&(this.notchOutline(this.shouldFloat),this.adapter.floatLabel(this.shouldFloat),this.styleFloating(this.shouldFloat),this.validateOnValueChange&&this.adapter.shakeLabel(this.shouldShake))},e.prototype.isValid=function(){return this.useNativeValidation?this.isNativeInputValid():this.valid},e.prototype.setValid=function(t){this.valid=t,this.styleValidity(t);var e=!t&&!this.isFocused&&!!this.getValue();this.adapter.hasLabel()&&this.adapter.shakeLabel(e)},e.prototype.setValidateOnValueChange=function(t){this.validateOnValueChange=t},e.prototype.getValidateOnValueChange=function(){return this.validateOnValueChange},e.prototype.setUseNativeValidation=function(t){this.useNativeValidation=t},e.prototype.isDisabled=function(){return this.getNativeInput().disabled},e.prototype.setDisabled=function(t){this.getNativeInput().disabled=t,this.styleDisabled(t)},e.prototype.setHelperTextContent=function(t){this.helperText&&this.helperText.setContent(t)},e.prototype.setLeadingIconAriaLabel=function(t){this.leadingIcon&&this.leadingIcon.setAriaLabel(t)},e.prototype.setLeadingIconContent=function(t){this.leadingIcon&&this.leadingIcon.setContent(t)},e.prototype.setTrailingIconAriaLabel=function(t){this.trailingIcon&&this.trailingIcon.setAriaLabel(t)},e.prototype.setTrailingIconContent=function(t){this.trailingIcon&&this.trailingIcon.setContent(t)},e.prototype.setcharacterCounter=function(t){if(this.characterCounter){var e=this.getNativeInput().maxLength;if(-1===e)throw new Error("MDCTextFieldFoundation: Expected maxlength html property on text input or textarea.");this.characterCounter.setCounterValue(t,e)}},e.prototype.isBadInput=function(){return this.getNativeInput().validity.badInput||!1},e.prototype.isNativeInputValid=function(){return this.getNativeInput().validity.valid},e.prototype.styleValidity=function(t){var i=e.cssClasses.INVALID;if(t?this.adapter.removeClass(i):this.adapter.addClass(i),this.helperText){if(this.helperText.setValidity(t),!this.helperText.isValidation())return;var n=this.helperText.isVisible(),o=this.helperText.getId();n&&o?this.adapter.setInputAttr(En.ARIA_DESCRIBEDBY,o):this.adapter.removeInputAttr(En.ARIA_DESCRIBEDBY)}},e.prototype.styleFocused=function(t){var i=e.cssClasses.FOCUSED;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.styleDisabled=function(t){var i=e.cssClasses,n=i.DISABLED,o=i.INVALID;t?(this.adapter.addClass(n),this.adapter.removeClass(o)):this.adapter.removeClass(n),this.leadingIcon&&this.leadingIcon.setDisabled(t),this.trailingIcon&&this.trailingIcon.setDisabled(t)},e.prototype.styleFloating=function(t){var i=e.cssClasses.LABEL_FLOATING;t?this.adapter.addClass(i):this.adapter.removeClass(i)},e.prototype.getNativeInput=function(){return(this.adapter?this.adapter.getNativeInput():null)||{disabled:!1,maxLength:-1,required:!1,type:"input",validity:{badInput:!1,valid:!0},value:""}},e}(ee); -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */ -const Rn={},Pn=xe(class extends _e{constructor(t){if(super(t),t.type!==ye&&t.type!==ge&&t.type!==be)throw Error("The `live` directive is not allowed on child or event bindings");if(!(t=>void 0===t.strings)(t))throw Error("`live` bindings can only contain a single expression")}render(t){return t}update(t,[e]){if(e===U||e===W)return e;const i=t.element,n=t.name;if(t.type===ye){if(e===i[n])return U}else if(t.type===be){if(!!e===i.hasAttribute(n))return U}else if(t.type===ge&&i.getAttribute(n)===e+"")return U;return((t,e=Rn)=>{t._$AH=e; -/** - * @license - * Copyright 2020 Google LLC - * SPDX-License-Identifier: BSD-3-Clause - */})(t),e}}),Dn=["touchstart","touchmove","scroll","mousewheel"],Mn=(t={})=>{const e={};for(const i in t)e[i]=t[i];return Object.assign({badInput:!1,customError:!1,patternMismatch:!1,rangeOverflow:!1,rangeUnderflow:!1,stepMismatch:!1,tooLong:!1,tooShort:!1,typeMismatch:!1,valid:!0,valueMissing:!1},e)};class Fn extends me{constructor(){super(...arguments),this.mdcFoundationClass=An,this.value="",this.type="text",this.placeholder="",this.label="",this.icon="",this.iconTrailing="",this.disabled=!1,this.required=!1,this.minLength=-1,this.maxLength=-1,this.outlined=!1,this.helper="",this.validateOnInitialRender=!1,this.validationMessage="",this.autoValidate=!1,this.pattern="",this.min="",this.max="",this.step=null,this.size=null,this.helperPersistent=!1,this.charCounter=!1,this.endAligned=!1,this.prefix="",this.suffix="",this.name="",this.readOnly=!1,this.autocapitalize="",this.outlineOpen=!1,this.outlineWidth=0,this.isUiValid=!0,this.focused=!1,this._validity=Mn(),this.validityTransform=null}get validity(){return this._checkValidity(this.value),this._validity}get willValidate(){return this.formElement.willValidate}get selectionStart(){return this.formElement.selectionStart}get selectionEnd(){return this.formElement.selectionEnd}focus(){const t=new CustomEvent("focus");this.formElement.dispatchEvent(t),this.formElement.focus()}blur(){const t=new CustomEvent("blur");this.formElement.dispatchEvent(t),this.formElement.blur()}select(){this.formElement.select()}setSelectionRange(t,e,i){this.formElement.setSelectionRange(t,e,i)}update(t){t.has("autoValidate")&&this.mdcFoundation&&this.mdcFoundation.setValidateOnValueChange(this.autoValidate),t.has("value")&&"string"!=typeof this.value&&(this.value=`${this.value}`),super.update(t)}setFormData(t){this.name&&t.append(this.name,this.value)}render(){const t=this.charCounter&&-1!==this.maxLength,e=!!this.helper||!!this.validationMessage||t,i={"mdc-text-field--disabled":this.disabled,"mdc-text-field--no-label":!this.label,"mdc-text-field--filled":!this.outlined,"mdc-text-field--outlined":this.outlined,"mdc-text-field--with-leading-icon":this.icon,"mdc-text-field--with-trailing-icon":this.iconTrailing,"mdc-text-field--end-aligned":this.endAligned};return $` - - ${this.renderHelperText(e,t)} - `}updated(t){t.has("value")&&void 0!==t.get("value")&&(this.mdcFoundation.setValue(this.value),this.autoValidate&&this.reportValidity())}renderRipple(){return this.outlined?"":$` - - `}renderOutline(){return this.outlined?$` - - ${this.renderLabel()} - `:""}renderLabel(){return this.label?$` - ${this.label} - `:""}renderLeadingIcon(){return this.icon?this.renderIcon(this.icon):""}renderTrailingIcon(){return this.iconTrailing?this.renderIcon(this.iconTrailing,!0):""}renderIcon(t,e=!1){return $`${t}`}renderPrefix(){return this.prefix?this.renderAffix(this.prefix):""}renderSuffix(){return this.suffix?this.renderAffix(this.suffix,!0):""}renderAffix(t,e=!1){return $` - ${t}`}renderInput(t){const e=-1===this.minLength?void 0:this.minLength,i=-1===this.maxLength?void 0:this.maxLength,n=this.autocapitalize?this.autocapitalize:void 0,o=this.validationMessage&&!this.isUiValid,r=this.label?"label":void 0,s=t?"helper-text":void 0,a=this.focused||this.helperPersistent||o?"helper-text":void 0;return $` - `}renderLineRipple(){return this.outlined?"":$` - - `}renderHelperText(t,e){const i=this.validationMessage&&!this.isUiValid,n={"mdc-text-field-helper-text--persistent":this.helperPersistent,"mdc-text-field-helper-text--validation-msg":i},o=this.focused||this.helperPersistent||i?void 0:"true",r=i?this.validationMessage:this.helper;return t?$` -
-
${r}
- ${this.renderCharCounter(e)} -
`:""}renderCharCounter(t){const e=Math.min(this.value.length,this.maxLength);return t?$` - ${e} / ${this.maxLength}`:""}onInputFocus(){this.focused=!0}onInputBlur(){this.focused=!1,this.reportValidity()}checkValidity(){const t=this._checkValidity(this.value);if(!t){const t=new Event("invalid",{bubbles:!1,cancelable:!0});this.dispatchEvent(t)}return t}reportValidity(){const t=this.checkValidity();return this.mdcFoundation.setValid(t),this.isUiValid=t,t}_checkValidity(t){const e=this.formElement.validity;let i=Mn(e);if(this.validityTransform){const e=this.validityTransform(t,i);i=Object.assign(Object.assign({},i),e),this.mdcFoundation.setUseNativeValidation(!1)}else this.mdcFoundation.setUseNativeValidation(!0);return this._validity=i,this._validity.valid}setCustomValidity(t){this.validationMessage=t,this.formElement.setCustomValidity(t)}handleInputChange(){this.value=this.formElement.value}createAdapter(){return Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({},this.getRootAdapterMethods()),this.getInputAdapterMethods()),this.getLabelAdapterMethods()),this.getLineRippleAdapterMethods()),this.getOutlineAdapterMethods())}getRootAdapterMethods(){return Object.assign({registerTextFieldInteractionHandler:(t,e)=>this.addEventListener(t,e),deregisterTextFieldInteractionHandler:(t,e)=>this.removeEventListener(t,e),registerValidationAttributeChangeHandler:t=>{const e=new MutationObserver((e=>{t((t=>t.map((t=>t.attributeName)).filter((t=>t)))(e))}));return e.observe(this.formElement,{attributes:!0}),e},deregisterValidationAttributeChangeHandler:t=>t.disconnect()},se(this.mdcRoot))}getInputAdapterMethods(){return{getNativeInput:()=>this.formElement,setInputAttr:()=>{},removeInputAttr:()=>{},isFocused:()=>!!this.shadowRoot&&this.shadowRoot.activeElement===this.formElement,registerInputInteractionHandler:(t,e)=>this.formElement.addEventListener(t,e,{passive:t in Dn}),deregisterInputInteractionHandler:(t,e)=>this.formElement.removeEventListener(t,e)}}getLabelAdapterMethods(){return{floatLabel:t=>this.labelElement&&this.labelElement.floatingLabelFoundation.float(t),getLabelWidth:()=>this.labelElement?this.labelElement.floatingLabelFoundation.getWidth():0,hasLabel:()=>Boolean(this.labelElement),shakeLabel:t=>this.labelElement&&this.labelElement.floatingLabelFoundation.shake(t),setLabelRequired:t=>{this.labelElement&&this.labelElement.floatingLabelFoundation.setRequired(t)}}}getLineRippleAdapterMethods(){return{activateLineRipple:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.activate()},deactivateLineRipple:()=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.deactivate()},setLineRippleTransformOrigin:t=>{this.lineRippleElement&&this.lineRippleElement.lineRippleFoundation.setRippleCenter(t)}}}async getUpdateComplete(){var t;const e=await super.getUpdateComplete();return await(null===(t=this.outlineElement)||void 0===t?void 0:t.updateComplete),e}firstUpdated(){var t;super.firstUpdated(),this.mdcFoundation.setValidateOnValueChange(this.autoValidate),this.validateOnInitialRender&&this.reportValidity(),null===(t=this.outlineElement)||void 0===t||t.updateComplete.then((()=>{var t;this.outlineWidth=(null===(t=this.labelElement)||void 0===t?void 0:t.floatingLabelFoundation.getWidth())||0}))}getOutlineAdapterMethods(){return{closeOutline:()=>this.outlineElement&&(this.outlineOpen=!1),hasOutline:()=>Boolean(this.outlineElement),notchOutline:t=>{this.outlineElement&&!this.outlineOpen&&(this.outlineWidth=t,this.outlineOpen=!0)}}}async layout(){await this.updateComplete;const t=this.labelElement;if(!t)return void(this.outlineOpen=!1);const e=!!this.label&&!!this.value;if(t.floatingLabelFoundation.float(e),!this.outlined)return;this.outlineOpen=e,await this.updateComplete;const i=t.floatingLabelFoundation.getWidth();this.outlineOpen&&(this.outlineWidth=i,await this.updateComplete)}}o([Mt(".mdc-text-field")],Fn.prototype,"mdcRoot",void 0),o([Mt("input")],Fn.prototype,"formElement",void 0),o([Mt(".mdc-floating-label")],Fn.prototype,"labelElement",void 0),o([Mt(".mdc-line-ripple")],Fn.prototype,"lineRippleElement",void 0),o([Mt("mwc-notched-outline")],Fn.prototype,"outlineElement",void 0),o([Mt(".mdc-notched-outline__notch")],Fn.prototype,"notchElement",void 0),o([At({type:String})],Fn.prototype,"value",void 0),o([At({type:String})],Fn.prototype,"type",void 0),o([At({type:String})],Fn.prototype,"placeholder",void 0),o([At({type:String}),ve((function(t,e){void 0!==e&&this.label!==e&&this.layout()}))],Fn.prototype,"label",void 0),o([At({type:String})],Fn.prototype,"icon",void 0),o([At({type:String})],Fn.prototype,"iconTrailing",void 0),o([At({type:Boolean,reflect:!0})],Fn.prototype,"disabled",void 0),o([At({type:Boolean})],Fn.prototype,"required",void 0),o([At({type:Number})],Fn.prototype,"minLength",void 0),o([At({type:Number})],Fn.prototype,"maxLength",void 0),o([At({type:Boolean,reflect:!0}),ve((function(t,e){void 0!==e&&this.outlined!==e&&this.layout()}))],Fn.prototype,"outlined",void 0),o([At({type:String})],Fn.prototype,"helper",void 0),o([At({type:Boolean})],Fn.prototype,"validateOnInitialRender",void 0),o([At({type:String})],Fn.prototype,"validationMessage",void 0),o([At({type:Boolean})],Fn.prototype,"autoValidate",void 0),o([At({type:String})],Fn.prototype,"pattern",void 0),o([At({type:String})],Fn.prototype,"min",void 0),o([At({type:String})],Fn.prototype,"max",void 0),o([At({type:String})],Fn.prototype,"step",void 0),o([At({type:Number})],Fn.prototype,"size",void 0),o([At({type:Boolean})],Fn.prototype,"helperPersistent",void 0),o([At({type:Boolean})],Fn.prototype,"charCounter",void 0),o([At({type:Boolean})],Fn.prototype,"endAligned",void 0),o([At({type:String})],Fn.prototype,"prefix",void 0),o([At({type:String})],Fn.prototype,"suffix",void 0),o([At({type:String})],Fn.prototype,"name",void 0),o([At({type:String})],Fn.prototype,"inputMode",void 0),o([At({type:Boolean})],Fn.prototype,"readOnly",void 0),o([At({type:String})],Fn.prototype,"autocapitalize",void 0),o([Rt()],Fn.prototype,"outlineOpen",void 0),o([Rt()],Fn.prototype,"outlineWidth",void 0),o([Rt()],Fn.prototype,"isUiValid",void 0),o([Rt()],Fn.prototype,"focused",void 0),o([Dt({passive:!0})],Fn.prototype,"handleInputChange",null); -/** - * @license - * Copyright 2021 Google LLC - * SPDX-LIcense-Identifier: Apache-2.0 - */ -const Nn=ht`.mdc-floating-label{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);position:absolute;left:0;-webkit-transform-origin:left top;transform-origin:left top;line-height:1.15rem;text-align:left;text-overflow:ellipsis;white-space:nowrap;cursor:text;overflow:hidden;will-change:transform;transition:transform 150ms cubic-bezier(0.4, 0, 0.2, 1),color 150ms cubic-bezier(0.4, 0, 0.2, 1)}[dir=rtl] .mdc-floating-label,.mdc-floating-label[dir=rtl]{right:0;left:auto;-webkit-transform-origin:right top;transform-origin:right top;text-align:right}.mdc-floating-label--float-above{cursor:auto}.mdc-floating-label--required::after{margin-left:1px;margin-right:0px;content:"*"}[dir=rtl] .mdc-floating-label--required::after,.mdc-floating-label--required[dir=rtl]::after{margin-left:0;margin-right:1px}.mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-standard 250ms 1}@keyframes mdc-floating-label-shake-float-above-standard{0%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-106%) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-106%) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-106%) scale(0.75)}}.mdc-line-ripple::before,.mdc-line-ripple::after{position:absolute;bottom:0;left:0;width:100%;border-bottom-style:solid;content:""}.mdc-line-ripple::before{border-bottom-width:1px}.mdc-line-ripple::before{z-index:1}.mdc-line-ripple::after{transform:scaleX(0);border-bottom-width:2px;opacity:0;z-index:2}.mdc-line-ripple::after{transition:transform 180ms cubic-bezier(0.4, 0, 0.2, 1),opacity 180ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-line-ripple--active::after{transform:scaleX(1);opacity:1}.mdc-line-ripple--deactivating::after{opacity:0}.mdc-notched-outline{display:flex;position:absolute;top:0;right:0;left:0;box-sizing:border-box;width:100%;max-width:100%;height:100%;text-align:left;pointer-events:none}[dir=rtl] .mdc-notched-outline,.mdc-notched-outline[dir=rtl]{text-align:right}.mdc-notched-outline__leading,.mdc-notched-outline__notch,.mdc-notched-outline__trailing{box-sizing:border-box;height:100%;border-top:1px solid;border-bottom:1px solid;pointer-events:none}.mdc-notched-outline__leading{border-left:1px solid;border-right:none;width:12px}[dir=rtl] .mdc-notched-outline__leading,.mdc-notched-outline__leading[dir=rtl]{border-left:none;border-right:1px solid}.mdc-notched-outline__trailing{border-left:none;border-right:1px solid;flex-grow:1}[dir=rtl] .mdc-notched-outline__trailing,.mdc-notched-outline__trailing[dir=rtl]{border-left:1px solid;border-right:none}.mdc-notched-outline__notch{flex:0 0 auto;width:auto;max-width:calc(100% - 12px * 2)}.mdc-notched-outline .mdc-floating-label{display:inline-block;position:relative;max-width:100%}.mdc-notched-outline .mdc-floating-label--float-above{text-overflow:clip}.mdc-notched-outline--upgraded .mdc-floating-label--float-above{max-width:calc(100% / 0.75)}.mdc-notched-outline--notched .mdc-notched-outline__notch{padding-left:0;padding-right:8px;border-top:none}[dir=rtl] .mdc-notched-outline--notched .mdc-notched-outline__notch,.mdc-notched-outline--notched .mdc-notched-outline__notch[dir=rtl]{padding-left:8px;padding-right:0}.mdc-notched-outline--no-label .mdc-notched-outline__notch{display:none}@keyframes mdc-ripple-fg-radius-in{from{animation-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transform:translate(var(--mdc-ripple-fg-translate-start, 0)) scale(1)}to{transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}}@keyframes mdc-ripple-fg-opacity-in{from{animation-timing-function:linear;opacity:0}to{opacity:var(--mdc-ripple-fg-opacity, 0)}}@keyframes mdc-ripple-fg-opacity-out{from{animation-timing-function:linear;opacity:var(--mdc-ripple-fg-opacity, 0)}to{opacity:0}}.mdc-text-field--filled{--mdc-ripple-fg-size: 0;--mdc-ripple-left: 0;--mdc-ripple-top: 0;--mdc-ripple-fg-scale: 1;--mdc-ripple-fg-translate-end: 0;--mdc-ripple-fg-translate-start: 0;-webkit-tap-highlight-color:rgba(0,0,0,0);will-change:transform,opacity}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{position:absolute;border-radius:50%;opacity:0;pointer-events:none;content:""}.mdc-text-field--filled .mdc-text-field__ripple::before{transition:opacity 15ms linear,background-color 15ms linear;z-index:1;z-index:var(--mdc-ripple-z-index, 1)}.mdc-text-field--filled .mdc-text-field__ripple::after{z-index:0;z-index:var(--mdc-ripple-z-index, 0)}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::before{transform:scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{top:0;left:0;transform:scale(0);transform-origin:center center}.mdc-text-field--filled.mdc-ripple-upgraded--unbounded .mdc-text-field__ripple::after{top:var(--mdc-ripple-top, 0);left:var(--mdc-ripple-left, 0)}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-activation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-radius-in 225ms forwards,mdc-ripple-fg-opacity-in 75ms forwards}.mdc-text-field--filled.mdc-ripple-upgraded--foreground-deactivation .mdc-text-field__ripple::after{animation:mdc-ripple-fg-opacity-out 150ms;transform:translate(var(--mdc-ripple-fg-translate-end, 0)) scale(var(--mdc-ripple-fg-scale, 1))}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{top:calc(50% - 100%);left:calc(50% - 100%);width:200%;height:200%}.mdc-text-field--filled.mdc-ripple-upgraded .mdc-text-field__ripple::after{width:var(--mdc-ripple-fg-size, 100%);height:var(--mdc-ripple-fg-size, 100%)}.mdc-text-field__ripple{position:absolute;top:0;left:0;width:100%;height:100%;pointer-events:none}.mdc-text-field{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:0;border-bottom-left-radius:0;display:inline-flex;align-items:baseline;padding:0 16px;position:relative;box-sizing:border-box;overflow:hidden;will-change:opacity,transform,color}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input{color:rgba(0, 0, 0, 0.87)}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.54)}}@media all{.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.54)}}.mdc-text-field .mdc-text-field__input{caret-color:#6200ee;caret-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field-character-counter,.mdc-text-field:not(.mdc-text-field--disabled)+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.54)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.6)}.mdc-text-field .mdc-floating-label{top:50%;transform:translateY(-50%);pointer-events:none}.mdc-text-field__input{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);width:100%;min-width:0;border:none;border-radius:0;background:none;appearance:none;padding:0}.mdc-text-field__input::-ms-clear{display:none}.mdc-text-field__input::-webkit-calendar-picker-indicator{display:none}.mdc-text-field__input:focus{outline:none}.mdc-text-field__input:invalid{box-shadow:none}@media all{.mdc-text-field__input::placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field__input:-ms-input-placeholder{transition:opacity 67ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0}}@media all{.mdc-text-field--no-label .mdc-text-field__input::placeholder,.mdc-text-field--focused .mdc-text-field__input::placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}@media all{.mdc-text-field--no-label .mdc-text-field__input:-ms-input-placeholder,.mdc-text-field--focused .mdc-text-field__input:-ms-input-placeholder{transition-delay:40ms;transition-duration:110ms;opacity:1}}.mdc-text-field__affix{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-subtitle1-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:1rem;font-size:var(--mdc-typography-subtitle1-font-size, 1rem);font-weight:400;font-weight:var(--mdc-typography-subtitle1-font-weight, 400);letter-spacing:0.009375em;letter-spacing:var(--mdc-typography-subtitle1-letter-spacing, 0.009375em);text-decoration:inherit;text-decoration:var(--mdc-typography-subtitle1-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-subtitle1-text-transform, inherit);height:28px;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1);opacity:0;white-space:nowrap}.mdc-text-field--label-floating .mdc-text-field__affix,.mdc-text-field--no-label .mdc-text-field__affix{opacity:1}@supports(-webkit-hyphens: none){.mdc-text-field--outlined .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field__affix--prefix,.mdc-text-field__affix--prefix[dir=rtl]{padding-left:2px;padding-right:0}.mdc-text-field--end-aligned .mdc-text-field__affix--prefix{padding-left:0;padding-right:12px}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--end-aligned .mdc-text-field__affix--prefix[dir=rtl]{padding-left:12px;padding-right:0}.mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field__affix--suffix,.mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:12px}.mdc-text-field--end-aligned .mdc-text-field__affix--suffix{padding-left:2px;padding-right:0}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--end-aligned .mdc-text-field__affix--suffix[dir=rtl]{padding-left:0;padding-right:2px}.mdc-text-field--filled{height:56px}.mdc-text-field--filled .mdc-text-field__ripple::before,.mdc-text-field--filled .mdc-text-field__ripple::after{background-color:rgba(0, 0, 0, 0.87);background-color:var(--mdc-ripple-color, rgba(0, 0, 0, 0.87))}.mdc-text-field--filled:hover .mdc-text-field__ripple::before,.mdc-text-field--filled.mdc-ripple-surface--hover .mdc-text-field__ripple::before{opacity:0.04;opacity:var(--mdc-ripple-hover-opacity, 0.04)}.mdc-text-field--filled.mdc-ripple-upgraded--background-focused .mdc-text-field__ripple::before,.mdc-text-field--filled:not(.mdc-ripple-upgraded):focus .mdc-text-field__ripple::before{transition-duration:75ms;opacity:0.12;opacity:var(--mdc-ripple-focus-opacity, 0.12)}.mdc-text-field--filled::before{display:inline-block;width:0;height:40px;content:"";vertical-align:0}.mdc-text-field--filled:not(.mdc-text-field--disabled){background-color:whitesmoke}.mdc-text-field--filled:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42)}.mdc-text-field--filled:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--filled .mdc-line-ripple::after{border-bottom-color:#6200ee;border-bottom-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--filled .mdc-floating-label{left:16px;right:initial}[dir=rtl] .mdc-text-field--filled .mdc-floating-label,.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:16px}.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-106%) scale(0.75)}.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{height:100%}.mdc-text-field--filled.mdc-text-field--no-label .mdc-floating-label{display:none}.mdc-text-field--filled.mdc-text-field--no-label::before{display:none}@supports(-webkit-hyphens: none){.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__affix{align-items:center;align-self:center;display:inline-flex;height:100%}}.mdc-text-field--outlined{height:56px;overflow:visible}.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) scale(1)}.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) scale(0.75)}.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--outlined .mdc-text-field__input{height:100%}.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.38)}.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.87)}.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--outlined:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#6200ee;border-color:var(--mdc-theme-primary, #6200ee)}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading[dir=rtl]{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__leading{width:max(12px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__notch{max-width:calc(100% - max(12px, var(--mdc-shape-small, 4px)) * 2)}}.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing{border-top-left-radius:0;border-top-right-radius:4px;border-top-right-radius:var(--mdc-shape-small, 4px);border-bottom-right-radius:4px;border-bottom-right-radius:var(--mdc-shape-small, 4px);border-bottom-left-radius:0}[dir=rtl] .mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing,.mdc-text-field--outlined .mdc-notched-outline .mdc-notched-outline__trailing[dir=rtl]{border-top-left-radius:4px;border-top-left-radius:var(--mdc-shape-small, 4px);border-top-right-radius:0;border-bottom-right-radius:0;border-bottom-left-radius:4px;border-bottom-left-radius:var(--mdc-shape-small, 4px)}@supports(top: max(0%)){.mdc-text-field--outlined{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined{padding-right:max(16px, var(--mdc-shape-small, 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}@supports(top: max(0%)){.mdc-text-field--outlined+.mdc-text-field-helper-line{padding-right:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-left:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-leading-icon{padding-right:max(16px, var(--mdc-shape-small, 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-right:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-leading-icon,.mdc-text-field--outlined.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:max(16px, var(--mdc-shape-small, 4px))}}.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-right:0}@supports(top: max(0%)){.mdc-text-field--outlined.mdc-text-field--with-trailing-icon{padding-left:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0}@supports(top: max(0%)){[dir=rtl] .mdc-text-field--outlined.mdc-text-field--with-trailing-icon,.mdc-text-field--outlined.mdc-text-field--with-trailing-icon[dir=rtl]{padding-right:max(16px, calc(var(--mdc-shape-small, 4px) + 4px))}}.mdc-text-field--outlined.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:1px}.mdc-text-field--outlined .mdc-text-field__ripple::before,.mdc-text-field--outlined .mdc-text-field__ripple::after{background-color:transparent;background-color:var(--mdc-ripple-color, transparent)}.mdc-text-field--outlined .mdc-floating-label{left:4px;right:initial}[dir=rtl] .mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:4px}.mdc-text-field--outlined .mdc-text-field__input{display:flex;border:none !important;background-color:transparent}.mdc-text-field--outlined .mdc-notched-outline{z-index:1}.mdc-text-field--textarea{flex-direction:column;align-items:center;width:auto;height:auto;padding:0;transition:none}.mdc-text-field--textarea .mdc-floating-label{top:19px}.mdc-text-field--textarea .mdc-floating-label:not(.mdc-floating-label--float-above){transform:none}.mdc-text-field--textarea .mdc-text-field__input{flex-grow:1;height:auto;min-height:1.5rem;overflow-x:hidden;overflow-y:auto;box-sizing:border-box;resize:none;padding:0 16px;line-height:1.5rem}.mdc-text-field--textarea.mdc-text-field--filled::before{display:none}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--float-above{transform:translateY(-10.25px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--filled .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-filled 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-filled{0%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-10.25px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-10.25px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-10.25px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--filled .mdc-text-field__input{margin-top:23px;margin-bottom:9px}.mdc-text-field--textarea.mdc-text-field--filled.mdc-text-field--no-label .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-27.25px) scale(1)}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-24.75px) scale(0.75)}.mdc-text-field--textarea.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--textarea.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-textarea-outlined 250ms 1}@keyframes mdc-floating-label-shake-float-above-textarea-outlined{0%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 0%)) translateY(-24.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 0%)) translateY(-24.75px) scale(0.75)}100%{transform:translateX(calc(0 - 0%)) translateY(-24.75px) scale(0.75)}}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-text-field__input{margin-top:16px;margin-bottom:16px}.mdc-text-field--textarea.mdc-text-field--outlined .mdc-floating-label{top:18px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field__input{margin-bottom:2px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter{align-self:flex-end;padding:0 16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::after{display:inline-block;width:0;height:16px;content:"";vertical-align:-16px}.mdc-text-field--textarea.mdc-text-field--with-internal-counter .mdc-text-field-character-counter::before{display:none}.mdc-text-field__resizer{align-self:stretch;display:inline-flex;flex-direction:column;flex-grow:1;max-height:100%;max-width:100%;min-height:56px;min-width:fit-content;min-width:-moz-available;min-width:-webkit-fill-available;overflow:hidden;resize:both}.mdc-text-field--filled .mdc-text-field__resizer{transform:translateY(-1px)}.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--filled .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateY(1px)}.mdc-text-field--outlined .mdc-text-field__resizer{transform:translateX(-1px) translateY(-1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer,.mdc-text-field--outlined .mdc-text-field__resizer[dir=rtl]{transform:translateX(1px) translateY(-1px)}.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter{transform:translateX(1px) translateY(1px)}[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input,[dir=rtl] .mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter,.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field__input[dir=rtl],.mdc-text-field--outlined .mdc-text-field__resizer .mdc-text-field-character-counter[dir=rtl]{transform:translateX(-1px) translateY(1px)}.mdc-text-field--with-leading-icon{padding-left:0;padding-right:16px}[dir=rtl] .mdc-text-field--with-leading-icon,.mdc-text-field--with-leading-icon[dir=rtl]{padding-left:16px;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 48px);left:48px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label[dir=rtl]{left:initial;right:48px}.mdc-text-field--with-leading-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label{left:36px;right:initial}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label[dir=rtl]{left:initial;right:36px}.mdc-text-field--with-leading-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{transform:translateY(-37.25px) translateX(-32px) scale(1)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-37.25px) translateX(32px) scale(1)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--float-above{font-size:.75rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{transform:translateY(-34.75px) translateX(-32px) scale(0.75)}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl],.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above[dir=rtl]{transform:translateY(-34.75px) translateX(32px) scale(0.75)}.mdc-text-field--with-leading-icon.mdc-text-field--outlined.mdc-notched-outline--upgraded .mdc-floating-label--float-above,.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-notched-outline--upgraded .mdc-floating-label--float-above{font-size:1rem}.mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon{0%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - 32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - 32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - 32px)) translateY(-34.75px) scale(0.75)}}[dir=rtl] .mdc-text-field--with-leading-icon.mdc-text-field--outlined .mdc-floating-label--shake,.mdc-text-field--with-leading-icon.mdc-text-field--outlined[dir=rtl] .mdc-floating-label--shake{animation:mdc-floating-label-shake-float-above-text-field-outlined-leading-icon 250ms 1}@keyframes mdc-floating-label-shake-float-above-text-field-outlined-leading-icon-rtl{0%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}33%{animation-timing-function:cubic-bezier(0.5, 0, 0.701732, 0.495819);transform:translateX(calc(4% - -32px)) translateY(-34.75px) scale(0.75)}66%{animation-timing-function:cubic-bezier(0.302435, 0.381352, 0.55, 0.956352);transform:translateX(calc(-4% - -32px)) translateY(-34.75px) scale(0.75)}100%{transform:translateX(calc(0 - -32px)) translateY(-34.75px) scale(0.75)}}.mdc-text-field--with-trailing-icon{padding-left:16px;padding-right:0}[dir=rtl] .mdc-text-field--with-trailing-icon,.mdc-text-field--with-trailing-icon[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 64px)}.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 64px / 0.75)}.mdc-text-field--with-trailing-icon.mdc-text-field--outlined :not(.mdc-notched-outline--notched) .mdc-notched-outline__notch{max-width:calc(100% - 60px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon{padding-left:0;padding-right:0}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label{max-width:calc(100% - 96px)}.mdc-text-field--with-leading-icon.mdc-text-field--with-trailing-icon.mdc-text-field--filled .mdc-floating-label--float-above{max-width:calc(100% / 0.75 - 96px / 0.75)}.mdc-text-field-helper-line{display:flex;justify-content:space-between;box-sizing:border-box}.mdc-text-field+.mdc-text-field-helper-line{padding-right:16px;padding-left:16px}.mdc-form-field>.mdc-text-field+label{align-self:flex-start}.mdc-text-field--focused:not(.mdc-text-field--disabled) .mdc-floating-label{color:rgba(98, 0, 238, 0.87)}.mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--focused .mdc-notched-outline__trailing{border-width:2px}.mdc-text-field--focused+.mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg){opacity:1}.mdc-text-field--focused.mdc-text-field--outlined .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:2px}.mdc-text-field--focused.mdc-text-field--outlined.mdc-text-field--textarea .mdc-notched-outline--notched .mdc-notched-outline__notch{padding-top:0}.mdc-text-field--invalid:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::after{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-floating-label{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid .mdc-text-field__input{caret-color:#b00020;caret-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-text-field__icon--trailing{color:#b00020;color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled):not(.mdc-text-field--focused):hover .mdc-notched-outline .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__leading,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__notch,.mdc-text-field--invalid:not(.mdc-text-field--disabled).mdc-text-field--focused .mdc-notched-outline__trailing{border-color:#b00020;border-color:var(--mdc-theme-error, #b00020)}.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-helper-text--validation-msg{opacity:1}.mdc-text-field--disabled{pointer-events:none}.mdc-text-field--disabled .mdc-text-field__input{color:rgba(0, 0, 0, 0.38)}@media all{.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:rgba(0, 0, 0, 0.38)}}@media all{.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:rgba(0, 0, 0, 0.38)}}.mdc-text-field--disabled .mdc-floating-label{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__icon--leading{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:rgba(0, 0, 0, 0.3)}.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:rgba(0, 0, 0, 0.38)}.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06)}.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:rgba(0, 0, 0, 0.06)}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input::placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__input:-ms-input-placeholder{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-floating-label{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-helper-text{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field-character-counter,.mdc-text-field--disabled+.mdc-text-field-helper-line .mdc-text-field-character-counter{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--leading{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__icon--trailing{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--prefix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-text-field__affix--suffix{color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:GrayText}}@media screen and (forced-colors: active),(-ms-high-contrast: active){.mdc-text-field--disabled .mdc-notched-outline__leading,.mdc-text-field--disabled .mdc-notched-outline__notch,.mdc-text-field--disabled .mdc-notched-outline__trailing{border-color:GrayText}}@media screen and (forced-colors: active){.mdc-text-field--disabled .mdc-text-field__input{background-color:Window}.mdc-text-field--disabled .mdc-floating-label{z-index:1}}.mdc-text-field--disabled .mdc-floating-label{cursor:default}.mdc-text-field--disabled.mdc-text-field--filled{background-color:#fafafa}.mdc-text-field--disabled.mdc-text-field--filled .mdc-text-field__ripple{display:none}.mdc-text-field--disabled .mdc-text-field__input{pointer-events:auto}.mdc-text-field--end-aligned .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--end-aligned .mdc-text-field__input[dir=rtl]{text-align:left}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix{direction:ltr}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{padding-left:0;padding-right:2px}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{padding-left:12px;padding-right:0}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--leading,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--leading{order:1}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--suffix{order:2}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__input,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__input{order:3}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__affix--prefix{order:4}[dir=rtl] .mdc-text-field--ltr-text .mdc-text-field__icon--trailing,.mdc-text-field--ltr-text[dir=rtl] .mdc-text-field__icon--trailing{order:5}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__input,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__input{text-align:right}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--prefix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--prefix{padding-right:12px}[dir=rtl] .mdc-text-field--ltr-text.mdc-text-field--end-aligned .mdc-text-field__affix--suffix,.mdc-text-field--ltr-text.mdc-text-field--end-aligned[dir=rtl] .mdc-text-field__affix--suffix{padding-left:2px}.mdc-text-field-helper-text{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin:0;opacity:0;will-change:opacity;transition:opacity 150ms 0ms cubic-bezier(0.4, 0, 0.2, 1)}.mdc-text-field-helper-text::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}.mdc-text-field-helper-text--persistent{transition:none;opacity:1;will-change:initial}.mdc-text-field-character-counter{-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;font-family:Roboto, sans-serif;font-family:var(--mdc-typography-caption-font-family, var(--mdc-typography-font-family, Roboto, sans-serif));font-size:0.75rem;font-size:var(--mdc-typography-caption-font-size, 0.75rem);line-height:1.25rem;line-height:var(--mdc-typography-caption-line-height, 1.25rem);font-weight:400;font-weight:var(--mdc-typography-caption-font-weight, 400);letter-spacing:0.0333333333em;letter-spacing:var(--mdc-typography-caption-letter-spacing, 0.0333333333em);text-decoration:inherit;text-decoration:var(--mdc-typography-caption-text-decoration, inherit);text-transform:inherit;text-transform:var(--mdc-typography-caption-text-transform, inherit);display:block;margin-top:0;line-height:normal;margin-left:auto;margin-right:0;padding-left:16px;padding-right:0;white-space:nowrap}.mdc-text-field-character-counter::before{display:inline-block;width:0;height:16px;content:"";vertical-align:0}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{margin-left:0;margin-right:auto}[dir=rtl] .mdc-text-field-character-counter,.mdc-text-field-character-counter[dir=rtl]{padding-left:0;padding-right:16px}.mdc-text-field__icon{align-self:center;cursor:pointer}.mdc-text-field__icon:not([tabindex]),.mdc-text-field__icon[tabindex="-1"]{cursor:default;pointer-events:none}.mdc-text-field__icon svg{display:block}.mdc-text-field__icon--leading{margin-left:16px;margin-right:8px}[dir=rtl] .mdc-text-field__icon--leading,.mdc-text-field__icon--leading[dir=rtl]{margin-left:8px;margin-right:16px}.mdc-text-field__icon--trailing{padding:12px;margin-left:0px;margin-right:0px}[dir=rtl] .mdc-text-field__icon--trailing,.mdc-text-field__icon--trailing[dir=rtl]{margin-left:0px;margin-right:0px}.material-icons{font-family:var(--mdc-icon-font, "Material Icons");font-weight:normal;font-style:normal;font-size:var(--mdc-icon-size, 24px);line-height:1;letter-spacing:normal;text-transform:none;display:inline-block;white-space:nowrap;word-wrap:normal;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:"liga"}:host{display:inline-flex;flex-direction:column;outline:none}.mdc-text-field{width:100%}.mdc-text-field:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.42);border-bottom-color:var(--mdc-text-field-idle-line-color, rgba(0, 0, 0, 0.42))}.mdc-text-field:not(.mdc-text-field--disabled):hover .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.87);border-bottom-color:var(--mdc-text-field-hover-line-color, rgba(0, 0, 0, 0.87))}.mdc-text-field.mdc-text-field--disabled .mdc-line-ripple::before{border-bottom-color:rgba(0, 0, 0, 0.06);border-bottom-color:var(--mdc-text-field-disabled-line-color, rgba(0, 0, 0, 0.06))}.mdc-text-field.mdc-text-field--invalid:not(.mdc-text-field--disabled) .mdc-line-ripple::before{border-bottom-color:#b00020;border-bottom-color:var(--mdc-theme-error, #b00020)}.mdc-text-field__input{direction:inherit}mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-idle-border-color, rgba(0, 0, 0, 0.38) )}:host(:not([disabled]):hover) :not(.mdc-text-field--invalid):not(.mdc-text-field--focused) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-hover-border-color, rgba(0, 0, 0, 0.87) )}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-fill-color, whitesmoke)}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-error-color, var(--mdc-theme-error, #b00020) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid+.mdc-text-field-helper-line .mdc-text-field-character-counter,:host(:not([disabled])) .mdc-text-field.mdc-text-field--invalid .mdc-text-field__icon{color:var(--mdc-text-field-error-color, var(--mdc-theme-error, #b00020))}:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host(:not([disabled])) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused mwc-notched-outline{--mdc-notched-outline-stroke-width: 2px}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-focused-label-color, var(--mdc-theme-primary, rgba(98, 0, 238, 0.87)) )}:host(:not([disabled])) .mdc-text-field.mdc-text-field--focused:not(.mdc-text-field--invalid) .mdc-floating-label{color:#6200ee;color:var(--mdc-theme-primary, #6200ee)}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input{color:var(--mdc-text-field-ink-color, rgba(0, 0, 0, 0.87))}:host(:not([disabled])) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host(:not([disabled])) .mdc-text-field-helper-line .mdc-text-field-helper-text:not(.mdc-text-field-helper-text--validation-msg),:host(:not([disabled])) .mdc-text-field-helper-line:not(.mdc-text-field--invalid) .mdc-text-field-character-counter{color:var(--mdc-text-field-label-ink-color, rgba(0, 0, 0, 0.6))}:host([disabled]) .mdc-text-field:not(.mdc-text-field--outlined){background-color:var(--mdc-text-field-disabled-fill-color, #fafafa)}:host([disabled]) .mdc-text-field.mdc-text-field--outlined mwc-notched-outline{--mdc-notched-outline-border-color: var( --mdc-text-field-outlined-disabled-border-color, rgba(0, 0, 0, 0.06) )}:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label,:host([disabled]) .mdc-text-field:not(.mdc-text-field--invalid):not(.mdc-text-field--focused) .mdc-floating-label::after{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field .mdc-text-field__input,:host([disabled]) .mdc-text-field .mdc-text-field__input::placeholder{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-helper-text,:host([disabled]) .mdc-text-field-helper-line .mdc-text-field-character-counter{color:var(--mdc-text-field-disabled-ink-color, rgba(0, 0, 0, 0.38))}`,Bn={"mwc-textfield":class extends Fn{static get styles(){return Nn}},"mwc-notched-outline":class extends an{static get styles(){return fn}}};let Ln=class extends(te(Ot)){constructor(){super(...arguments),this._initialized=!1}setConfig(t){this._config=t,null==this._config.auto_update&&(this._config=Object.assign(Object.assign({},this._config),{auto_update:!0})),this.loadCardHelpers()}connectedCallback(){super.connectedCallback();const t=this.save_layout.bind(this);window.addEventListener("wiser-zigbee-save-layout",t)}disconnectedCallback(){window.removeEventListener("wiser-zigbee-save-layout",this.save_layout),super.disconnectedCallback()}shouldUpdate(){return this._initialized||this._initialize(),!0}get _name(){var t;return(null===(t=this._config)||void 0===t?void 0:t.name)||""}get _hub(){var t;return(null===(t=this._config)||void 0===t?void 0:t.hub)||""}get _auto_update(){var t;return(null===(t=this._config)||void 0===t?void 0:t.auto_update)||!1}async loadData(){var t;this.hass&&(this._hubs=await(t=this.hass,t.callWS({type:"wiser/hubs"})))}render(){return this.hass&&this._helpers&&this._config&&this._hubs?$` - ${this.hubSelector()} - - - - -
-
Version: ${$t}
- `:$``}hubSelector(){var t;const e=this._hubs?this._hubs:[];return e.length>1?$` - t.stopPropagation()} - > - ${null===(t=this._hubs)||void 0===t?void 0:t.map((t=>$`${t}`))} - - `:$``}_initialize(){void 0!==this.hass&&void 0!==this._config&&void 0!==this._helpers&&(this._initialized=!0)}async loadCardHelpers(){this._helpers=await window.loadCardHelpers(),await this.loadData()}save_layout(t){this._config&&(this._config=Object.assign(Object.assign({},this._config),{layout_data:t.detail.layout_data}),Ht(this,"config-changed",{config:this._config}))}_valueChanged(t){if(!this._config||!this.hass)return;const e=t.target;if(this[`_${e.configValue}`]!==e.value){if(e.configValue)if("hub"==e.configValue&&(this._config=Object.assign(Object.assign({},this._config),{layout_data:""})),""===e.value){const t=Object.assign({},this._config);delete t[e.configValue],this._config=t}else this._config=Object.assign(Object.assign({},this._config),{[e.configValue]:void 0!==e.checked?e.checked:e.value});Ht(this,"config-changed",{config:this._config})}}}; -/** - * vis-data - * http://visjs.org/ - * - * Manage unstructured data using DataSet. Add, update, and remove data, and listen for changes in the data. - * - * @version 7.1.9 - * @date 2023-11-24T17:53:34.179Z - * - * @copyright (c) 2011-2017 Almende B.V, http://almende.com - * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs - * - * @license - * vis.js is dual licensed under both - * - * 1. The Apache 2.0 License - * http://www.apache.org/licenses/LICENSE-2.0 - * - * and - * - * 2. The MIT License - * http://opensource.org/licenses/MIT - * - * vis.js may be distributed under either license. - */ -function zn(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}Ln.elementDefinitions=Object.assign(Object.assign(Object.assign(Object.assign({},Bn),mn),wn),Oe),Ln.styles=ht` - mwc-select, - mwc-textfield { - margin-bottom: 16px; - display: block; - } - mwc-formfield { - padding-bottom: 20px; - display: flex; - } - mwc-switch { - --mdc-theme-secondary: var(--switch-checked-color); - } - `,o([At({attribute:!1})],Ln.prototype,"hass",void 0),o([Rt()],Ln.prototype,"_config",void 0),o([Rt()],Ln.prototype,"_helpers",void 0),o([Rt()],Ln.prototype,"_hubs",void 0),Ln=o([St("wiser-zigbee-card-editor")],Ln);var jn="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function Hn(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var $n={exports:{}},Un=function(t){return t&&t.Math===Math&&t},Wn=Un("object"==typeof globalThis&&globalThis)||Un("object"==typeof window&&window)||Un("object"==typeof self&&self)||Un("object"==typeof jn&&jn)||function(){return this}()||jn||Function("return this")(),Vn=function(t){try{return!!t()}catch(t){return!0}},qn=!Vn((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")})),Xn=qn,Yn=Function.prototype,Gn=Yn.apply,Kn=Yn.call,Zn="object"==typeof Reflect&&Reflect.apply||(Xn?Kn.bind(Gn):function(){return Kn.apply(Gn,arguments)}),Qn=qn,Jn=Function.prototype,to=Jn.call,eo=Qn&&Jn.bind.bind(to,to),io=Qn?eo:function(t){return function(){return to.apply(t,arguments)}},no=io,oo=no({}.toString),ro=no("".slice),so=function(t){return ro(oo(t),8,-1)},ao=so,lo=io,co=function(t){if("Function"===ao(t))return lo(t)},ho="object"==typeof document&&document.all,uo={all:ho,IS_HTMLDDA:void 0===ho&&void 0!==ho},po=uo.all,fo=uo.IS_HTMLDDA?function(t){return"function"==typeof t||t===po}:function(t){return"function"==typeof t},mo={},vo=!Vn((function(){return 7!==Object.defineProperty({},1,{get:function(){return 7}})[1]})),go=qn,yo=Function.prototype.call,bo=go?yo.bind(yo):function(){return yo.apply(yo,arguments)},xo={},_o={}.propertyIsEnumerable,wo=Object.getOwnPropertyDescriptor,Eo=wo&&!_o.call({1:2},1);xo.f=Eo?function(t){var e=wo(this,t);return!!e&&e.enumerable}:_o;var ko,Oo,Co=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},So=Vn,To=so,Io=Object,Ao=io("".split),Ro=So((function(){return!Io("z").propertyIsEnumerable(0)}))?function(t){return"String"===To(t)?Ao(t,""):Io(t)}:Io,Po=function(t){return null==t},Do=Po,Mo=TypeError,Fo=function(t){if(Do(t))throw new Mo("Can't call method on "+t);return t},No=Ro,Bo=Fo,Lo=function(t){return No(Bo(t))},zo=fo,jo=uo.all,Ho=uo.IS_HTMLDDA?function(t){return"object"==typeof t?null!==t:zo(t)||t===jo}:function(t){return"object"==typeof t?null!==t:zo(t)},$o={},Uo=$o,Wo=Wn,Vo=fo,qo=function(t){return Vo(t)?t:void 0},Xo=function(t,e){return arguments.length<2?qo(Uo[t])||qo(Wo[t]):Uo[t]&&Uo[t][e]||Wo[t]&&Wo[t][e]},Yo=io({}.isPrototypeOf),Go="undefined"!=typeof navigator&&String(navigator.userAgent)||"",Ko=Wn,Zo=Go,Qo=Ko.process,Jo=Ko.Deno,tr=Qo&&Qo.versions||Jo&&Jo.version,er=tr&&tr.v8;er&&(Oo=(ko=er.split("."))[0]>0&&ko[0]<4?1:+(ko[0]+ko[1])),!Oo&&Zo&&(!(ko=Zo.match(/Edge\/(\d+)/))||ko[1]>=74)&&(ko=Zo.match(/Chrome\/(\d+)/))&&(Oo=+ko[1]);var ir=Oo,nr=ir,or=Vn,rr=Wn.String,sr=!!Object.getOwnPropertySymbols&&!or((function(){var t=Symbol("symbol detection");return!rr(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&nr&&nr<41})),ar=sr&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,dr=Xo,lr=fo,cr=Yo,hr=Object,ur=ar?function(t){return"symbol"==typeof t}:function(t){var e=dr("Symbol");return lr(e)&&cr(e.prototype,hr(t))},pr=String,fr=function(t){try{return pr(t)}catch(t){return"Object"}},mr=fo,vr=fr,gr=TypeError,yr=function(t){if(mr(t))return t;throw new gr(vr(t)+" is not a function")},br=yr,xr=Po,_r=function(t,e){var i=t[e];return xr(i)?void 0:br(i)},wr=bo,Er=fo,kr=Ho,Or=TypeError,Cr={exports:{}},Sr=Wn,Tr=Object.defineProperty,Ir=function(t,e){try{Tr(Sr,t,{value:e,configurable:!0,writable:!0})}catch(i){Sr[t]=e}return e},Ar="__core-js_shared__",Rr=Wn[Ar]||Ir(Ar,{}),Pr=Rr;(Cr.exports=function(t,e){return Pr[t]||(Pr[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.33.2",mode:"pure",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.33.2/LICENSE",source:"https://github.com/zloirock/core-js"});var Dr=Cr.exports,Mr=Fo,Fr=Object,Nr=function(t){return Fr(Mr(t))},Br=Nr,Lr=io({}.hasOwnProperty),zr=Object.hasOwn||function(t,e){return Lr(Br(t),e)},jr=io,Hr=0,$r=Math.random(),Ur=jr(1..toString),Wr=function(t){return"Symbol("+(void 0===t?"":t)+")_"+Ur(++Hr+$r,36)},Vr=Dr,qr=zr,Xr=Wr,Yr=sr,Gr=ar,Kr=Wn.Symbol,Zr=Vr("wks"),Qr=Gr?Kr.for||Kr:Kr&&Kr.withoutSetter||Xr,Jr=function(t){return qr(Zr,t)||(Zr[t]=Yr&&qr(Kr,t)?Kr[t]:Qr("Symbol."+t)),Zr[t]},ts=bo,es=Ho,is=ur,ns=_r,os=function(t,e){var i,n;if("string"===e&&Er(i=t.toString)&&!kr(n=wr(i,t)))return n;if(Er(i=t.valueOf)&&!kr(n=wr(i,t)))return n;if("string"!==e&&Er(i=t.toString)&&!kr(n=wr(i,t)))return n;throw new Or("Can't convert object to primitive value")},rs=TypeError,ss=Jr("toPrimitive"),as=function(t,e){if(!es(t)||is(t))return t;var i,n=ns(t,ss);if(n){if(void 0===e&&(e="default"),i=ts(n,t,e),!es(i)||is(i))return i;throw new rs("Can't convert object to primitive value")}return void 0===e&&(e="number"),os(t,e)},ds=ur,ls=function(t){var e=as(t,"string");return ds(e)?e:e+""},cs=Ho,hs=Wn.document,us=cs(hs)&&cs(hs.createElement),ps=function(t){return us?hs.createElement(t):{}},fs=ps,ms=!vo&&!Vn((function(){return 7!==Object.defineProperty(fs("div"),"a",{get:function(){return 7}}).a})),vs=vo,gs=bo,ys=xo,bs=Co,xs=Lo,_s=ls,ws=zr,Es=ms,ks=Object.getOwnPropertyDescriptor;mo.f=vs?ks:function(t,e){if(t=xs(t),e=_s(e),Es)try{return ks(t,e)}catch(t){}if(ws(t,e))return bs(!gs(ys.f,t,e),t[e])};var Os=Vn,Cs=fo,Ss=/#|\.prototype\./,Ts=function(t,e){var i=As[Is(t)];return i===Ps||i!==Rs&&(Cs(e)?Os(e):!!e)},Is=Ts.normalize=function(t){return String(t).replace(Ss,".").toLowerCase()},As=Ts.data={},Rs=Ts.NATIVE="N",Ps=Ts.POLYFILL="P",Ds=Ts,Ms=yr,Fs=qn,Ns=co(co.bind),Bs=function(t,e){return Ms(t),void 0===e?t:Fs?Ns(t,e):function(){return t.apply(e,arguments)}},Ls={},zs=vo&&Vn((function(){return 42!==Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),js=Ho,Hs=String,$s=TypeError,Us=function(t){if(js(t))return t;throw new $s(Hs(t)+" is not an object")},Ws=vo,Vs=ms,qs=zs,Xs=Us,Ys=ls,Gs=TypeError,Ks=Object.defineProperty,Zs=Object.getOwnPropertyDescriptor,Qs="enumerable",Js="configurable",ta="writable";Ls.f=Ws?qs?function(t,e,i){if(Xs(t),e=Ys(e),Xs(i),"function"==typeof t&&"prototype"===e&&"value"in i&&ta in i&&!i[ta]){var n=Zs(t,e);n&&n[ta]&&(t[e]=i.value,i={configurable:Js in i?i[Js]:n[Js],enumerable:Qs in i?i[Qs]:n[Qs],writable:!1})}return Ks(t,e,i)}:Ks:function(t,e,i){if(Xs(t),e=Ys(e),Xs(i),Vs)try{return Ks(t,e,i)}catch(t){}if("get"in i||"set"in i)throw new Gs("Accessors not supported");return"value"in i&&(t[e]=i.value),t};var ea=Ls,ia=Co,na=vo?function(t,e,i){return ea.f(t,e,ia(1,i))}:function(t,e,i){return t[e]=i,t},oa=Wn,ra=Zn,sa=co,aa=fo,da=mo.f,la=Ds,ca=$o,ha=Bs,ua=na,pa=zr,fa=function(t){var e=function(i,n,o){if(this instanceof e){switch(arguments.length){case 0:return new t;case 1:return new t(i);case 2:return new t(i,n)}return new t(i,n,o)}return ra(t,this,arguments)};return e.prototype=t.prototype,e},ma=function(t,e){var i,n,o,r,s,a,d,l,c,h=t.target,u=t.global,p=t.stat,f=t.proto,m=u?oa:p?oa[h]:(oa[h]||{}).prototype,v=u?ca:ca[h]||ua(ca,h,{})[h],g=v.prototype;for(r in e)n=!(i=la(u?r:h+(p?".":"#")+r,t.forced))&&m&&pa(m,r),a=v[r],n&&(d=t.dontCallGetSet?(c=da(m,r))&&c.value:m[r]),s=n&&d?d:e[r],n&&typeof a==typeof s||(l=t.bind&&n?ha(s,oa):t.wrap&&n?fa(s):f&&aa(s)?sa(s):s,(t.sham||s&&s.sham||a&&a.sham)&&ua(l,"sham",!0),ua(v,r,l),f&&(pa(ca,o=h+"Prototype")||ua(ca,o,{}),ua(ca[o],r,s),t.real&&g&&(i||!g[r])&&ua(g,r,s)))},va=ma,ga=vo,ya=Ls.f;va({target:"Object",stat:!0,forced:Object.defineProperty!==ya,sham:!ga},{defineProperty:ya});var ba=$o.Object,xa=$n.exports=function(t,e,i){return ba.defineProperty(t,e,i)};ba.defineProperty.sham&&(xa.sham=!0);var _a=$n.exports,wa=_a,Ea=Hn(wa),ka=so,Oa=Array.isArray||function(t){return"Array"===ka(t)},Ca=Math.ceil,Sa=Math.floor,Ta=Math.trunc||function(t){var e=+t;return(e>0?Sa:Ca)(e)},Ia=Ta,Aa=function(t){var e=+t;return e!=e||0===e?0:Ia(e)},Ra=Aa,Pa=Math.min,Da=function(t){return t>0?Pa(Ra(t),9007199254740991):0},Ma=function(t){return Da(t.length)},Fa=TypeError,Na=function(t){if(t>9007199254740991)throw Fa("Maximum allowed index exceeded");return t},Ba=ls,La=Ls,za=Co,ja=function(t,e,i){var n=Ba(e);n in t?La.f(t,n,za(0,i)):t[n]=i},Ha={};Ha[Jr("toStringTag")]="z";var $a="[object z]"===String(Ha),Ua=$a,Wa=fo,Va=so,qa=Jr("toStringTag"),Xa=Object,Ya="Arguments"===Va(function(){return arguments}()),Ga=Ua?Va:function(t){var e,i,n;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(i=function(t,e){try{return t[e]}catch(t){}}(e=Xa(t),qa))?i:Ya?Va(e):"Object"===(n=Va(e))&&Wa(e.callee)?"Arguments":n},Ka=fo,Za=Rr,Qa=io(Function.toString);Ka(Za.inspectSource)||(Za.inspectSource=function(t){return Qa(t)});var Ja=Za.inspectSource,td=io,ed=Vn,id=fo,nd=Ga,od=Ja,rd=function(){},sd=[],ad=Xo("Reflect","construct"),dd=/^\s*(?:class|function)\b/,ld=td(dd.exec),cd=!dd.test(rd),hd=function(t){if(!id(t))return!1;try{return ad(rd,sd,t),!0}catch(t){return!1}},ud=function(t){if(!id(t))return!1;switch(nd(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return cd||!!ld(dd,od(t))}catch(t){return!0}};ud.sham=!0;var pd=!ad||ed((function(){var t;return hd(hd.call)||!hd(Object)||!hd((function(){t=!0}))||t}))?ud:hd,fd=Oa,md=pd,vd=Ho,gd=Jr("species"),yd=Array,bd=function(t){var e;return fd(t)&&(e=t.constructor,(md(e)&&(e===yd||fd(e.prototype))||vd(e)&&null===(e=e[gd]))&&(e=void 0)),void 0===e?yd:e},xd=function(t,e){return new(bd(t))(0===e?0:e)},_d=Vn,wd=ir,Ed=Jr("species"),kd=function(t){return wd>=51||!_d((function(){var e=[];return(e.constructor={})[Ed]=function(){return{foo:1}},1!==e[t](Boolean).foo}))},Od=ma,Cd=Vn,Sd=Oa,Td=Ho,Id=Nr,Ad=Ma,Rd=Na,Pd=ja,Dd=xd,Md=kd,Fd=ir,Nd=Jr("isConcatSpreadable"),Bd=Fd>=51||!Cd((function(){var t=[];return t[Nd]=!1,t.concat()[0]!==t})),Ld=function(t){if(!Td(t))return!1;var e=t[Nd];return void 0!==e?!!e:Sd(t)};Od({target:"Array",proto:!0,arity:1,forced:!Bd||!Md("concat")},{concat:function(t){var e,i,n,o,r,s=Id(this),a=Dd(s,0),d=0;for(e=-1,n=arguments.length;ea;)if((o=r[a++])!=o)return!0}else for(;s>a;a++)if((t||a in r)&&r[a]===i)return t||a||0;return!t&&-1}},Zd={includes:Kd(!0),indexOf:Kd(!1)},Qd={},Jd=zr,tl=Lo,el=Zd.indexOf,il=Qd,nl=io([].push),ol=function(t,e){var i,n=tl(t),o=0,r=[];for(i in n)!Jd(il,i)&&Jd(n,i)&&nl(r,i);for(;e.length>o;)Jd(n,i=e[o++])&&(~el(r,i)||nl(r,i));return r},rl=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],sl=ol,al=rl,dl=Object.keys||function(t){return sl(t,al)},ll=vo,cl=zs,hl=Ls,ul=Us,pl=Lo,fl=dl;$d.f=ll&&!cl?Object.defineProperties:function(t,e){ul(t);for(var i,n=pl(e),o=fl(e),r=o.length,s=0;r>s;)hl.f(t,i=o[s++],n[i]);return t};var ml,vl=Xo("document","documentElement"),gl=Wr,yl=Dr("keys"),bl=function(t){return yl[t]||(yl[t]=gl(t))},xl=Us,_l=$d,wl=rl,El=Qd,kl=vl,Ol=ps,Cl="prototype",Sl="script",Tl=bl("IE_PROTO"),Il=function(){},Al=function(t){return"<"+Sl+">"+t+""},Rl=function(t){t.write(Al("")),t.close();var e=t.parentWindow.Object;return t=null,e},Pl=function(){try{ml=new ActiveXObject("htmlfile")}catch(t){}var t,e,i;Pl="undefined"!=typeof document?document.domain&&ml?Rl(ml):(e=Ol("iframe"),i="java"+Sl+":",e.style.display="none",kl.appendChild(e),e.src=String(i),(t=e.contentWindow.document).open(),t.write(Al("document.F=Object")),t.close(),t.F):Rl(ml);for(var n=wl.length;n--;)delete Pl[Cl][wl[n]];return Pl()};El[Tl]=!0;var Dl=Object.create||function(t,e){var i;return null!==t?(Il[Cl]=xl(t),i=new Il,Il[Cl]=null,i[Tl]=t):i=Pl(),void 0===e?i:_l.f(i,e)},Ml={},Fl=ol,Nl=rl.concat("length","prototype");Ml.f=Object.getOwnPropertyNames||function(t){return Fl(t,Nl)};var Bl={},Ll=qd,zl=Ma,jl=ja,Hl=Array,$l=Math.max,Ul=function(t,e,i){for(var n=zl(t),o=Ll(e,n),r=Ll(void 0===i?n:i,n),s=Hl($l(r-o,0)),a=0;oy;y++)if((a||y in m)&&(p=v(u=m[y],y,f),t))if(e)x[y]=p;else if(p)switch(t){case 3:return!0;case 5:return u;case 6:return y;case 2:Vc(x,u)}else switch(t){case 4:return!1;case 7:Vc(x,u)}return r?-1:n||o?o:x}},Xc={forEach:qc(0),map:qc(1),filter:qc(2),some:qc(3),every:qc(4),find:qc(5),findIndex:qc(6),filterReject:qc(7)},Yc=ma,Gc=Wn,Kc=bo,Zc=io,Qc=vo,Jc=sr,th=Vn,eh=zr,ih=Yo,nh=Us,oh=Lo,rh=ls,sh=Hd,ah=Co,dh=Dl,lh=dl,ch=Ml,hh=Bl,uh=Gl,ph=mo,fh=Ls,mh=$d,vh=xo,gh=Zl,yh=Jl,bh=Dr,xh=Qd,_h=Wr,wh=Jr,Eh=tc,kh=lc,Oh=fc,Ch=Ec,Sh=zc,Th=Xc.forEach,Ih=bl("hidden"),Ah="Symbol",Rh="prototype",Ph=Sh.set,Dh=Sh.getterFor(Ah),Mh=Object[Rh],Fh=Gc.Symbol,Nh=Fh&&Fh[Rh],Bh=Gc.RangeError,Lh=Gc.TypeError,zh=Gc.QObject,jh=ph.f,Hh=fh.f,$h=hh.f,Uh=vh.f,Wh=Zc([].push),Vh=bh("symbols"),qh=bh("op-symbols"),Xh=bh("wks"),Yh=!zh||!zh[Rh]||!zh[Rh].findChild,Gh=function(t,e,i){var n=jh(Mh,e);n&&delete Mh[e],Hh(t,e,i),n&&t!==Mh&&Hh(Mh,e,n)},Kh=Qc&&th((function(){return 7!==dh(Hh({},"a",{get:function(){return Hh(this,"a",{value:7}).a}})).a}))?Gh:Hh,Zh=function(t,e){var i=Vh[t]=dh(Nh);return Ph(i,{type:Ah,tag:t,description:e}),Qc||(i.description=e),i},Qh=function(t,e,i){t===Mh&&Qh(qh,e,i),nh(t);var n=rh(e);return nh(i),eh(Vh,n)?(i.enumerable?(eh(t,Ih)&&t[Ih][n]&&(t[Ih][n]=!1),i=dh(i,{enumerable:ah(0,!1)})):(eh(t,Ih)||Hh(t,Ih,ah(1,{})),t[Ih][n]=!0),Kh(t,n,i)):Hh(t,n,i)},Jh=function(t,e){nh(t);var i=oh(e),n=lh(i).concat(nu(i));return Th(n,(function(e){Qc&&!Kc(tu,i,e)||Qh(t,e,i[e])})),t},tu=function(t){var e=rh(t),i=Kc(Uh,this,e);return!(this===Mh&&eh(Vh,e)&&!eh(qh,e))&&(!(i||!eh(this,e)||!eh(Vh,e)||eh(this,Ih)&&this[Ih][e])||i)},eu=function(t,e){var i=oh(t),n=rh(e);if(i!==Mh||!eh(Vh,n)||eh(qh,n)){var o=jh(i,n);return!o||!eh(Vh,n)||eh(i,Ih)&&i[Ih][n]||(o.enumerable=!0),o}},iu=function(t){var e=$h(oh(t)),i=[];return Th(e,(function(t){eh(Vh,t)||eh(xh,t)||Wh(i,t)})),i},nu=function(t){var e=t===Mh,i=$h(e?qh:oh(t)),n=[];return Th(i,(function(t){!eh(Vh,t)||e&&!eh(Mh,t)||Wh(n,Vh[t])})),n};Jc||(Fh=function(){if(ih(Nh,this))throw new Lh("Symbol is not a constructor");var t=arguments.length&&void 0!==arguments[0]?sh(arguments[0]):void 0,e=_h(t),i=function(t){var n=void 0===this?Gc:this;n===Mh&&Kc(i,qh,t),eh(n,Ih)&&eh(n[Ih],e)&&(n[Ih][e]=!1);var o=ah(1,t);try{Kh(n,e,o)}catch(t){if(!(t instanceof Bh))throw t;Gh(n,e,o)}};return Qc&&Yh&&Kh(Mh,e,{configurable:!0,set:i}),Zh(e,t)},gh(Nh=Fh[Rh],"toString",(function(){return Dh(this).tag})),gh(Fh,"withoutSetter",(function(t){return Zh(_h(t),t)})),vh.f=tu,fh.f=Qh,mh.f=Jh,ph.f=eu,ch.f=hh.f=iu,uh.f=nu,Eh.f=function(t){return Zh(wh(t),t)},Qc&&yh(Nh,"description",{configurable:!0,get:function(){return Dh(this).description}})),Yc({global:!0,constructor:!0,wrap:!0,forced:!Jc,sham:!Jc},{Symbol:Fh}),Th(lh(Xh),(function(t){kh(t)})),Yc({target:Ah,stat:!0,forced:!Jc},{useSetter:function(){Yh=!0},useSimple:function(){Yh=!1}}),Yc({target:"Object",stat:!0,forced:!Jc,sham:!Qc},{create:function(t,e){return void 0===e?dh(t):Jh(dh(t),e)},defineProperty:Qh,defineProperties:Jh,getOwnPropertyDescriptor:eu}),Yc({target:"Object",stat:!0,forced:!Jc},{getOwnPropertyNames:iu}),Oh(),Ch(Fh,Ah),xh[Ih]=!0;var ou=sr&&!!Symbol.for&&!!Symbol.keyFor,ru=ma,su=Xo,au=zr,du=Hd,lu=Dr,cu=ou,hu=lu("string-to-symbol-registry"),uu=lu("symbol-to-string-registry");ru({target:"Symbol",stat:!0,forced:!cu},{for:function(t){var e=du(t);if(au(hu,e))return hu[e];var i=su("Symbol")(e);return hu[e]=i,uu[i]=e,i}});var pu=ma,fu=zr,mu=ur,vu=fr,gu=ou,yu=Dr("symbol-to-string-registry");pu({target:"Symbol",stat:!0,forced:!gu},{keyFor:function(t){if(!mu(t))throw new TypeError(vu(t)+" is not a symbol");if(fu(yu,t))return yu[t]}});var bu=io([].slice),xu=Oa,_u=fo,wu=so,Eu=Hd,ku=io([].push),Ou=ma,Cu=Xo,Su=Zn,Tu=bo,Iu=io,Au=Vn,Ru=fo,Pu=ur,Du=bu,Mu=function(t){if(_u(t))return t;if(xu(t)){for(var e=t.length,i=[],n=0;n=e.length)return t.target=void 0,pf(void 0,!0);switch(t.kind){case"keys":return pf(i,!1);case"values":return pf(e[i],!1)}return pf([i,e[i]],!1)}),"values"),cf.Arguments=cf.Array;var gf={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0},yf=Wn,bf=Ga,xf=na,_f=rp,wf=Jr("toStringTag");for(var Ef in gf){var kf=yf[Ef],Of=kf&&kf.prototype;Of&&bf(Of)!==wf&&xf(Of,wf,Ef),_f[Ef]=_f.Array}var Cf=op,Sf=Jr,Tf=Ls.f,If=Sf("metadata"),Af=Function.prototype;void 0===Af[If]&&Tf(Af,If,{value:null}),lc("asyncDispose"),lc("dispose"),lc("metadata");var Rf=Cf,Pf=io,Df=Xo("Symbol"),Mf=Df.keyFor,Ff=Pf(Df.prototype.valueOf),Nf=Df.isRegisteredSymbol||function(t){try{return void 0!==Mf(Ff(t))}catch(t){return!1}};ma({target:"Symbol",stat:!0},{isRegisteredSymbol:Nf});for(var Bf=Dr,Lf=Xo,zf=io,jf=ur,Hf=Jr,$f=Lf("Symbol"),Uf=$f.isWellKnownSymbol,Wf=Lf("Object","getOwnPropertyNames"),Vf=zf($f.prototype.valueOf),qf=Bf("wks"),Xf=0,Yf=Wf($f),Gf=Yf.length;Xf=a?t?"":void 0:(n=rm(r,s))<55296||n>56319||s+1===a||(o=rm(r,s+1))<56320||o>57343?t?om(r,s):n:t?sm(r,s,s+2):o-56320+(n-55296<<10)+65536}},dm={codeAt:am(!1),charAt:am(!0)}.charAt,lm=Hd,cm=zc,hm=af,um=df,pm="String Iterator",fm=cm.set,mm=cm.getterFor(pm);hm(String,"String",(function(t){fm(this,{type:pm,string:lm(t),index:0})}),(function(){var t,e=mm(this),i=e.string,n=e.index;return n>=i.length?um(void 0,!0):(t=dm(i,n),e.index+=t.length,um(t,!1))}));var vm=tc.f("iterator"),gm=vm,ym=Hn(gm);function bm(t){return bm="function"==typeof Jf&&"symbol"==typeof ym?function(t){return typeof t}:function(t){return t&&"function"==typeof Jf&&t.constructor===Jf&&t!==Jf.prototype?"symbol":typeof t},bm(t)}var xm=Hn(tc.f("toPrimitive"));function _m(t){var e=function(t,e){if("object"!==bm(t)||null===t)return t;var i=t[xm];if(void 0!==i){var n=i.call(t,e||"default");if("object"!==bm(n))return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===e?String:Number)(t)}(t,"string");return"symbol"===bm(e)?e:String(e)}function wm(t,e){for(var i=0;i=0:a>d;d+=l)d in s&&(o=i(o,s[d],d,r));return o}},Jm={left:Qm(!1),right:Qm(!0)},tv=Vn,ev=function(t,e){var i=[][t];return!!i&&tv((function(){i.call(null,e||function(){return 1},1)}))},iv="process"===so(Wn.process),nv=Jm.left;ma({target:"Array",proto:!0,forced:!iv&&ir>79&&ir<83||!ev("reduce")},{reduce:function(t){var e=arguments.length;return nv(this,t,e,e>1?arguments[1]:void 0)}});var ov=zm("Array","reduce"),rv=Yo,sv=ov,av=Array.prototype,dv=function(t){var e=t.reduce;return t===av||rv(av,t)&&e===av.reduce?sv:e},lv=Hn(dv),cv=Xc.filter;ma({target:"Array",proto:!0,forced:!kd("filter")},{filter:function(t){return cv(this,t,arguments.length>1?arguments[1]:void 0)}});var hv=zm("Array","filter"),uv=Yo,pv=hv,fv=Array.prototype,mv=function(t){var e=t.filter;return t===fv||uv(fv,t)&&e===fv.filter?pv:e},vv=Hn(mv),gv=Xc.map;ma({target:"Array",proto:!0,forced:!kd("map")},{map:function(t){return gv(this,t,arguments.length>1?arguments[1]:void 0)}});var yv=zm("Array","map"),bv=Yo,xv=yv,_v=Array.prototype,wv=function(t){var e=t.map;return t===_v||bv(_v,t)&&e===_v.map?xv:e},Ev=Hn(wv),kv=Oa,Ov=Ma,Cv=Na,Sv=Bs,Tv=function(t,e,i,n,o,r,s,a){for(var d,l,c=o,h=0,u=!!s&&Sv(s,a);h0&&kv(d)?(l=Ov(d),c=Tv(t,e,d,l,c,r-1)-1):(Cv(c+1),t[c]=d),c++),h++;return c},Iv=Tv,Av=yr,Rv=Nr,Pv=Ma,Dv=xd;ma({target:"Array",proto:!0},{flatMap:function(t){var e,i=Rv(this),n=Pv(i);return Av(t),(e=Dv(i,0)).length=Iv(e,i,i,n,0,1,t,arguments.length>1?arguments[1]:void 0),e}}),zm("Array","flatMap");var Mv=bo,Fv=Us,Nv=_r,Bv=function(t,e,i){var n,o;Fv(t);try{if(!(n=Nv(t,"return"))){if("throw"===e)throw i;return i}n=Mv(n,t)}catch(t){o=!0,n=t}if("throw"===e)throw i;if(o)throw n;return Fv(n),i},Lv=Us,zv=Bv,jv=rp,Hv=Jr("iterator"),$v=Array.prototype,Uv=function(t){return void 0!==t&&(jv.Array===t||$v[Hv]===t)},Wv=Ga,Vv=_r,qv=Po,Xv=rp,Yv=Jr("iterator"),Gv=function(t){if(!qv(t))return Vv(t,Yv)||Vv(t,"@@iterator")||Xv[Wv(t)]},Kv=bo,Zv=yr,Qv=Us,Jv=fr,tg=Gv,eg=TypeError,ig=function(t,e){var i=arguments.length<2?tg(t):e;if(Zv(i))return Qv(Kv(i,t));throw new eg(Jv(t)+" is not iterable")},ng=Bs,og=bo,rg=Nr,sg=function(t,e,i,n){try{return n?e(Lv(i)[0],i[1]):e(i)}catch(e){zv(t,"throw",e)}},ag=Uv,dg=pd,lg=Ma,cg=ja,hg=ig,ug=Gv,pg=Array,fg=Jr("iterator"),mg=!1;try{var vg=0,gg={next:function(){return{done:!!vg++}},return:function(){mg=!0}};gg[fg]=function(){return this},Array.from(gg,(function(){throw 2}))}catch(t){}var yg=function(t,e){try{if(!e&&!mg)return!1}catch(t){return!1}var i=!1;try{var n={};n[fg]=function(){return{next:function(){return{done:i=!0}}}},t(n)}catch(t){}return i},bg=function(t){var e=rg(t),i=dg(this),n=arguments.length,o=n>1?arguments[1]:void 0,r=void 0!==o;r&&(o=ng(o,n>2?arguments[2]:void 0));var s,a,d,l,c,h,u=ug(e),p=0;if(!u||this===pg&&ag(u))for(s=lg(e),a=i?new this(s):pg(s);s>p;p++)h=r?o(e[p],p):e[p],cg(a,p,h);else for(c=(l=hg(e,u)).next,a=i?new this:[];!(d=og(c,l)).done;p++)h=r?sg(l,o,[d.value,p],!0):d.value,cg(a,p,h);return a.length=p,a};ma({target:"Array",stat:!0,forced:!yg((function(t){Array.from(t)}))},{from:bg});var xg=$o.Array.from,_g=Hn(xg),wg=Gv,Eg=Hn(wg),kg=Hn(wg);ma({target:"Array",stat:!0},{isArray:Oa});var Og=$o.Array.isArray,Cg=Hn(Og);var Sg=vo,Tg=Oa,Ig=TypeError,Ag=Object.getOwnPropertyDescriptor,Rg=Sg&&!function(){if(void 0!==this)return!0;try{Object.defineProperty([],"length",{writable:!1}).length=1}catch(t){return t instanceof TypeError}}()?function(t,e){if(Tg(t)&&!Ag(t,"length").writable)throw new Ig("Cannot set read only .length");return t.length=e}:function(t,e){return t.length=e},Pg=Nr,Dg=Ma,Mg=Rg,Fg=Na;ma({target:"Array",proto:!0,arity:1,forced:Vn((function(){return 4294967297!==[].push.call({length:4294967296},1)}))||!function(){try{Object.defineProperty([],"length",{writable:!1}).push()}catch(t){return t instanceof TypeError}}()},{push:function(t){var e=Pg(this),i=Dg(e),n=arguments.length;Fg(i+n);for(var o=0;ot.length)&&(e=t.length);for(var i=0,n=new Array(e);i1?arguments[1]:void 0)};ma({target:"Array",proto:!0,forced:[].forEach!==Ny},{forEach:Ny});var By=zm("Array","forEach"),Ly=Ga,zy=zr,jy=Yo,Hy=By,$y=Array.prototype,Uy={DOMTokenList:!0,NodeList:!0},Wy=function(t){var e=t.forEach;return t===$y||jy($y,t)&&e===$y.forEach||zy(Uy,Ly(t))?Hy:e},Vy=Hn(Wy),qy=ma,Xy=Oa,Yy=io([].reverse),Gy=[1,2];qy({target:"Array",proto:!0,forced:String(Gy)===String(Gy.reverse())},{reverse:function(){return Xy(this)&&(this.length=this.length),Yy(this)}});var Ky=zm("Array","reverse"),Zy=Yo,Qy=Ky,Jy=Array.prototype,tb=function(t){var e=t.reverse;return t===Jy||Zy(Jy,t)&&e===Jy.reverse?Qy:e},eb=tb,ib=Hn(eb),nb=fr,ob=TypeError,rb=function(t,e){if(!delete t[e])throw new ob("Cannot delete property "+nb(e)+" of "+nb(t))},sb=ma,ab=Nr,db=qd,lb=Aa,cb=Ma,hb=Rg,ub=Na,pb=xd,fb=ja,mb=rb,vb=kd("splice"),gb=Math.max,yb=Math.min;sb({target:"Array",proto:!0,forced:!vb},{splice:function(t,e){var i,n,o,r,s,a,d=ab(this),l=cb(d),c=db(t,l),h=arguments.length;for(0===h?i=n=0:1===h?(i=0,n=l-c):(i=h-2,n=yb(gb(lb(e),0),l-c)),ub(l+i-n),o=pb(d,n),r=0;rl-n+i;r--)mb(d,r-1)}else if(i>n)for(r=l-n;r>c;r--)a=r+i-1,(s=r+n-1)in d?d[a]=d[s]:mb(d,a);for(r=0;ro;)for(var a,d=Db(arguments[o++]),l=r?Nb(Ib(d),r(d)):Ib(d),c=l.length,h=0;c>h;)a=l[h++],Ob&&!Sb(s,d,a)||(i[a]=d[a]);return i}:Mb,Lb=Bb;ma({target:"Object",stat:!0,arity:2,forced:Object.assign!==Lb},{assign:Lb});var zb=Hn($o.Object.assign),jb=Nr,Hb=xp,$b=up;ma({target:"Object",stat:!0,forced:Vn((function(){Hb(1)})),sham:!$b},{getPrototypeOf:function(t){return Hb(jb(t))}});var Ub=$o.Object.getPrototypeOf;ma({target:"Object",stat:!0,sham:!vo},{create:Dl});var Wb=$o.Object,Vb=function(t,e){return Wb.create(t,e)},qb=Vb,Xb=Hn(qb),Yb=$o,Gb=Zn;Yb.JSON||(Yb.JSON={stringify:JSON.stringify});var Kb=function(t,e,i){return Gb(Yb.JSON.stringify,null,arguments)},Zb=Hn(Kb),Qb="function"==typeof Bun&&Bun&&"string"==typeof Bun.version,Jb=TypeError,tx=function(t,e){if(ti,s=nx(n)?n:dx(n),a=r?sx(arguments,i):[],d=r?function(){ix(s,this,a)}:s;return e?t(d,o):t(d)}:t},hx=ma,ux=Wn,px=cx(ux.setInterval,!0);hx({global:!0,bind:!0,forced:ux.setInterval!==px},{setInterval:px});var fx=ma,mx=Wn,vx=cx(mx.setTimeout,!0);fx({global:!0,bind:!0,forced:mx.setTimeout!==vx},{setTimeout:vx});var gx=Hn($o.setTimeout),yx={exports:{}};!function(t){function e(t){if(t)return function(t){return Object.assign(t,e.prototype),t._callbacks=new Map,t}(t);this._callbacks=new Map}e.prototype.on=function(t,e){const i=this._callbacks.get(t)??[];return i.push(e),this._callbacks.set(t,i),this},e.prototype.once=function(t,e){const i=(...n)=>{this.off(t,i),e.apply(this,n)};return i.fn=e,this.on(t,i),this},e.prototype.off=function(t,e){if(void 0===t&&void 0===e)return this._callbacks.clear(),this;if(void 0===e)return this._callbacks.delete(t),this;const i=this._callbacks.get(t);if(i){for(const[t,n]of i.entries())if(n===e||n.fn===e){i.splice(t,1);break}0===i.length?this._callbacks.delete(t):this._callbacks.set(t,i)}return this},e.prototype.emit=function(t,...e){const i=this._callbacks.get(t);if(i){const t=[...i];for(const i of t)i.apply(this,e)}return this},e.prototype.listeners=function(t){return this._callbacks.get(t)??[]},e.prototype.listenerCount=function(t){if(t)return this.listeners(t).length;let e=0;for(const t of this._callbacks.values())e+=t.length;return e},e.prototype.hasListeners=function(t){return this.listenerCount(t)>0},e.prototype.addEventListener=e.prototype.on,e.prototype.removeListener=e.prototype.off,e.prototype.removeEventListener=e.prototype.off,e.prototype.removeAllListeners=e.prototype.off,t.exports=e}(yx);var bx,xx=Hn(yx.exports); -/*! Hammer.JS - v2.0.17-rc - 2019-12-16 - * http://naver.github.io/egjs - * - * Forked By Naver egjs - * Copyright (c) hammerjs - * Licensed under the MIT license */ -function _x(){return _x=Object.assign||function(t){for(var e=1;e-1}var l_=function(){function t(t,e){this.manager=t,this.set(e)}var e=t.prototype;return e.set=function(t){t===Mx&&(t=this.compute()),Dx&&this.manager.element.style&&jx[t]&&(this.manager.element.style[Px]=t),this.actions=t.toLowerCase().trim()},e.update=function(){this.set(this.manager.options.touchAction)},e.compute=function(){var t=[];return s_(this.manager.recognizers,(function(e){a_(e.options.enable,[e])&&(t=t.concat(e.getTouchAction()))})),function(t){if(d_(t,Bx))return Bx;var e=d_(t,Lx),i=d_(t,zx);return e&&i?Bx:e||i?e?Lx:zx:d_(t,Nx)?Nx:Fx}(t.join(" "))},e.preventDefaults=function(t){var e=t.srcEvent,i=t.offsetDirection;if(this.manager.session.prevented)e.preventDefault();else{var n=this.actions,o=d_(n,Bx)&&!jx[Bx],r=d_(n,zx)&&!jx[zx],s=d_(n,Lx)&&!jx[Lx];if(o){var a=1===t.pointers.length,d=t.distance<2,l=t.deltaTime<250;if(a&&d&&l)return}if(!s||!r)return o||r&&i&e_||s&&i&i_?this.preventSrc(e):void 0}},e.preventSrc=function(t){this.manager.session.prevented=!0,t.preventDefault()},t}();function c_(t,e){for(;t;){if(t===e)return!0;t=t.parentNode}return!1}function h_(t){var e=t.length;if(1===e)return{x:Tx(t[0].clientX),y:Tx(t[0].clientY)};for(var i=0,n=0,o=0;o=Ix(e)?t<0?Zx:Qx:e<0?Jx:t_}function v_(t,e,i){return{x:e/t||0,y:i/t||0}}function g_(t,e){var i=t.session,n=e.pointers,o=n.length;i.firstInput||(i.firstInput=u_(e)),o>1&&!i.firstMultiple?i.firstMultiple=u_(e):1===o&&(i.firstMultiple=!1);var r=i.firstInput,s=i.firstMultiple,a=s?s.center:r.center,d=e.center=h_(n);e.timeStamp=Ax(),e.deltaTime=e.timeStamp-r.timeStamp,e.angle=f_(a,d),e.distance=p_(a,d),function(t,e){var i=e.center,n=t.offsetDelta||{},o=t.prevDelta||{},r=t.prevInput||{};e.eventType!==Xx&&r.eventType!==Yx||(o=t.prevDelta={x:r.deltaX||0,y:r.deltaY||0},n=t.offsetDelta={x:i.x,y:i.y}),e.deltaX=o.x+(i.x-n.x),e.deltaY=o.y+(i.y-n.y)}(i,e),e.offsetDirection=m_(e.deltaX,e.deltaY);var l,c,h=v_(e.deltaTime,e.deltaX,e.deltaY);e.overallVelocityX=h.x,e.overallVelocityY=h.y,e.overallVelocity=Ix(h.x)>Ix(h.y)?h.x:h.y,e.scale=s?(l=s.pointers,p_((c=n)[0],c[1],r_)/p_(l[0],l[1],r_)):1,e.rotation=s?function(t,e){return f_(e[1],e[0],r_)+f_(t[1],t[0],r_)}(s.pointers,n):0,e.maxPointers=i.prevInput?e.pointers.length>i.prevInput.maxPointers?e.pointers.length:i.prevInput.maxPointers:e.pointers.length,function(t,e){var i,n,o,r,s=t.lastInterval||e,a=e.timeStamp-s.timeStamp;if(e.eventType!==Gx&&(a>qx||void 0===s.velocity)){var d=e.deltaX-s.deltaX,l=e.deltaY-s.deltaY,c=v_(a,d,l);n=c.x,o=c.y,i=Ix(c.x)>Ix(c.y)?c.x:c.y,r=m_(d,l),t.lastInterval=e}else i=s.velocity,n=s.velocityX,o=s.velocityY,r=s.direction;e.velocity=i,e.velocityX=n,e.velocityY=o,e.direction=r}(i,e);var u,p=t.element,f=e.srcEvent;c_(u=f.composedPath?f.composedPath()[0]:f.path?f.path[0]:f.target,p)&&(p=u),e.target=p}function y_(t,e,i){var n=i.pointers.length,o=i.changedPointers.length,r=e&Xx&&n-o==0,s=e&(Yx|Gx)&&n-o==0;i.isFirst=!!r,i.isFinal=!!s,r&&(t.session={}),i.eventType=e,g_(t,i),t.emit("hammer.input",i),t.recognize(i),t.session.prevInput=i}function b_(t){return t.trim().split(/\s+/g)}function x_(t,e,i){s_(b_(e),(function(e){t.addEventListener(e,i,!1)}))}function __(t,e,i){s_(b_(e),(function(e){t.removeEventListener(e,i,!1)}))}function w_(t){var e=t.ownerDocument||t;return e.defaultView||e.parentWindow||window}var E_=function(){function t(t,e){var i=this;this.manager=t,this.callback=e,this.element=t.element,this.target=t.options.inputTarget,this.domHandler=function(e){a_(t.options.enable,[t])&&i.handler(e)},this.init()}var e=t.prototype;return e.handler=function(){},e.init=function(){this.evEl&&x_(this.element,this.evEl,this.domHandler),this.evTarget&&x_(this.target,this.evTarget,this.domHandler),this.evWin&&x_(w_(this.element),this.evWin,this.domHandler)},e.destroy=function(){this.evEl&&__(this.element,this.evEl,this.domHandler),this.evTarget&&__(this.target,this.evTarget,this.domHandler),this.evWin&&__(w_(this.element),this.evWin,this.domHandler)},t}();function k_(t,e,i){if(t.indexOf&&!i)return t.indexOf(e);for(var n=0;ni[e]})):n.sort()),n}var P_={touchstart:Xx,touchmove:2,touchend:Yx,touchcancel:Gx},D_=function(t){function e(){var i;return e.prototype.evTarget="touchstart touchmove touchend touchcancel",(i=t.apply(this,arguments)||this).targetIds={},i}return wx(e,t),e.prototype.handler=function(t){var e=P_[t.type],i=M_.call(this,t,e);i&&this.callback(this.manager,e,{pointers:i[0],changedPointers:i[1],pointerType:Wx,srcEvent:t})},e}(E_);function M_(t,e){var i,n,o=A_(t.touches),r=this.targetIds;if(e&(2|Xx)&&1===o.length)return r[o[0].identifier]=!0,[o,o];var s=A_(t.changedTouches),a=[],d=this.target;if(n=o.filter((function(t){return c_(t.target,d)})),e===Xx)for(i=0;i-1&&n.splice(t,1)}),B_)}}function z_(t,e){t&Xx?(this.primaryTouch=e.changedPointers[0].identifier,L_.call(this,e)):t&(Yx|Gx)&&L_.call(this,e)}function j_(t){for(var e=t.srcEvent.clientX,i=t.srcEvent.clientY,n=0;n-1&&this.requireFail.splice(e,1),this},e.hasRequireFailures=function(){return this.requireFail.length>0},e.canRecognizeWith=function(t){return!!this.simultaneous[t.id]},e.emit=function(t){var e=this,i=this.state;function n(i){e.manager.emit(i,t)}i<8&&n(e.options.event+q_(i)),n(e.options.event),t.additionalEvent&&n(t.additionalEvent),i>=8&&n(e.options.event+q_(i))},e.tryEmit=function(t){if(this.canEmit())return this.emit(t);this.state=U_},e.canEmit=function(){for(var t=0;te.threshold&&o&e.direction},i.attrTest=function(t){return G_.prototype.attrTest.call(this,t)&&(2&this.state||!(2&this.state)&&this.directionTest(t))},i.emit=function(e){this.pX=e.deltaX,this.pY=e.deltaY;var i=K_(e.direction);i&&(e.additionalEvent=this.options.event+i),t.prototype.emit.call(this,e)},e}(G_),Q_=function(t){function e(e){return void 0===e&&(e={}),t.call(this,_x({event:"swipe",threshold:10,velocity:.3,direction:e_|i_,pointers:1},e))||this}wx(e,t);var i=e.prototype;return i.getTouchAction=function(){return Z_.prototype.getTouchAction.call(this)},i.attrTest=function(e){var i,n=this.options.direction;return n&(e_|i_)?i=e.overallVelocity:n&e_?i=e.overallVelocityX:n&i_&&(i=e.overallVelocityY),t.prototype.attrTest.call(this,e)&&n&e.offsetDirection&&e.distance>this.options.threshold&&e.maxPointers===this.options.pointers&&Ix(i)>this.options.velocity&&e.eventType&Yx},i.emit=function(t){var e=K_(t.offsetDirection);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)},e}(G_),J_=function(t){function e(e){return void 0===e&&(e={}),t.call(this,_x({event:"pinch",threshold:0,pointers:2},e))||this}wx(e,t);var i=e.prototype;return i.getTouchAction=function(){return[Bx]},i.attrTest=function(e){return t.prototype.attrTest.call(this,e)&&(Math.abs(e.scale-1)>this.options.threshold||2&this.state)},i.emit=function(e){if(1!==e.scale){var i=e.scale<1?"in":"out";e.additionalEvent=this.options.event+i}t.prototype.emit.call(this,e)},e}(G_),tw=function(t){function e(e){return void 0===e&&(e={}),t.call(this,_x({event:"rotate",threshold:0,pointers:2},e))||this}wx(e,t);var i=e.prototype;return i.getTouchAction=function(){return[Bx]},i.attrTest=function(e){return t.prototype.attrTest.call(this,e)&&(Math.abs(e.rotation)>this.options.threshold||2&this.state)},e}(G_),ew=function(t){function e(e){var i;return void 0===e&&(e={}),(i=t.call(this,_x({event:"press",pointers:1,time:251,threshold:9},e))||this)._timer=null,i._input=null,i}wx(e,t);var i=e.prototype;return i.getTouchAction=function(){return[Fx]},i.process=function(t){var e=this,i=this.options,n=t.pointers.length===i.pointers,o=t.distancei.time;if(this._input=t,!o||!n||t.eventType&(Yx|Gx)&&!r)this.reset();else if(t.eventType&Xx)this.reset(),this._timer=setTimeout((function(){e.state=8,e.tryEmit()}),i.time);else if(t.eventType&Yx)return 8;return U_},i.reset=function(){clearTimeout(this._timer)},i.emit=function(t){8===this.state&&(t&&t.eventType&Yx?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=Ax(),this.manager.emit(this.options.event,this._input)))},e}(X_),iw={domEvents:!1,touchAction:Mx,enable:!0,inputTarget:null,inputClass:null,cssProps:{userSelect:"none",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},nw=[[tw,{enable:!1}],[J_,{enable:!1},["rotate"]],[Q_,{direction:e_}],[Z_,{direction:e_},["swipe"]],[Y_],[Y_,{event:"doubletap",taps:2},["tap"]],[ew]];function ow(t,e){var i,n=t.element;n.style&&(s_(t.options.cssProps,(function(o,r){i=Rx(n.style,r),e?(t.oldCssProps[i]=n.style[i],n.style[i]=o):n.style[i]=t.oldCssProps[i]||""})),e||(t.oldCssProps={}))}var rw=function(){function t(t,e){var i,n=this;this.options=Ox({},iw,e||{}),this.options.inputTarget=this.options.inputTarget||t,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=t,this.input=new((i=this).options.inputClass||($x?I_:Ux?D_:Hx?H_:N_))(i,y_),this.touchAction=new l_(this,this.options.touchAction),ow(this,!0),s_(this.options.recognizers,(function(t){var e=n.add(new t[0](t[1]));t[2]&&e.recognizeWith(t[2]),t[3]&&e.requireFailure(t[3])}),this)}var e=t.prototype;return e.set=function(t){return Ox(this.options,t),t.touchAction&&this.touchAction.update(),t.inputTarget&&(this.input.destroy(),this.input.target=t.inputTarget,this.input.init()),this},e.stop=function(t){this.session.stopped=t?2:1},e.recognize=function(t){var e=this.session;if(!e.stopped){var i;this.touchAction.preventDefaults(t);var n=this.recognizers,o=e.curRecognizer;(!o||o&&8&o.state)&&(e.curRecognizer=null,o=null);for(var r=0;r\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",o=window.console&&(window.console.warn||window.console.log);return o&&o.call(window.console,n,i),t.apply(this,arguments)}}var cw=lw((function(t,e,i){for(var n=Object.keys(e),o=0;o=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function gw(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i2)return xw.apply(void 0,wy(n=[bw(e[0],e[1])]).call(n,my(Ey(e).call(e,2))));var o=e[0],r=e[1];if(o instanceof Date&&r instanceof Date)return o.setTime(r.getTime()),o;var s,a=vw(Ay(r));try{for(a.s();!(s=a.n()).done;){var d=s.value;Object.prototype.propertyIsEnumerable.call(r,d)&&(r[d]===yw?delete o[d]:null===o[d]||null===r[d]||"object"!=typeof o[d]||"object"!=typeof r[d]||Ry(o[d])||Ry(r[d])?o[d]=_w(r[d]):o[d]=xw(o[d],r[d]))}}catch(t){a.e(t)}finally{a.f()}return o}function _w(t){return Ry(t)?Ev(t).call(t,(function(t){return _w(t)})):"object"==typeof t&&null!==t?t instanceof Date?new Date(t.getTime()):xw({},t):t}function ww(t){for(var e=0,i=My(t);e({set:t})}}()};function kw(t){var e,i=this;this._cleanupQueue=[],this.active=!1,this._dom={container:t,overlay:document.createElement("div")},this._dom.overlay.classList.add("vis-overlay"),this._dom.container.appendChild(this._dom.overlay),this._cleanupQueue.push((function(){i._dom.overlay.parentNode.removeChild(i._dom.overlay)}));var n=Ew(this._dom.overlay);n.on("tap",qm(e=this._onTapOverlay).call(e,this)),this._cleanupQueue.push((function(){n.destroy()}));var o=["tap","doubletap","press","pinch","pan","panstart","panmove","panend"];Vy(o).call(o,(function(t){n.on(t,(function(t){t.srcEvent.stopPropagation()}))})),document&&document.body&&(this._onClick=function(e){(function(t,e){for(;t;){if(t===e)return!0;t=t.parentNode}return!1})(e.target,t)||i.deactivate()},document.body.addEventListener("click",this._onClick),this._cleanupQueue.push((function(){document.body.removeEventListener("click",i._onClick)}))),this._escListener=function(t){("key"in t?"Escape"===t.key:27===t.keyCode)&&i.deactivate()}}xx(kw.prototype),kw.current=null,kw.prototype.destroy=function(){var t,e;this.deactivate();var i,n=vw(ib(t=kb(e=this._cleanupQueue).call(e,0)).call(t));try{for(n.s();!(i=n.n()).done;){(0,i.value)()}}catch(t){n.e(t)}finally{n.f()}},kw.prototype.activate=function(){kw.current&&kw.current.deactivate(),kw.current=this,this.active=!0,this._dom.overlay.style.display="none",this._dom.container.classList.add("vis-active"),this.emit("change"),this.emit("activate"),document.body.addEventListener("keydown",this._escListener)},kw.prototype.deactivate=function(){this.active=!1,this._dom.overlay.style.display="block",this._dom.container.classList.remove("vis-active"),document.body.removeEventListener("keydown",this._escListener),this.emit("change"),this.emit("deactivate")},kw.prototype._onTapOverlay=function(t){this.activate(),t.srcEvent.stopPropagation()};var Ow=pd,Cw=fr,Sw=TypeError,Tw=function(t){if(Ow(t))return t;throw new Sw(Cw(t)+" is not a constructor")},Iw=ma,Aw=Zn,Rw=Fm,Pw=Tw,Dw=Us,Mw=Ho,Fw=Dl,Nw=Vn,Bw=Xo("Reflect","construct"),Lw=Object.prototype,zw=[].push,jw=Nw((function(){function t(){}return!(Bw((function(){}),[],t)instanceof t)})),Hw=!Nw((function(){Bw((function(){}))})),$w=jw||Hw;Iw({target:"Reflect",stat:!0,forced:$w,sham:$w},{construct:function(t,e){Pw(t),Dw(e);var i=arguments.length<3?t:Pw(arguments[2]);if(Hw&&!jw)return Bw(t,e,i);if(t===i){switch(e.length){case 0:return new t;case 1:return new t(e[0]);case 2:return new t(e[0],e[1]);case 3:return new t(e[0],e[1],e[2]);case 4:return new t(e[0],e[1],e[2],e[3])}var n=[null];return Aw(zw,n,e),new(Aw(Rw,t,n))}var o=i.prototype,r=Fw(Mw(o)?o:Lw),s=Aw(t,r,e);return Mw(s)?s:r}});var Uw=Hn($o.Reflect.construct),Ww=Hn($o.Object.getOwnPropertySymbols),Vw={exports:{}},qw=ma,Xw=Vn,Yw=Lo,Gw=mo.f,Kw=vo;qw({target:"Object",stat:!0,forced:!Kw||Xw((function(){Gw(1)})),sham:!Kw},{getOwnPropertyDescriptor:function(t,e){return Gw(Yw(t),e)}});var Zw=$o.Object,Qw=Vw.exports=function(t,e){return Zw.getOwnPropertyDescriptor(t,e)};Zw.getOwnPropertyDescriptor.sham&&(Qw.sham=!0);var Jw=Hn(Vw.exports),tE=Iy,eE=Lo,iE=mo,nE=ja;ma({target:"Object",stat:!0,sham:!vo},{getOwnPropertyDescriptors:function(t){for(var e,i,n=eE(t),o=iE.f,r=tE(n),s={},a=0;r.length>a;)void 0!==(i=o(n,e=r[a++]))&&nE(s,e,i);return s}});var oE=Hn($o.Object.getOwnPropertyDescriptors),rE={exports:{}},sE=ma,aE=vo,dE=$d.f;sE({target:"Object",stat:!0,forced:Object.defineProperties!==dE,sham:!aE},{defineProperties:dE});var lE=$o.Object,cE=rE.exports=function(t,e){return lE.defineProperties(t,e)};lE.defineProperties.sham&&(cE.sham=!0);var hE=Hn(rE.exports),uE=Hn(_a);function pE(t){if(void 0===t)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return t}var fE=qb,mE=Hn(fE);ma({target:"Object",stat:!0},{setPrototypeOf:Vp});var vE=$o.Object.setPrototypeOf,gE=Hn(vE),yE=Hn(Vm);function bE(t,e){var i;return bE=gE?yE(i=gE).call(i):function(t,e){return t.__proto__=e,t},bE(t,e)}var xE=Ub,_E=Hn(xE);function wE(t){var e;return wE=gE?yE(e=_E).call(e):function(t){return t.__proto__||_E(t)},wE(t)}var EE={exports:{}},kE={exports:{}};!function(t){var e=Qf,i=gm;function n(o){return t.exports=n="function"==typeof e&&"symbol"==typeof i?function(t){return typeof t}:function(t){return t&&"function"==typeof e&&t.constructor===e&&t!==e.prototype?"symbol":typeof t},t.exports.__esModule=!0,t.exports.default=t.exports,n(o)}t.exports=n,t.exports.__esModule=!0,t.exports.default=t.exports}(kE);var OE=kE.exports,CE=Wy,SE=zr,TE=Iy,IE=mo,AE=Ls,RE=Ho,PE=na,DE=Error,ME=io("".replace),FE=String(new DE("zxcasd").stack),NE=/\n\s*at [^:]*:[^\n]*/,BE=NE.test(FE),LE=Co,zE=!Vn((function(){var t=new Error("a");return!("stack"in t)||(Object.defineProperty(t,"stack",LE(1,7)),7!==t.stack)})),jE=na,HE=function(t,e){if(BE&&"string"==typeof t&&!DE.prepareStackTrace)for(;e--;)t=ME(t,NE,"");return t},$E=zE,UE=Error.captureStackTrace,WE=Bs,VE=bo,qE=Us,XE=fr,YE=Uv,GE=Ma,KE=Yo,ZE=ig,QE=Gv,JE=Bv,tk=TypeError,ek=function(t,e){this.stopped=t,this.result=e},ik=ek.prototype,nk=function(t,e,i){var n,o,r,s,a,d,l,c=i&&i.that,h=!(!i||!i.AS_ENTRIES),u=!(!i||!i.IS_RECORD),p=!(!i||!i.IS_ITERATOR),f=!(!i||!i.INTERRUPTED),m=WE(e,c),v=function(t){return n&&JE(n,"normal",t),new ek(!0,t)},g=function(t){return h?(qE(t),f?m(t[0],t[1],v):m(t[0],t[1])):f?m(t,v):m(t)};if(u)n=t.iterator;else if(p)n=t;else{if(!(o=QE(t)))throw new tk(XE(t)+" is not iterable");if(YE(o)){for(r=0,s=GE(t);s>r;r++)if((a=g(t[r]))&&KE(ik,a))return a;return new ek(!1)}n=ZE(t,o)}for(d=u?t.next:n.next;!(l=VE(d,n)).done;){try{a=g(l.value)}catch(t){JE(n,"throw",t)}if("object"==typeof a&&a&&KE(ik,a))return a}return new ek(!1)},ok=Hd,rk=ma,sk=Yo,ak=xp,dk=Vp,lk=function(t,e,i){for(var n=TE(e),o=AE.f,r=IE.f,s=0;s2&&pk(i,arguments[2]);var o=[];return mk(t,bk,{that:o}),hk(i,"errors",o),i};dk?dk(xk,yk):lk(xk,yk,{name:!0});var _k=xk.prototype=ck(yk.prototype,{constructor:uk(1,xk),message:uk(1,""),name:uk(1,"AggregateError")});rk({global:!0,constructor:!0,arity:2},{AggregateError:xk});var wk,Ek,kk,Ok,Ck=Xo,Sk=Jl,Tk=vo,Ik=Jr("species"),Ak=function(t){var e=Ck(t);Tk&&e&&!e[Ik]&&Sk(e,Ik,{configurable:!0,get:function(){return this}})},Rk=Yo,Pk=TypeError,Dk=function(t,e){if(Rk(e,t))return t;throw new Pk("Incorrect invocation")},Mk=Us,Fk=Tw,Nk=Po,Bk=Jr("species"),Lk=function(t,e){var i,n=Mk(t).constructor;return void 0===n||Nk(i=Mk(n)[Bk])?e:Fk(i)},zk=/(?:ipad|iphone|ipod).*applewebkit/i.test(Go),jk=Wn,Hk=Zn,$k=Bs,Uk=fo,Wk=zr,Vk=Vn,qk=vl,Xk=bu,Yk=ps,Gk=tx,Kk=zk,Zk=iv,Qk=jk.setImmediate,Jk=jk.clearImmediate,tO=jk.process,eO=jk.Dispatch,iO=jk.Function,nO=jk.MessageChannel,oO=jk.String,rO=0,sO={},aO="onreadystatechange";Vk((function(){wk=jk.location}));var dO=function(t){if(Wk(sO,t)){var e=sO[t];delete sO[t],e()}},lO=function(t){return function(){dO(t)}},cO=function(t){dO(t.data)},hO=function(t){jk.postMessage(oO(t),wk.protocol+"//"+wk.host)};Qk&&Jk||(Qk=function(t){Gk(arguments.length,1);var e=Uk(t)?t:iO(t),i=Xk(arguments,1);return sO[++rO]=function(){Hk(e,void 0,i)},Ek(rO),rO},Jk=function(t){delete sO[t]},Zk?Ek=function(t){tO.nextTick(lO(t))}:eO&&eO.now?Ek=function(t){eO.now(lO(t))}:nO&&!Kk?(Ok=(kk=new nO).port2,kk.port1.onmessage=cO,Ek=$k(Ok.postMessage,Ok)):jk.addEventListener&&Uk(jk.postMessage)&&!jk.importScripts&&wk&&"file:"!==wk.protocol&&!Vk(hO)?(Ek=hO,jk.addEventListener("message",cO,!1)):Ek=aO in Yk("script")?function(t){qk.appendChild(Yk("script"))[aO]=function(){qk.removeChild(this),dO(t)}}:function(t){setTimeout(lO(t),0)});var uO={set:Qk,clear:Jk},pO=function(){this.head=null,this.tail=null};pO.prototype={add:function(t){var e={item:t,next:null},i=this.tail;i?i.next=e:this.head=e,this.tail=e},get:function(){var t=this.head;if(t)return null===(this.head=t.next)&&(this.tail=null),t.item}};var fO,mO,vO,gO,yO,bO=pO,xO=/ipad|iphone|ipod/i.test(Go)&&"undefined"!=typeof Pebble,_O=/web0s(?!.*chrome)/i.test(Go),wO=Wn,EO=Bs,kO=mo.f,OO=uO.set,CO=bO,SO=zk,TO=xO,IO=_O,AO=iv,RO=wO.MutationObserver||wO.WebKitMutationObserver,PO=wO.document,DO=wO.process,MO=wO.Promise,FO=kO(wO,"queueMicrotask"),NO=FO&&FO.value;if(!NO){var BO=new CO,LO=function(){var t,e;for(AO&&(t=DO.domain)&&t.exit();e=BO.get();)try{e()}catch(t){throw BO.head&&fO(),t}t&&t.enter()};SO||AO||IO||!RO||!PO?!TO&&MO&&MO.resolve?((gO=MO.resolve(void 0)).constructor=MO,yO=EO(gO.then,gO),fO=function(){yO(LO)}):AO?fO=function(){DO.nextTick(LO)}:(OO=EO(OO,wO),fO=function(){OO(LO)}):(mO=!0,vO=PO.createTextNode(""),new RO(LO).observe(vO,{characterData:!0}),fO=function(){vO.data=mO=!mO}),NO=function(t){BO.head||fO(),BO.add(t)}}var zO=NO,jO=function(t){try{return{error:!1,value:t()}}catch(t){return{error:!0,value:t}}},HO=Wn.Promise,$O="object"==typeof Deno&&Deno&&"object"==typeof Deno.version,UO=!$O&&!iv&&"object"==typeof window&&"object"==typeof document,WO=Wn,VO=HO,qO=fo,XO=Ds,YO=Ja,GO=Jr,KO=UO,ZO=$O,QO=ir,JO=VO&&VO.prototype,tC=GO("species"),eC=!1,iC=qO(WO.PromiseRejectionEvent),nC=XO("Promise",(function(){var t=YO(VO),e=t!==String(VO);if(!e&&66===QO)return!0;if(!JO.catch||!JO.finally)return!0;if(!QO||QO<51||!/native code/.test(t)){var i=new VO((function(t){t(1)})),n=function(t){t((function(){}),(function(){}))};if((i.constructor={})[tC]=n,!(eC=i.then((function(){}))instanceof n))return!0}return!e&&(KO||ZO)&&!iC})),oC={CONSTRUCTOR:nC,REJECTION_EVENT:iC,SUBCLASSING:eC},rC={},sC=yr,aC=TypeError,dC=function(t){var e,i;this.promise=new t((function(t,n){if(void 0!==e||void 0!==i)throw new aC("Bad Promise constructor");e=t,i=n})),this.resolve=sC(e),this.reject=sC(i)};rC.f=function(t){return new dC(t)};var lC,cC,hC=ma,uC=iv,pC=Wn,fC=bo,mC=Zl,vC=Ec,gC=Ak,yC=yr,bC=fo,xC=Ho,_C=Dk,wC=Lk,EC=uO.set,kC=zO,OC=function(t,e){try{1===arguments.length?console.error(t):console.error(t,e)}catch(t){}},CC=jO,SC=bO,TC=zc,IC=HO,AC=rC,RC="Promise",PC=oC.CONSTRUCTOR,DC=oC.REJECTION_EVENT,MC=TC.getterFor(RC),FC=TC.set,NC=IC&&IC.prototype,BC=IC,LC=NC,zC=pC.TypeError,jC=pC.document,HC=pC.process,$C=AC.f,UC=$C,WC=!!(jC&&jC.createEvent&&pC.dispatchEvent),VC="unhandledrejection",qC=function(t){var e;return!(!xC(t)||!bC(e=t.then))&&e},XC=function(t,e){var i,n,o,r=e.value,s=1===e.state,a=s?t.ok:t.fail,d=t.resolve,l=t.reject,c=t.domain;try{a?(s||(2===e.rejection&&QC(e),e.rejection=1),!0===a?i=r:(c&&c.enter(),i=a(r),c&&(c.exit(),o=!0)),i===t.promise?l(new zC("Promise-chain cycle")):(n=qC(i))?fC(n,i,d,l):d(i)):l(r)}catch(t){c&&!o&&c.exit(),l(t)}},YC=function(t,e){t.notified||(t.notified=!0,kC((function(){for(var i,n=t.reactions;i=n.get();)XC(i,t);t.notified=!1,e&&!t.rejection&&KC(t)})))},GC=function(t,e,i){var n,o;WC?((n=jC.createEvent("Event")).promise=e,n.reason=i,n.initEvent(t,!1,!0),pC.dispatchEvent(n)):n={promise:e,reason:i},!DC&&(o=pC["on"+t])?o(n):t===VC&&OC("Unhandled promise rejection",i)},KC=function(t){fC(EC,pC,(function(){var e,i=t.facade,n=t.value;if(ZC(t)&&(e=CC((function(){uC?HC.emit("unhandledRejection",n,i):GC(VC,i,n)})),t.rejection=uC||ZC(t)?2:1,e.error))throw e.value}))},ZC=function(t){return 1!==t.rejection&&!t.parent},QC=function(t){fC(EC,pC,(function(){var e=t.facade;uC?HC.emit("rejectionHandled",e):GC("rejectionhandled",e,t.value)}))},JC=function(t,e,i){return function(n){t(e,n,i)}},tS=function(t,e,i){t.done||(t.done=!0,i&&(t=i),t.value=e,t.state=2,YC(t,!0))},eS=function(t,e,i){if(!t.done){t.done=!0,i&&(t=i);try{if(t.facade===e)throw new zC("Promise can't be resolved itself");var n=qC(e);n?kC((function(){var i={done:!1};try{fC(n,e,JC(eS,i,t),JC(tS,i,t))}catch(e){tS(i,e,t)}})):(t.value=e,t.state=1,YC(t,!1))}catch(e){tS({done:!1},e,t)}}};PC&&(LC=(BC=function(t){_C(this,LC),yC(t),fC(lC,this);var e=MC(this);try{t(JC(eS,e),JC(tS,e))}catch(t){tS(e,t)}}).prototype,(lC=function(t){FC(this,{type:RC,done:!1,notified:!1,parent:!1,reactions:new SC,rejection:!1,state:0,value:void 0})}).prototype=mC(LC,"then",(function(t,e){var i=MC(this),n=$C(wC(this,BC));return i.parent=!0,n.ok=!bC(t)||t,n.fail=bC(e)&&e,n.domain=uC?HC.domain:void 0,0===i.state?i.reactions.add(n):kC((function(){XC(n,i)})),n.promise})),cC=function(){var t=new lC,e=MC(t);this.promise=t,this.resolve=JC(eS,e),this.reject=JC(tS,e)},AC.f=$C=function(t){return t===BC||undefined===t?new cC(t):UC(t)}),hC({global:!0,constructor:!0,wrap:!0,forced:PC},{Promise:BC}),vC(BC,RC,!1,!0),gC(RC);var iS=HO,nS=oC.CONSTRUCTOR||!yg((function(t){iS.all(t).then(void 0,(function(){}))})),oS=bo,rS=yr,sS=rC,aS=jO,dS=nk;ma({target:"Promise",stat:!0,forced:nS},{all:function(t){var e=this,i=sS.f(e),n=i.resolve,o=i.reject,r=aS((function(){var i=rS(e.resolve),r=[],s=0,a=1;dS(t,(function(t){var d=s++,l=!1;a++,oS(i,e,t).then((function(t){l||(l=!0,r[d]=t,--a||n(r))}),o)})),--a||n(r)}));return r.error&&o(r.value),i.promise}});var lS=ma,cS=oC.CONSTRUCTOR;HO&&HO.prototype,lS({target:"Promise",proto:!0,forced:cS,real:!0},{catch:function(t){return this.then(void 0,t)}});var hS=bo,uS=yr,pS=rC,fS=jO,mS=nk;ma({target:"Promise",stat:!0,forced:nS},{race:function(t){var e=this,i=pS.f(e),n=i.reject,o=fS((function(){var o=uS(e.resolve);mS(t,(function(t){hS(o,e,t).then(i.resolve,n)}))}));return o.error&&n(o.value),i.promise}});var vS=bo,gS=rC;ma({target:"Promise",stat:!0,forced:oC.CONSTRUCTOR},{reject:function(t){var e=gS.f(this);return vS(e.reject,void 0,t),e.promise}});var yS=Us,bS=Ho,xS=rC,_S=function(t,e){if(yS(t),bS(e)&&e.constructor===t)return e;var i=xS.f(t);return(0,i.resolve)(e),i.promise},wS=ma,ES=HO,kS=oC.CONSTRUCTOR,OS=_S,CS=Xo("Promise"),SS=!kS;wS({target:"Promise",stat:!0,forced:true},{resolve:function(t){return OS(SS&&this===CS?ES:this,t)}});var TS=bo,IS=yr,AS=rC,RS=jO,PS=nk;ma({target:"Promise",stat:!0,forced:nS},{allSettled:function(t){var e=this,i=AS.f(e),n=i.resolve,o=i.reject,r=RS((function(){var i=IS(e.resolve),o=[],r=0,s=1;PS(t,(function(t){var a=r++,d=!1;s++,TS(i,e,t).then((function(t){d||(d=!0,o[a]={status:"fulfilled",value:t},--s||n(o))}),(function(t){d||(d=!0,o[a]={status:"rejected",reason:t},--s||n(o))}))})),--s||n(o)}));return r.error&&o(r.value),i.promise}});var DS=bo,MS=yr,FS=Xo,NS=rC,BS=jO,LS=nk,zS="No one promise resolved";ma({target:"Promise",stat:!0,forced:nS},{any:function(t){var e=this,i=FS("AggregateError"),n=NS.f(e),o=n.resolve,r=n.reject,s=BS((function(){var n=MS(e.resolve),s=[],a=0,d=1,l=!1;LS(t,(function(t){var c=a++,h=!1;d++,DS(n,e,t).then((function(t){h||l||(l=!0,o(t))}),(function(t){h||l||(h=!0,s[c]=t,--d||r(new i(s,zS)))}))})),--d||r(new i(s,zS))}));return s.error&&r(s.value),n.promise}});var jS=ma,HS=HO,$S=Vn,US=Xo,WS=fo,VS=Lk,qS=_S,XS=HS&&HS.prototype;jS({target:"Promise",proto:!0,real:!0,forced:!!HS&&$S((function(){XS.finally.call({then:function(){}},(function(){}))}))},{finally:function(t){var e=VS(this,US("Promise")),i=WS(t);return this.then(i?function(i){return qS(e,t()).then((function(){return i}))}:t,i?function(i){return qS(e,t()).then((function(){throw i}))}:t)}});var YS=$o.Promise,GS=rC;ma({target:"Promise",stat:!0},{withResolvers:function(){var t=GS.f(this);return{promise:t.promise,resolve:t.resolve,reject:t.reject}}});var KS=YS,ZS=rC,QS=jO;ma({target:"Promise",stat:!0,forced:!0},{try:function(t){var e=ZS.f(this),i=QS(t);return(i.error?e.reject:e.resolve)(i.value),e.promise}});var JS=KS,tT=eb;!function(t){var e=OE.default,i=wa,n=Qf,o=fE,r=xE,s=CE,a=Hg,d=vE,l=JS,c=tT,h=ly;function u(){t.exports=u=function(){return f},t.exports.__esModule=!0,t.exports.default=t.exports;var p,f={},m=Object.prototype,v=m.hasOwnProperty,g=i||function(t,e,i){t[e]=i.value},y="function"==typeof n?n:{},b=y.iterator||"@@iterator",x=y.asyncIterator||"@@asyncIterator",_=y.toStringTag||"@@toStringTag";function w(t,e,n){return i(t,e,{value:n,enumerable:!0,configurable:!0,writable:!0}),t[e]}try{w({},"")}catch(p){w=function(t,e,i){return t[e]=i}}function E(t,e,i,n){var r=e&&e.prototype instanceof A?e:A,s=o(r.prototype),a=new $(n||[]);return g(s,"_invoke",{value:L(t,i,a)}),s}function k(t,e,i){try{return{type:"normal",arg:t.call(e,i)}}catch(t){return{type:"throw",arg:t}}}f.wrap=E;var O="suspendedStart",C="suspendedYield",S="executing",T="completed",I={};function A(){}function R(){}function P(){}var D={};w(D,b,(function(){return this}));var M=r&&r(r(U([])));M&&M!==m&&v.call(M,b)&&(D=M);var F=P.prototype=A.prototype=o(D);function N(t){var e;s(e=["next","throw","return"]).call(e,(function(e){w(t,e,(function(t){return this._invoke(e,t)}))}))}function B(t,i){function n(o,r,s,a){var d=k(t[o],t,r);if("throw"!==d.type){var l=d.arg,c=l.value;return c&&"object"==e(c)&&v.call(c,"__await")?i.resolve(c.__await).then((function(t){n("next",t,s,a)}),(function(t){n("throw",t,s,a)})):i.resolve(c).then((function(t){l.value=t,s(l)}),(function(t){return n("throw",t,s,a)}))}a(d.arg)}var o;g(this,"_invoke",{value:function(t,e){function r(){return new i((function(i,o){n(t,e,i,o)}))}return o=o?o.then(r,r):r()}})}function L(t,e,i){var n=O;return function(o,r){if(n===S)throw new Error("Generator is already running");if(n===T){if("throw"===o)throw r;return{value:p,done:!0}}for(i.method=o,i.arg=r;;){var s=i.delegate;if(s){var a=z(s,i);if(a){if(a===I)continue;return a}}if("next"===i.method)i.sent=i._sent=i.arg;else if("throw"===i.method){if(n===O)throw n=T,i.arg;i.dispatchException(i.arg)}else"return"===i.method&&i.abrupt("return",i.arg);n=S;var d=k(t,e,i);if("normal"===d.type){if(n=i.done?T:C,d.arg===I)continue;return{value:d.arg,done:i.done}}"throw"===d.type&&(n=T,i.method="throw",i.arg=d.arg)}}}function z(t,e){var i=e.method,n=t.iterator[i];if(n===p)return e.delegate=null,"throw"===i&&t.iterator.return&&(e.method="return",e.arg=p,z(t,e),"throw"===e.method)||"return"!==i&&(e.method="throw",e.arg=new TypeError("The iterator does not provide a '"+i+"' method")),I;var o=k(n,t.iterator,e.arg);if("throw"===o.type)return e.method="throw",e.arg=o.arg,e.delegate=null,I;var r=o.arg;return r?r.done?(e[t.resultName]=r.value,e.next=t.nextLoc,"return"!==e.method&&(e.method="next",e.arg=p),e.delegate=null,I):r:(e.method="throw",e.arg=new TypeError("iterator result is not an object"),e.delegate=null,I)}function j(t){var e,i={tryLoc:t[0]};1 in t&&(i.catchLoc=t[1]),2 in t&&(i.finallyLoc=t[2],i.afterLoc=t[3]),a(e=this.tryEntries).call(e,i)}function H(t){var e=t.completion||{};e.type="normal",delete e.arg,t.completion=e}function $(t){this.tryEntries=[{tryLoc:"root"}],s(t).call(t,j,this),this.reset(!0)}function U(t){if(t||""===t){var i=t[b];if(i)return i.call(t);if("function"==typeof t.next)return t;if(!isNaN(t.length)){var n=-1,o=function e(){for(;++n=0;--n){var o=this.tryEntries[n],r=o.completion;if("root"===o.tryLoc)return i("end");if(o.tryLoc<=this.prev){var s=v.call(o,"catchLoc"),a=v.call(o,"finallyLoc");if(s&&a){if(this.prev=0;--i){var n=this.tryEntries[i];if(n.tryLoc<=this.prev&&v.call(n,"finallyLoc")&&this.prev=0;--e){var i=this.tryEntries[e];if(i.finallyLoc===t)return this.complete(i.completion,i.afterLoc),H(i),I}},catch:function(t){for(var e=this.tryEntries.length-1;e>=0;--e){var i=this.tryEntries[e];if(i.tryLoc===t){var n=i.completion;if("throw"===n.type){var o=n.arg;H(i)}return o}}throw new Error("illegal catch attempt")},delegateYield:function(t,e,i){return this.delegate={iterator:U(t),resultName:e,nextLoc:i},"next"===this.method&&(this.arg=p),I}},f}t.exports=u,t.exports.__esModule=!0,t.exports.default=t.exports}(EE);var eT=(0,EE.exports)(),iT=eT;try{regeneratorRuntime=eT}catch(t){"object"==typeof globalThis?globalThis.regeneratorRuntime=eT:Function("r","regeneratorRuntime = r")(eT)}var nT=Hn(iT),oT={exports:{}},rT=Vn((function(){if("function"==typeof ArrayBuffer){var t=new ArrayBuffer(8);Object.isExtensible(t)&&Object.defineProperty(t,"a",{value:8})}})),sT=Vn,aT=Ho,dT=so,lT=rT,cT=Object.isExtensible,hT=sT((function(){cT(1)}))||lT?function(t){return!!aT(t)&&((!lT||"ArrayBuffer"!==dT(t))&&(!cT||cT(t)))}:cT,uT=!Vn((function(){return Object.isExtensible(Object.preventExtensions({}))})),pT=ma,fT=io,mT=Qd,vT=Ho,gT=zr,yT=Ls.f,bT=Ml,xT=Bl,_T=hT,wT=uT,ET=!1,kT=Wr("meta"),OT=0,CT=function(t){yT(t,kT,{value:{objectID:"O"+OT++,weakData:{}}})},ST=oT.exports={enable:function(){ST.enable=function(){},ET=!0;var t=bT.f,e=fT([].splice),i={};i[kT]=1,t(i).length&&(bT.f=function(i){for(var n=t(i),o=0,r=n.length;o1?arguments[1]:void 0);e=e?e.next:i.first;)for(n(e.value,e.key,this);e&&e.removed;)e=e.previous},has:function(t){return!!d(this,t)}}),GT(r,i?{get:function(t){var e=d(this,t);return e&&e.value},set:function(t,e){return a(this,0===t?0:t,e)}}:{add:function(t){return a(this,t=0===t?0:t,t)}}),nI&&YT(r,"size",{configurable:!0,get:function(){return s(this).size}}),o},setStrong:function(t,e,i){var n=e+" Iterator",o=sI(e),r=sI(n);tI(t,e,(function(t,e){rI(this,{type:n,target:t,state:o(t),kind:e,last:void 0})}),(function(){for(var t=r(this),e=t.kind,i=t.last;i&&i.removed;)i=i.previous;return t.target&&(t.last=i=i?i.next:t.state.first)?eI("keys"===e?i.key:"values"===e?i.value:[i.key,i.value],!1):(t.target=void 0,eI(void 0,!0))}),i?"entries":"values",!i,!0),iI(e)}};VT("Map",(function(t){return function(){return t(this,arguments.length?arguments[0]:void 0)}}),aI);var dI=Hn($o.Map),lI=Xc.some;ma({target:"Array",proto:!0,forced:!ev("some")},{some:function(t){return lI(this,t,arguments.length>1?arguments[1]:void 0)}});var cI=zm("Array","some"),hI=Yo,uI=cI,pI=Array.prototype,fI=function(t){var e=t.some;return t===pI||hI(pI,t)&&e===pI.some?uI:e},mI=Hn(fI),vI=zm("Array","keys"),gI=Ga,yI=zr,bI=Yo,xI=vI,_I=Array.prototype,wI={DOMTokenList:!0,NodeList:!0},EI=function(t){var e=t.keys;return t===_I||bI(_I,t)&&e===_I.keys||yI(wI,gI(t))?xI:e},kI=Hn(EI),OI=Ul,CI=Math.floor,SI=function(t,e){var i=t.length,n=CI(i/2);return i<8?TI(t,e):II(t,SI(OI(t,0,n),e),SI(OI(t,n),e),e)},TI=function(t,e){for(var i,n,o=t.length,r=1;r0;)t[n]=t[--n];n!==r++&&(t[n]=i)}return t},II=function(t,e,i,n){for(var o=e.length,r=i.length,s=0,a=0;s3)){if(XI)return!0;if(GI)return GI<603;var t,e,i,n,o="";for(t=65;t<76;t++){switch(e=String.fromCharCode(t),t){case 66:case 69:case 70:case 72:i=3;break;case 68:case 71:i=4;break;default:i=2}for(n=0;n<47;n++)KI.push({k:e+n,v:i})}for(KI.sort((function(t,e){return e.v-t.v})),n=0;n$I(i)?1:-1}}(t)),i=jI(o),n=0;nthis.max&&this.flush(),null!=this._timeout&&(clearTimeout(this._timeout),this._timeout=null),this.queue.length>0&&"number"==typeof this.delay&&(this._timeout=gx((function(){t.flush()}),this.delay))}},{key:"flush",value:function(){var t,e;Vy(t=kb(e=this._queue).call(e,0)).call(t,(function(t){t.fn.apply(t.context||t.fn,t.args||[])}))}}],[{key:"extend",value:function(e,i){var n=new t(i);if(void 0!==e.flush)throw new Error("Target object already has a property flush");e.flush=function(){n.flush()};var o=[{name:"flush",original:void 0}];if(i&&i.replace)for(var r=0;r=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function jA(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);inT.mark((function e(){var n,o,r,s,a;return nT.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=zA(i),e.prev=1,n.s();case 3:if((o=n.n()).done){e.next=10;break}if(r=fy(o.value,2),s=r[0],a=r[1],!t(a,s)){e.next=8;break}return e.next=8,[s,a];case 8:e.next=3;break;case 10:e.next=15;break;case 12:e.prev=12,e.t0=e.catch(1),n.e(e.t0);case 15:return e.prev=15,n.f(),e.finish(15);case 18:case"end":return e.stop()}}),e,null,[[1,12,15,18]])}))()})}},{key:"forEach",value:function(t){var e,i=zA(this._pairs);try{for(i.s();!(e=i.n()).done;){var n=fy(e.value,2),o=n[0];t(n[1],o)}}catch(t){i.e(t)}finally{i.f()}}},{key:"map",value:function(t){var i=this._pairs;return new e({[yA]:()=>nT.mark((function e(){var n,o,r,s,a;return nT.wrap((function(e){for(;;)switch(e.prev=e.next){case 0:n=zA(i),e.prev=1,n.s();case 3:if((o=n.n()).done){e.next=9;break}return r=fy(o.value,2),s=r[0],a=r[1],e.next=7,[s,t(a,s)];case 7:e.next=3;break;case 9:e.next=14;break;case 11:e.prev=11,e.t0=e.catch(1),n.e(e.t0);case 14:return e.prev=14,n.f(),e.finish(14);case 17:case"end":return e.stop()}}),e,null,[[1,11,14,17]])}))()})}},{key:"max",value:function(t){var e=LA(this._pairs),i=e.next();if(i.done)return null;for(var n=i.value[1],o=t(i.value[1],i.value[0]);!(i=e.next()).done;){var r=fy(i.value,2),s=r[0],a=r[1],d=t(a,s);d>o&&(o=d,n=a)}return n}},{key:"min",value:function(t){var e=LA(this._pairs),i=e.next();if(i.done)return null;for(var n=i.value[1],o=t(i.value[1],i.value[0]);!(i=e.next()).done;){var r=fy(i.value,2),s=r[0],a=r[1],d=t(a,s);d=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function VA(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i1?i-1:0),o=1;oo?1:no)&&(n=s,o=a)}}catch(t){r.e(t)}finally{r.f()}return n||null}},{key:"min",value:function(t){var e,i,n=null,o=null,r=WA(gA(e=this._data).call(e));try{for(r.s();!(i=r.n()).done;){var s=i.value,a=s[t];"number"==typeof a&&(null==o||anT.mark((function i(){var n,o,r,s;return nT.wrap((function(i){for(;;)switch(i.prev=i.next){case 0:n=WA(t),i.prev=1,n.s();case 3:if((o=n.n()).done){i.next=11;break}if(r=o.value,null==(s=e.get(r))){i.next=9;break}return i.next=9,[r,s];case 9:i.next=3;break;case 11:i.next=16;break;case 13:i.prev=13,i.t0=i.catch(1),n.e(i.t0);case 16:return i.prev=16,n.f(),i.finish(16);case 19:case"end":return i.stop()}}),i,null,[[1,13,16,19]])}))()})}var i;return new HA({[yA]:qm(i=SA(this._data)).call(i,this._data)})}}]),i}(NA);function YA(t,e){return"object"==typeof e&&null!==e&&t===e.idProp&&"function"==typeof Vy(e)&&"function"==typeof e.get&&"function"==typeof e.getDataSet&&"function"==typeof e.getIds&&"number"==typeof e.length&&"function"==typeof Ev(e)&&"function"==typeof e.off&&"function"==typeof e.on&&"function"==typeof e.stream&&function(t,e){return"object"==typeof e&&null!==e&&t===e.idProp&&"function"==typeof e.add&&"function"==typeof e.clear&&"function"==typeof e.distinct&&"function"==typeof Vy(e)&&"function"==typeof e.get&&"function"==typeof e.getDataSet&&"function"==typeof e.getIds&&"number"==typeof e.length&&"function"==typeof Ev(e)&&"function"==typeof e.max&&"function"==typeof e.min&&"function"==typeof e.off&&"function"==typeof e.on&&"function"==typeof e.remove&&"function"==typeof e.setOptions&&"function"==typeof e.stream&&"function"==typeof e.update&&"function"==typeof e.updateOnly}(t,e.getDataSet())} -/** - * vis-network - * https://visjs.github.io/vis-network/ - * - * A dynamic, browser-based visualization library. - * - * @version 9.1.9 - * @date 2023-11-03T01:42:27.418Z - * - * @copyright (c) 2011-2017 Almende B.V, http://almende.com - * @copyright (c) 2017-2019 visjs contributors, https://github.com/visjs - * - * @license - * vis.js is dual licensed under both - * - * 1. The Apache 2.0 License - * http://www.apache.org/licenses/LICENSE-2.0 - * - * and - * - * 2. The MIT License - * http://opensource.org/licenses/MIT - * - * vis.js may be distributed under either license. - */var GA="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function KA(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}var ZA=function(t){return t&&t.Math===Math&&t},QA=ZA("object"==typeof globalThis&&globalThis)||ZA("object"==typeof window&&window)||ZA("object"==typeof self&&self)||ZA("object"==typeof GA&&GA)||function(){return this}()||GA||Function("return this")(),JA=function(t){try{return!!t()}catch(t){return!0}},tR=!JA((function(){var t=function(){}.bind();return"function"!=typeof t||t.hasOwnProperty("prototype")})),eR=tR,iR=Function.prototype,nR=iR.apply,oR=iR.call,rR="object"==typeof Reflect&&Reflect.apply||(eR?oR.bind(nR):function(){return oR.apply(nR,arguments)}),sR=tR,aR=Function.prototype,dR=aR.call,lR=sR&&aR.bind.bind(dR,dR),cR=sR?lR:function(t){return function(){return dR.apply(t,arguments)}},hR=cR,uR=hR({}.toString),pR=hR("".slice),fR=function(t){return pR(uR(t),8,-1)},mR=fR,vR=cR,gR=function(t){if("Function"===mR(t))return vR(t)},yR="object"==typeof document&&document.all,bR={all:yR,IS_HTMLDDA:void 0===yR&&void 0!==yR},xR=bR.all,_R=bR.IS_HTMLDDA?function(t){return"function"==typeof t||t===xR}:function(t){return"function"==typeof t},wR={},ER=!JA((function(){return 7!==Object.defineProperty({},1,{get:function(){return 7}})[1]})),kR=tR,OR=Function.prototype.call,CR=kR?OR.bind(OR):function(){return OR.apply(OR,arguments)},SR={},TR={}.propertyIsEnumerable,IR=Object.getOwnPropertyDescriptor,AR=IR&&!TR.call({1:2},1);SR.f=AR?function(t){var e=IR(this,t);return!!e&&e.enumerable}:TR;var RR,PR,DR=function(t,e){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:e}},MR=JA,FR=fR,NR=Object,BR=cR("".split),LR=MR((function(){return!NR("z").propertyIsEnumerable(0)}))?function(t){return"String"===FR(t)?BR(t,""):NR(t)}:NR,zR=function(t){return null==t},jR=zR,HR=TypeError,$R=function(t){if(jR(t))throw new HR("Can't call method on "+t);return t},UR=LR,WR=$R,VR=function(t){return UR(WR(t))},qR=_R,XR=bR.all,YR=bR.IS_HTMLDDA?function(t){return"object"==typeof t?null!==t:qR(t)||t===XR}:function(t){return"object"==typeof t?null!==t:qR(t)},GR={},KR=GR,ZR=QA,QR=_R,JR=function(t){return QR(t)?t:void 0},tP=function(t,e){return arguments.length<2?JR(KR[t])||JR(ZR[t]):KR[t]&&KR[t][e]||ZR[t]&&ZR[t][e]},eP=cR({}.isPrototypeOf),iP="undefined"!=typeof navigator&&String(navigator.userAgent)||"",nP=QA,oP=iP,rP=nP.process,sP=nP.Deno,aP=rP&&rP.versions||sP&&sP.version,dP=aP&&aP.v8;dP&&(PR=(RR=dP.split("."))[0]>0&&RR[0]<4?1:+(RR[0]+RR[1])),!PR&&oP&&(!(RR=oP.match(/Edge\/(\d+)/))||RR[1]>=74)&&(RR=oP.match(/Chrome\/(\d+)/))&&(PR=+RR[1]);var lP=PR,cP=lP,hP=JA,uP=QA.String,pP=!!Object.getOwnPropertySymbols&&!hP((function(){var t=Symbol("symbol detection");return!uP(t)||!(Object(t)instanceof Symbol)||!Symbol.sham&&cP&&cP<41})),fP=pP&&!Symbol.sham&&"symbol"==typeof Symbol.iterator,mP=tP,vP=_R,gP=eP,yP=Object,bP=fP?function(t){return"symbol"==typeof t}:function(t){var e=mP("Symbol");return vP(e)&&gP(e.prototype,yP(t))},xP=String,_P=function(t){try{return xP(t)}catch(t){return"Object"}},wP=_R,EP=_P,kP=TypeError,OP=function(t){if(wP(t))return t;throw new kP(EP(t)+" is not a function")},CP=OP,SP=zR,TP=function(t,e){var i=t[e];return SP(i)?void 0:CP(i)},IP=CR,AP=_R,RP=YR,PP=TypeError,DP={exports:{}},MP=QA,FP=Object.defineProperty,NP=function(t,e){try{FP(MP,t,{value:e,configurable:!0,writable:!0})}catch(i){MP[t]=e}return e},BP="__core-js_shared__",LP=QA[BP]||NP(BP,{}),zP=LP;(DP.exports=function(t,e){return zP[t]||(zP[t]=void 0!==e?e:{})})("versions",[]).push({version:"3.33.0",mode:"pure",copyright:"© 2014-2023 Denis Pushkarev (zloirock.ru)",license:"https://github.com/zloirock/core-js/blob/v3.33.0/LICENSE",source:"https://github.com/zloirock/core-js"});var jP=DP.exports,HP=$R,$P=Object,UP=function(t){return $P(HP(t))},WP=UP,VP=cR({}.hasOwnProperty),qP=Object.hasOwn||function(t,e){return VP(WP(t),e)},XP=cR,YP=0,GP=Math.random(),KP=XP(1..toString),ZP=function(t){return"Symbol("+(void 0===t?"":t)+")_"+KP(++YP+GP,36)},QP=jP,JP=qP,tD=ZP,eD=pP,iD=fP,nD=QA.Symbol,oD=QP("wks"),rD=iD?nD.for||nD:nD&&nD.withoutSetter||tD,sD=function(t){return JP(oD,t)||(oD[t]=eD&&JP(nD,t)?nD[t]:rD("Symbol."+t)),oD[t]},aD=CR,dD=YR,lD=bP,cD=TP,hD=function(t,e){var i,n;if("string"===e&&AP(i=t.toString)&&!RP(n=IP(i,t)))return n;if(AP(i=t.valueOf)&&!RP(n=IP(i,t)))return n;if("string"!==e&&AP(i=t.toString)&&!RP(n=IP(i,t)))return n;throw new PP("Can't convert object to primitive value")},uD=TypeError,pD=sD("toPrimitive"),fD=function(t,e){if(!dD(t)||lD(t))return t;var i,n=cD(t,pD);if(n){if(void 0===e&&(e="default"),i=aD(n,t,e),!dD(i)||lD(i))return i;throw new uD("Can't convert object to primitive value")}return void 0===e&&(e="number"),hD(t,e)},mD=bP,vD=function(t){var e=fD(t,"string");return mD(e)?e:e+""},gD=YR,yD=QA.document,bD=gD(yD)&&gD(yD.createElement),xD=function(t){return bD?yD.createElement(t):{}},_D=xD,wD=!ER&&!JA((function(){return 7!==Object.defineProperty(_D("div"),"a",{get:function(){return 7}}).a})),ED=ER,kD=CR,OD=SR,CD=DR,SD=VR,TD=vD,ID=qP,AD=wD,RD=Object.getOwnPropertyDescriptor;wR.f=ED?RD:function(t,e){if(t=SD(t),e=TD(e),AD)try{return RD(t,e)}catch(t){}if(ID(t,e))return CD(!kD(OD.f,t,e),t[e])};var PD=JA,DD=_R,MD=/#|\.prototype\./,FD=function(t,e){var i=BD[ND(t)];return i===zD||i!==LD&&(DD(e)?PD(e):!!e)},ND=FD.normalize=function(t){return String(t).replace(MD,".").toLowerCase()},BD=FD.data={},LD=FD.NATIVE="N",zD=FD.POLYFILL="P",jD=FD,HD=OP,$D=tR,UD=gR(gR.bind),WD=function(t,e){return HD(t),void 0===e?t:$D?UD(t,e):function(){return t.apply(e,arguments)}},VD={},qD=ER&&JA((function(){return 42!==Object.defineProperty((function(){}),"prototype",{value:42,writable:!1}).prototype})),XD=YR,YD=String,GD=TypeError,KD=function(t){if(XD(t))return t;throw new GD(YD(t)+" is not an object")},ZD=ER,QD=wD,JD=qD,tM=KD,eM=vD,iM=TypeError,nM=Object.defineProperty,oM=Object.getOwnPropertyDescriptor,rM="enumerable",sM="configurable",aM="writable";VD.f=ZD?JD?function(t,e,i){if(tM(t),e=eM(e),tM(i),"function"==typeof t&&"prototype"===e&&"value"in i&&aM in i&&!i[aM]){var n=oM(t,e);n&&n[aM]&&(t[e]=i.value,i={configurable:sM in i?i[sM]:n[sM],enumerable:rM in i?i[rM]:n[rM],writable:!1})}return nM(t,e,i)}:nM:function(t,e,i){if(tM(t),e=eM(e),tM(i),QD)try{return nM(t,e,i)}catch(t){}if("get"in i||"set"in i)throw new iM("Accessors not supported");return"value"in i&&(t[e]=i.value),t};var dM=VD,lM=DR,cM=ER?function(t,e,i){return dM.f(t,e,lM(1,i))}:function(t,e,i){return t[e]=i,t},hM=QA,uM=rR,pM=gR,fM=_R,mM=wR.f,vM=jD,gM=GR,yM=WD,bM=cM,xM=qP,_M=function(t){var e=function(i,n,o){if(this instanceof e){switch(arguments.length){case 0:return new t;case 1:return new t(i);case 2:return new t(i,n)}return new t(i,n,o)}return uM(t,this,arguments)};return e.prototype=t.prototype,e},wM=function(t,e){var i,n,o,r,s,a,d,l,c,h=t.target,u=t.global,p=t.stat,f=t.proto,m=u?hM:p?hM[h]:(hM[h]||{}).prototype,v=u?gM:gM[h]||bM(gM,h,{})[h],g=v.prototype;for(r in e)n=!(i=vM(u?r:h+(p?".":"#")+r,t.forced))&&m&&xM(m,r),a=v[r],n&&(d=t.dontCallGetSet?(c=mM(m,r))&&c.value:m[r]),s=n&&d?d:e[r],n&&typeof a==typeof s||(l=t.bind&&n?yM(s,hM):t.wrap&&n?_M(s):f&&fM(s)?pM(s):s,(t.sham||s&&s.sham||a&&a.sham)&&bM(l,"sham",!0),bM(v,r,l),f&&(xM(gM,o=h+"Prototype")||bM(gM,o,{}),bM(gM[o],r,s),t.real&&g&&(i||!g[r])&&bM(g,r,s)))},EM=Math.ceil,kM=Math.floor,OM=Math.trunc||function(t){var e=+t;return(e>0?kM:EM)(e)},CM=OM,SM=function(t){var e=+t;return e!=e||0===e?0:CM(e)},TM=SM,IM=Math.max,AM=Math.min,RM=function(t,e){var i=TM(t);return i<0?IM(i+e,0):AM(i,e)},PM=SM,DM=Math.min,MM=function(t){return t>0?DM(PM(t),9007199254740991):0},FM=function(t){return MM(t.length)},NM=VR,BM=RM,LM=FM,zM=function(t){return function(e,i,n){var o,r=NM(e),s=LM(r),a=BM(n,s);if(t&&i!=i){for(;s>a;)if((o=r[a++])!=o)return!0}else for(;s>a;a++)if((t||a in r)&&r[a]===i)return t||a||0;return!t&&-1}},jM={includes:zM(!0),indexOf:zM(!1)},HM={},$M=qP,UM=VR,WM=jM.indexOf,VM=HM,qM=cR([].push),XM=function(t,e){var i,n=UM(t),o=0,r=[];for(i in n)!$M(VM,i)&&$M(n,i)&&qM(r,i);for(;e.length>o;)$M(n,i=e[o++])&&(~WM(r,i)||qM(r,i));return r},YM=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","toLocaleString","toString","valueOf"],GM=XM,KM=YM,ZM=Object.keys||function(t){return GM(t,KM)},QM={};QM.f=Object.getOwnPropertySymbols;var JM=ER,tF=cR,eF=CR,iF=JA,nF=ZM,oF=QM,rF=SR,sF=UP,aF=LR,dF=Object.assign,lF=Object.defineProperty,cF=tF([].concat),hF=!dF||iF((function(){if(JM&&1!==dF({b:1},dF(lF({},"a",{enumerable:!0,get:function(){lF(this,"b",{value:3,enumerable:!1})}}),{b:2})).b)return!0;var t={},e={},i=Symbol("assign detection"),n="abcdefghijklmnopqrst";return t[i]=7,n.split("").forEach((function(t){e[t]=t})),7!==dF({},t)[i]||nF(dF({},e)).join("")!==n}))?function(t,e){for(var i=sF(t),n=arguments.length,o=1,r=oF.f,s=rF.f;n>o;)for(var a,d=aF(arguments[o++]),l=r?cF(nF(d),r(d)):nF(d),c=l.length,h=0;c>h;)a=l[h++],JM&&!eF(s,d,a)||(i[a]=d[a]);return i}:dF,uF=hF;wM({target:"Object",stat:!0,arity:2,forced:Object.assign!==uF},{assign:uF});var pF=KA(GR.Object.assign),fF=cR([].slice),mF=cR,vF=OP,gF=YR,yF=qP,bF=fF,xF=tR,_F=Function,wF=mF([].concat),EF=mF([].join),kF={},OF=xF?_F.bind:function(t){var e=vF(this),i=e.prototype,n=bF(arguments,1),o=function(){var i=wF(n,bF(arguments));return this instanceof o?function(t,e,i){if(!yF(kF,e)){for(var n=[],o=0;o=.1;)(f=+r[h++%s])>c&&(f=c),p=Math.sqrt(f*f/(1+l*l)),e+=p=a<0?-p:p,i+=l*p,!0===u?t.lineTo(e,i):t.moveTo(e,i),c-=f,u=!u}var HF={circle:NF,dashedLine:jF,database:zF,diamond:function(t,e,i,n){t.beginPath(),t.lineTo(e,i+n),t.lineTo(e+n,i),t.lineTo(e,i-n),t.lineTo(e-n,i),t.closePath()},ellipse:LF,ellipse_vis:LF,hexagon:function(t,e,i,n){t.beginPath();var o=2*Math.PI/6;t.moveTo(e+n,i);for(var r=1;r<6;r++)t.lineTo(e+n*Math.cos(o*r),i+n*Math.sin(o*r));t.closePath()},roundRect:BF,square:function(t,e,i,n){t.beginPath(),t.rect(e-n,i-n,2*n,2*n),t.closePath()},star:function(t,e,i,n){t.beginPath(),i+=.1*(n*=.82);for(var o=0;o<10;o++){var r=o%2==0?1.3*n:.5*n;t.lineTo(e+r*Math.sin(2*o*Math.PI/10),i-r*Math.cos(2*o*Math.PI/10))}t.closePath()},triangle:function(t,e,i,n){t.beginPath(),i+=.275*(n*=1.15);var o=2*n,r=o/2,s=Math.sqrt(3)/6*o,a=Math.sqrt(o*o-r*r);t.moveTo(e,i-(a-s)),t.lineTo(e+r,i+s),t.lineTo(e-r,i+s),t.lineTo(e,i-(a-s)),t.closePath()},triangleDown:function(t,e,i,n){t.beginPath(),i-=.275*(n*=1.15);var o=2*n,r=o/2,s=Math.sqrt(3)/6*o,a=Math.sqrt(o*o-r*r);t.moveTo(e,i+(a-s)),t.lineTo(e+r,i-s),t.lineTo(e-r,i-s),t.lineTo(e,i+(a-s)),t.closePath()}};var $F={exports:{}};!function(t){function e(t){if(t)return function(t){for(var i in e.prototype)t[i]=e.prototype[i];return t}(t)}t.exports=e,e.prototype.on=e.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},e.prototype.once=function(t,e){function i(){this.off(t,i),e.apply(this,arguments)}return i.fn=e,this.on(t,i),this},e.prototype.off=e.prototype.removeListener=e.prototype.removeAllListeners=e.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i,n=this._callbacks["$"+t];if(!n)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var o=0;o=a?t?"":void 0:(n=cN(r,s))<55296||n>56319||s+1===a||(o=cN(r,s+1))<56320||o>57343?t?lN(r,s):n:t?hN(r,s,s+2):o-56320+(n-55296<<10)+65536}},pN={codeAt:uN(!1),charAt:uN(!0)},fN=_R,mN=QA.WeakMap,vN=fN(mN)&&/native code/.test(String(mN)),gN=ZP,yN=jP("keys"),bN=function(t){return yN[t]||(yN[t]=gN(t))},xN=vN,_N=QA,wN=YR,EN=cM,kN=qP,ON=LP,CN=bN,SN=HM,TN="Object already initialized",IN=_N.TypeError,AN=_N.WeakMap;if(xN||ON.state){var RN=ON.state||(ON.state=new AN);RN.get=RN.get,RN.has=RN.has,RN.set=RN.set,VF=function(t,e){if(RN.has(t))throw new IN(TN);return e.facade=t,RN.set(t,e),e},qF=function(t){return RN.get(t)||{}},XF=function(t){return RN.has(t)}}else{var PN=CN("state");SN[PN]=!0,VF=function(t,e){if(kN(t,PN))throw new IN(TN);return e.facade=t,EN(t,PN,e),e},qF=function(t){return kN(t,PN)?t[PN]:{}},XF=function(t){return kN(t,PN)}}var DN={set:VF,get:qF,has:XF,enforce:function(t){return XF(t)?qF(t):VF(t,{})},getterFor:function(t){return function(e){var i;if(!wN(e)||(i=qF(e)).type!==t)throw new IN("Incompatible receiver, "+t+" required");return i}}},MN=ER,FN=qP,NN=Function.prototype,BN=MN&&Object.getOwnPropertyDescriptor,LN=FN(NN,"name"),zN={EXISTS:LN,PROPER:LN&&"something"===function(){}.name,CONFIGURABLE:LN&&(!MN||MN&&BN(NN,"name").configurable)},jN={},HN=ER,$N=qD,UN=VD,WN=KD,VN=VR,qN=ZM;jN.f=HN&&!$N?Object.defineProperties:function(t,e){WN(t);for(var i,n=VN(e),o=qN(e),r=o.length,s=0;r>s;)UN.f(t,i=o[s++],n[i]);return t};var XN,YN=tP("document","documentElement"),GN=KD,KN=jN,ZN=YM,QN=HM,JN=YN,tB=xD,eB="prototype",iB="script",nB=bN("IE_PROTO"),oB=function(){},rB=function(t){return"<"+iB+">"+t+""},sB=function(t){t.write(rB("")),t.close();var e=t.parentWindow.Object;return t=null,e},aB=function(){try{XN=new ActiveXObject("htmlfile")}catch(t){}var t,e,i;aB="undefined"!=typeof document?document.domain&&XN?sB(XN):(e=tB("iframe"),i="java"+iB+":",e.style.display="none",JN.appendChild(e),e.src=String(i),(t=e.contentWindow.document).open(),t.write(rB("document.F=Object")),t.close(),t.F):sB(XN);for(var n=ZN.length;n--;)delete aB[eB][ZN[n]];return aB()};QN[nB]=!0;var dB,lB,cB,hB=Object.create||function(t,e){var i;return null!==t?(oB[eB]=GN(t),i=new oB,oB[eB]=null,i[nB]=t):i=aB(),void 0===e?i:KN.f(i,e)},uB=!JA((function(){function t(){}return t.prototype.constructor=null,Object.getPrototypeOf(new t)!==t.prototype})),pB=qP,fB=_R,mB=UP,vB=uB,gB=bN("IE_PROTO"),yB=Object,bB=yB.prototype,xB=vB?yB.getPrototypeOf:function(t){var e=mB(t);if(pB(e,gB))return e[gB];var i=e.constructor;return fB(i)&&e instanceof i?i.prototype:e instanceof yB?bB:null},_B=cM,wB=function(t,e,i,n){return n&&n.enumerable?t[e]=i:_B(t,e,i),t},EB=JA,kB=_R,OB=YR,CB=hB,SB=xB,TB=wB,IB=sD("iterator"),AB=!1;[].keys&&("next"in(cB=[].keys())?(lB=SB(SB(cB)))!==Object.prototype&&(dB=lB):AB=!0);var RB=!OB(dB)||EB((function(){var t={};return dB[IB].call(t)!==t}));kB((dB=RB?{}:CB(dB))[IB])||TB(dB,IB,(function(){return this}));var PB={IteratorPrototype:dB,BUGGY_SAFARI_ITERATORS:AB},DB=eN,MB=YF?{}.toString:function(){return"[object "+DB(this)+"]"},FB=YF,NB=VD.f,BB=cM,LB=qP,zB=MB,jB=sD("toStringTag"),HB=function(t,e,i,n){if(t){var o=i?t:t.prototype;LB(o,jB)||NB(o,jB,{configurable:!0,value:e}),n&&!FB&&BB(o,"toString",zB)}},$B={},UB=PB.IteratorPrototype,WB=hB,VB=DR,qB=HB,XB=$B,YB=function(){return this},GB=cR,KB=OP,ZB=_R,QB=String,JB=TypeError,tL=function(t,e,i){try{return GB(KB(Object.getOwnPropertyDescriptor(t,e)[i]))}catch(t){}},eL=KD,iL=function(t){if("object"==typeof t||ZB(t))return t;throw new JB("Can't set "+QB(t)+" as a prototype")},nL=Object.setPrototypeOf||("__proto__"in{}?function(){var t,e=!1,i={};try{(t=tL(Object.prototype,"__proto__","set"))(i,[]),e=i instanceof Array}catch(t){}return function(i,n){return eL(i),iL(n),e?t(i,n):i.__proto__=n,i}}():void 0),oL=wM,rL=CR,sL=function(t,e,i,n){var o=e+" Iterator";return t.prototype=WB(UB,{next:VB(+!n,i)}),qB(t,o,!1,!0),XB[o]=YB,t},aL=xB,dL=HB,lL=wB,cL=$B,hL=zN.PROPER,uL=PB.BUGGY_SAFARI_ITERATORS,pL=sD("iterator"),fL="keys",mL="values",vL="entries",gL=function(){return this},yL=function(t,e,i,n,o,r,s){sL(i,e,n);var a,d,l,c=function(t){if(t===o&&m)return m;if(!uL&&t&&t in p)return p[t];switch(t){case fL:case mL:case vL:return function(){return new i(this,t)}}return function(){return new i(this)}},h=e+" Iterator",u=!1,p=t.prototype,f=p[pL]||p["@@iterator"]||o&&p[o],m=!uL&&f||c(o),v="Array"===e&&p.entries||f;if(v&&(a=aL(v.call(new t)))!==Object.prototype&&a.next&&(dL(a,h,!0,!0),cL[h]=gL),hL&&o===mL&&f&&f.name!==mL&&(u=!0,m=function(){return rL(f,this)}),o)if(d={values:c(mL),keys:r?m:c(fL),entries:c(vL)},s)for(l in d)(uL||u||!(l in p))&&lL(p,l,d[l]);else oL({target:e,proto:!0,forced:uL||u},d);return s&&p[pL]!==m&&lL(p,pL,m,{name:o}),cL[e]=m,d},bL=function(t,e){return{value:t,done:e}},xL=pN.charAt,_L=oN,wL=DN,EL=yL,kL=bL,OL="String Iterator",CL=wL.set,SL=wL.getterFor(OL);EL(String,"String",(function(t){CL(this,{type:OL,string:_L(t),index:0})}),(function(){var t,e=SL(this),i=e.string,n=e.index;return n>=i.length?kL(void 0,!0):(t=xL(i,n),e.index+=t.length,kL(t,!1))}));var TL=CR,IL=KD,AL=TP,RL=function(t,e,i){var n,o;IL(t);try{if(!(n=AL(t,"return"))){if("throw"===e)throw i;return i}n=TL(n,t)}catch(t){o=!0,n=t}if("throw"===e)throw i;if(o)throw n;return IL(n),i},PL=KD,DL=RL,ML=$B,FL=sD("iterator"),NL=Array.prototype,BL=function(t){return void 0!==t&&(ML.Array===t||NL[FL]===t)},LL=_R,zL=LP,jL=cR(Function.toString);LL(zL.inspectSource)||(zL.inspectSource=function(t){return jL(t)});var HL=zL.inspectSource,$L=cR,UL=JA,WL=_R,VL=eN,qL=HL,XL=function(){},YL=[],GL=tP("Reflect","construct"),KL=/^\s*(?:class|function)\b/,ZL=$L(KL.exec),QL=!KL.test(XL),JL=function(t){if(!WL(t))return!1;try{return GL(XL,YL,t),!0}catch(t){return!1}},tz=function(t){if(!WL(t))return!1;switch(VL(t)){case"AsyncFunction":case"GeneratorFunction":case"AsyncGeneratorFunction":return!1}try{return QL||!!ZL(KL,qL(t))}catch(t){return!0}};tz.sham=!0;var ez=!GL||UL((function(){var t;return JL(JL.call)||!JL(Object)||!JL((function(){t=!0}))||t}))?tz:JL,iz=vD,nz=VD,oz=DR,rz=function(t,e,i){var n=iz(e);n in t?nz.f(t,n,oz(0,i)):t[n]=i},sz=eN,az=TP,dz=zR,lz=$B,cz=sD("iterator"),hz=function(t){if(!dz(t))return az(t,cz)||az(t,"@@iterator")||lz[sz(t)]},uz=CR,pz=OP,fz=KD,mz=_P,vz=hz,gz=TypeError,yz=function(t,e){var i=arguments.length<2?vz(t):e;if(pz(i))return fz(uz(i,t));throw new gz(mz(t)+" is not iterable")},bz=WD,xz=CR,_z=UP,wz=function(t,e,i,n){try{return n?e(PL(i)[0],i[1]):e(i)}catch(e){DL(t,"throw",e)}},Ez=BL,kz=ez,Oz=FM,Cz=rz,Sz=yz,Tz=hz,Iz=Array,Az=sD("iterator"),Rz=!1;try{var Pz=0,Dz={next:function(){return{done:!!Pz++}},return:function(){Rz=!0}};Dz[Az]=function(){return this},Array.from(Dz,(function(){throw 2}))}catch(t){}var Mz=function(t){var e=_z(t),i=kz(this),n=arguments.length,o=n>1?arguments[1]:void 0,r=void 0!==o;r&&(o=bz(o,n>2?arguments[2]:void 0));var s,a,d,l,c,h,u=Tz(e),p=0;if(!u||this===Iz&&Ez(u))for(s=Oz(e),a=i?new this(s):Iz(s);s>p;p++)h=r?o(e[p],p):e[p],Cz(a,p,h);else for(c=(l=Sz(e,u)).next,a=i?new this:[];!(d=xz(c,l)).done;p++)h=r?wz(l,o,[d.value,p],!0):d.value,Cz(a,p,h);return a.length=p,a},Fz=function(t,e){try{if(!e&&!Rz)return!1}catch(t){return!1}var i=!1;try{var n={};n[Az]=function(){return{next:function(){return{done:i=!0}}}},t(n)}catch(t){}return i};wM({target:"Array",stat:!0,forced:!Fz((function(t){Array.from(t)}))},{from:Mz});var Nz=GR.Array.from,Bz=KA(Nz),Lz=VR,zz=$B,jz=DN;VD.f;var Hz=yL,$z=bL,Uz="Array Iterator",Wz=jz.set,Vz=jz.getterFor(Uz);Hz(Array,"Array",(function(t,e){Wz(this,{type:Uz,target:Lz(t),index:0,kind:e})}),(function(){var t=Vz(this),e=t.target,i=t.kind,n=t.index++;if(!e||n>=e.length)return t.target=void 0,$z(void 0,!0);switch(i){case"keys":return $z(n,!1);case"values":return $z(e[n],!1)}return $z([n,e[n]],!1)}),"values"),zz.Arguments=zz.Array;var qz=hz,Xz={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0},Yz=QA,Gz=eN,Kz=cM,Zz=$B,Qz=sD("toStringTag");for(var Jz in Xz){var tj=Yz[Jz],ej=tj&&tj.prototype;ej&&Gz(ej)!==Qz&&Kz(ej,Qz,Jz),Zz[Jz]=Zz.Array}var ij=qz,nj=KA(ij),oj=KA(ij);function rj(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var sj={exports:{}},aj=wM,dj=ER,lj=VD.f;aj({target:"Object",stat:!0,forced:Object.defineProperty!==lj,sham:!dj},{defineProperty:lj});var cj=GR.Object,hj=sj.exports=function(t,e,i){return cj.defineProperty(t,e,i)};cj.defineProperty.sham&&(hj.sham=!0);var uj=sj.exports,pj=KA(uj),fj=fR,mj=Array.isArray||function(t){return"Array"===fj(t)},vj=TypeError,gj=function(t){if(t>9007199254740991)throw vj("Maximum allowed index exceeded");return t},yj=mj,bj=ez,xj=YR,_j=sD("species"),wj=Array,Ej=function(t){var e;return yj(t)&&(e=t.constructor,(bj(e)&&(e===wj||yj(e.prototype))||xj(e)&&null===(e=e[_j]))&&(e=void 0)),void 0===e?wj:e},kj=function(t,e){return new(Ej(t))(0===e?0:e)},Oj=JA,Cj=lP,Sj=sD("species"),Tj=function(t){return Cj>=51||!Oj((function(){var e=[];return(e.constructor={})[Sj]=function(){return{foo:1}},1!==e[t](Boolean).foo}))},Ij=wM,Aj=JA,Rj=mj,Pj=YR,Dj=UP,Mj=FM,Fj=gj,Nj=rz,Bj=kj,Lj=Tj,zj=lP,jj=sD("isConcatSpreadable"),Hj=zj>=51||!Aj((function(){var t=[];return t[jj]=!1,t.concat()[0]!==t})),$j=function(t){if(!Pj(t))return!1;var e=t[jj];return void 0!==e?!!e:Rj(t)};Ij({target:"Array",proto:!0,arity:1,forced:!Hj||!Lj("concat")},{concat:function(t){var e,i,n,o,r,s=Dj(this),a=Bj(s,0),d=0;for(e=-1,n=arguments.length;ey;y++)if((a||y in m)&&(p=v(u=m[y],y,f),t))if(e)x[y]=p;else if(p)switch(t){case 3:return!0;case 5:return u;case 6:return y;case 2:EH(x,u)}else switch(t){case 4:return!1;case 7:EH(x,u)}return r?-1:n||o?o:x}},OH={forEach:kH(0),map:kH(1),filter:kH(2),some:kH(3),every:kH(4),find:kH(5),findIndex:kH(6),filterReject:kH(7)},CH=wM,SH=QA,TH=CR,IH=cR,AH=ER,RH=pP,PH=JA,DH=qP,MH=eP,FH=KD,NH=VR,BH=vD,LH=oN,zH=DR,jH=hB,HH=ZM,$H=Uj,UH=qj,WH=QM,VH=wR,qH=VD,XH=jN,YH=SR,GH=wB,KH=rH,ZH=jP,QH=HM,JH=ZP,t$=sD,e$=sH,i$=uH,n$=gH,o$=HB,r$=DN,s$=OH.forEach,a$=bN("hidden"),d$="Symbol",l$="prototype",c$=r$.set,h$=r$.getterFor(d$),u$=Object[l$],p$=SH.Symbol,f$=p$&&p$[l$],m$=SH.RangeError,v$=SH.TypeError,g$=SH.QObject,y$=VH.f,b$=qH.f,x$=UH.f,_$=YH.f,w$=IH([].push),E$=ZH("symbols"),k$=ZH("op-symbols"),O$=ZH("wks"),C$=!g$||!g$[l$]||!g$[l$].findChild,S$=function(t,e,i){var n=y$(u$,e);n&&delete u$[e],b$(t,e,i),n&&t!==u$&&b$(u$,e,n)},T$=AH&&PH((function(){return 7!==jH(b$({},"a",{get:function(){return b$(this,"a",{value:7}).a}})).a}))?S$:b$,I$=function(t,e){var i=E$[t]=jH(f$);return c$(i,{type:d$,tag:t,description:e}),AH||(i.description=e),i},A$=function(t,e,i){t===u$&&A$(k$,e,i),FH(t);var n=BH(e);return FH(i),DH(E$,n)?(i.enumerable?(DH(t,a$)&&t[a$][n]&&(t[a$][n]=!1),i=jH(i,{enumerable:zH(0,!1)})):(DH(t,a$)||b$(t,a$,zH(1,{})),t[a$][n]=!0),T$(t,n,i)):b$(t,n,i)},R$=function(t,e){FH(t);var i=NH(e),n=HH(i).concat(F$(i));return s$(n,(function(e){AH&&!TH(P$,i,e)||A$(t,e,i[e])})),t},P$=function(t){var e=BH(t),i=TH(_$,this,e);return!(this===u$&&DH(E$,e)&&!DH(k$,e))&&(!(i||!DH(this,e)||!DH(E$,e)||DH(this,a$)&&this[a$][e])||i)},D$=function(t,e){var i=NH(t),n=BH(e);if(i!==u$||!DH(E$,n)||DH(k$,n)){var o=y$(i,n);return!o||!DH(E$,n)||DH(i,a$)&&i[a$][n]||(o.enumerable=!0),o}},M$=function(t){var e=x$(NH(t)),i=[];return s$(e,(function(t){DH(E$,t)||DH(QH,t)||w$(i,t)})),i},F$=function(t){var e=t===u$,i=x$(e?k$:NH(t)),n=[];return s$(i,(function(t){!DH(E$,t)||e&&!DH(u$,t)||w$(n,E$[t])})),n};RH||(p$=function(){if(MH(f$,this))throw new v$("Symbol is not a constructor");var t=arguments.length&&void 0!==arguments[0]?LH(arguments[0]):void 0,e=JH(t),i=function(t){this===u$&&TH(i,k$,t),DH(this,a$)&&DH(this[a$],e)&&(this[a$][e]=!1);var n=zH(1,t);try{T$(this,e,n)}catch(t){if(!(t instanceof m$))throw t;S$(this,e,n)}};return AH&&C$&&T$(u$,e,{configurable:!0,set:i}),I$(e,t)},GH(f$=p$[l$],"toString",(function(){return h$(this).tag})),GH(p$,"withoutSetter",(function(t){return I$(JH(t),t)})),YH.f=P$,qH.f=A$,XH.f=R$,VH.f=D$,$H.f=UH.f=M$,WH.f=F$,e$.f=function(t){return I$(t$(t),t)},AH&&KH(f$,"description",{configurable:!0,get:function(){return h$(this).description}})),CH({global:!0,constructor:!0,wrap:!0,forced:!RH,sham:!RH},{Symbol:p$}),s$(HH(O$),(function(t){i$(t)})),CH({target:d$,stat:!0,forced:!RH},{useSetter:function(){C$=!0},useSimple:function(){C$=!1}}),CH({target:"Object",stat:!0,forced:!RH,sham:!AH},{create:function(t,e){return void 0===e?jH(t):R$(jH(t),e)},defineProperty:A$,defineProperties:R$,getOwnPropertyDescriptor:D$}),CH({target:"Object",stat:!0,forced:!RH},{getOwnPropertyNames:M$}),n$(),o$(p$,d$),QH[a$]=!0;var N$=pP&&!!Symbol.for&&!!Symbol.keyFor,B$=wM,L$=tP,z$=qP,j$=oN,H$=jP,$$=N$,U$=H$("string-to-symbol-registry"),W$=H$("symbol-to-string-registry");B$({target:"Symbol",stat:!0,forced:!$$},{for:function(t){var e=j$(t);if(z$(U$,e))return U$[e];var i=L$("Symbol")(e);return U$[e]=i,W$[i]=e,i}});var V$=wM,q$=qP,X$=bP,Y$=_P,G$=N$,K$=jP("symbol-to-string-registry");V$({target:"Symbol",stat:!0,forced:!G$},{keyFor:function(t){if(!X$(t))throw new TypeError(Y$(t)+" is not a symbol");if(q$(K$,t))return K$[t]}});var Z$=mj,Q$=_R,J$=fR,tU=oN,eU=cR([].push),iU=wM,nU=tP,oU=rR,rU=CR,sU=cR,aU=JA,dU=_R,lU=bP,cU=fF,hU=function(t){if(Q$(t))return t;if(Z$(t)){for(var e=t.length,i=[],n=0;nt.length)&&(e=t.length);for(var i=0,n=new Array(e);i1?arguments[1]:void 0)}});var vV=TF("Array").map,gV=eP,yV=vV,bV=Array.prototype,xV=function(t){var e=t.map;return t===bV||gV(bV,t)&&e===bV.map?yV:e},_V=KA(xV),wV=UP,EV=ZM;wM({target:"Object",stat:!0,forced:JA((function(){EV(1)}))},{keys:function(t){return EV(wV(t))}});var kV=KA(GR.Object.keys),OV=wM,CV=Date,SV=cR(CV.prototype.getTime);OV({target:"Date",stat:!0},{now:function(){return SV(new CV)}});var TV=KA(GR.Date.now),IV=JA,AV=function(t,e){var i=[][t];return!!i&&IV((function(){i.call(null,e||function(){return 1},1)}))},RV=OH.forEach,PV=AV("forEach")?[].forEach:function(t){return RV(this,t,arguments.length>1?arguments[1]:void 0)};wM({target:"Array",proto:!0,forced:[].forEach!==PV},{forEach:PV});var DV=TF("Array").forEach,MV=eN,FV=qP,NV=eP,BV=DV,LV=Array.prototype,zV={DOMTokenList:!0,NodeList:!0},jV=function(t){var e=t.forEach;return t===LV||NV(LV,t)&&e===LV.forEach||FV(zV,MV(t))?BV:e},HV=KA(jV),$V=wM,UV=mj,WV=cR([].reverse),VV=[1,2];$V({target:"Array",proto:!0,forced:String(VV)===String(VV.reverse())},{reverse:function(){return UV(this)&&(this.length=this.length),WV(this)}});var qV=TF("Array").reverse,XV=eP,YV=qV,GV=Array.prototype,KV=function(t){var e=t.reverse;return t===GV||XV(GV,t)&&e===GV.reverse?YV:e},ZV=KA(KV),QV=_P,JV=TypeError,tq=function(t,e){if(!delete t[e])throw new JV("Cannot delete property "+QV(e)+" of "+QV(t))},eq=wM,iq=UP,nq=RM,oq=SM,rq=FM,sq=gW,aq=gj,dq=kj,lq=rz,cq=tq,hq=Tj("splice"),uq=Math.max,pq=Math.min;eq({target:"Array",proto:!0,forced:!hq},{splice:function(t,e){var i,n,o,r,s,a,d=iq(this),l=rq(d),c=nq(t,l),h=arguments.length;for(0===h?i=n=0:1===h?(i=0,n=l-c):(i=h-2,n=pq(uq(oq(e),0),l-c)),aq(l+i-n),o=dq(d,n),r=0;rl-n+i;r--)cq(d,r-1)}else if(i>n)for(r=l-n;r>c;r--)a=r+i-1,(s=r+n-1)in d?d[a]=d[s]:cq(d,a);for(r=0;r1?arguments[1]:void 0)}});var _q=TF("Array").includes,wq=YR,Eq=fR,kq=sD("match"),Oq=function(t){var e;return wq(t)&&(void 0!==(e=t[kq])?!!e:"RegExp"===Eq(t))},Cq=TypeError,Sq=sD("match"),Tq=wM,Iq=function(t){if(Oq(t))throw new Cq("The method doesn't accept regular expressions");return t},Aq=$R,Rq=oN,Pq=function(t){var e=/./;try{"/./"[t](e)}catch(i){try{return e[Sq]=!1,"/./"[t](e)}catch(t){}}return!1},Dq=cR("".indexOf);Tq({target:"String",proto:!0,forced:!Pq("includes")},{includes:function(t){return!!~Dq(Rq(Aq(this)),Rq(Iq(t)),arguments.length>1?arguments[1]:void 0)}});var Mq=TF("String").includes,Fq=eP,Nq=_q,Bq=Mq,Lq=Array.prototype,zq=String.prototype,jq=function(t){var e=t.includes;return t===Lq||Fq(Lq,t)&&e===Lq.includes?Nq:"string"==typeof t||t===zq||Fq(zq,t)&&e===zq.includes?Bq:e},Hq=KA(jq),$q=UP,Uq=xB,Wq=uB;wM({target:"Object",stat:!0,forced:JA((function(){Uq(1)})),sham:!Wq},{getPrototypeOf:function(t){return Uq($q(t))}});var Vq=GR.Object.getPrototypeOf,qq=KA(Vq),Xq=OH.filter;wM({target:"Array",proto:!0,forced:!Tj("filter")},{filter:function(t){return Xq(this,t,arguments.length>1?arguments[1]:void 0)}});var Yq=TF("Array").filter,Gq=eP,Kq=Yq,Zq=Array.prototype,Qq=function(t){var e=t.filter;return t===Zq||Gq(Zq,t)&&e===Zq.filter?Kq:e},Jq=KA(Qq),tX="\t\n\v\f\r                 \u2028\u2029\ufeff",eX=$R,iX=oN,nX=tX,oX=cR("".replace),rX=RegExp("^["+nX+"]+"),sX=RegExp("(^|[^"+nX+"])["+nX+"]+$"),aX=function(t){return function(e){var i=iX(eX(e));return 1&t&&(i=oX(i,rX,"")),2&t&&(i=oX(i,sX,"$1")),i}},dX={start:aX(1),end:aX(2),trim:aX(3)},lX=QA,cX=JA,hX=cR,uX=oN,pX=dX.trim,fX=tX,mX=lX.parseInt,vX=lX.Symbol,gX=vX&&vX.iterator,yX=/^[+-]?0x/i,bX=hX(yX.exec),xX=8!==mX(fX+"08")||22!==mX(fX+"0x16")||gX&&!cX((function(){mX(Object(gX))}))?function(t,e){var i=pX(uX(t));return mX(i,e>>>0||(bX(yX,i)?16:10))}:mX;wM({global:!0,forced:parseInt!==xX},{parseInt:xX});var _X=KA(GR.parseInt),wX=wM,EX=jM.indexOf,kX=AV,OX=gR([].indexOf),CX=!!OX&&1/OX([1],1,-0)<0;wX({target:"Array",proto:!0,forced:CX||!kX("indexOf")},{indexOf:function(t){var e=arguments.length>1?arguments[1]:void 0;return CX?OX(this,t,e)||0:EX(this,t,e)}});var SX=TF("Array").indexOf,TX=eP,IX=SX,AX=Array.prototype,RX=function(t){var e=t.indexOf;return t===AX||TX(AX,t)&&e===AX.indexOf?IX:e},PX=KA(RX);wM({target:"Object",stat:!0,sham:!ER},{create:hB});var DX=GR.Object,MX=function(t,e){return DX.create(t,e)},FX=MX,NX=KA(FX),BX=GR,LX=rR;BX.JSON||(BX.JSON={stringify:JSON.stringify});var zX=function(t,e,i){return LX(BX.JSON.stringify,null,arguments)},jX=KA(zX),HX="function"==typeof Bun&&Bun&&"string"==typeof Bun.version,$X=TypeError,UX=QA,WX=rR,VX=_R,qX=HX,XX=iP,YX=fF,GX=function(t,e){if(ti,s=VX(n)?n:KX(n),a=r?YX(arguments,i):[],d=r?function(){WX(s,this,a)}:s;return e?t(d,o):t(d)}:t},JX=wM,tY=QA,eY=QX(tY.setInterval,!0);JX({global:!0,bind:!0,forced:tY.setInterval!==eY},{setInterval:eY});var iY=wM,nY=QA,oY=QX(nY.setTimeout,!0);iY({global:!0,bind:!0,forced:nY.setTimeout!==oY},{setTimeout:oY});var rY=KA(GR.setTimeout),sY=UP,aY=RM,dY=FM,lY=function(t){for(var e=sY(this),i=dY(e),n=arguments.length,o=aY(n>1?arguments[1]:void 0,i),r=n>2?arguments[2]:void 0,s=void 0===r?i:aY(r,i);s>o;)e[o++]=t;return e};wM({target:"Array",proto:!0},{fill:lY});var cY,hY=TF("Array").fill,uY=eP,pY=hY,fY=Array.prototype,mY=function(t){var e=t.fill;return t===fY||uY(fY,t)&&e===fY.fill?pY:e},vY=KA(mY); -/*! Hammer.JS - v2.0.17-rc - 2019-12-16 - * http://naver.github.io/egjs - * - * Forked By Naver egjs - * Copyright (c) hammerjs - * Licensed under the MIT license */ -function gY(){return gY=Object.assign||function(t){for(var e=1;e-1}var rG=function(){function t(t,e){this.manager=t,this.set(e)}var e=t.prototype;return e.set=function(t){t===AY&&(t=this.compute()),IY&&this.manager.element.style&&NY[t]&&(this.manager.element.style[TY]=t),this.actions=t.toLowerCase().trim()},e.update=function(){this.set(this.manager.options.touchAction)},e.compute=function(){var t=[];return iG(this.manager.recognizers,(function(e){nG(e.options.enable,[e])&&(t=t.concat(e.getTouchAction()))})),function(t){if(oG(t,DY))return DY;var e=oG(t,MY),i=oG(t,FY);return e&&i?DY:e||i?e?MY:FY:oG(t,PY)?PY:RY}(t.join(" "))},e.preventDefaults=function(t){var e=t.srcEvent,i=t.offsetDirection;if(this.manager.session.prevented)e.preventDefault();else{var n=this.actions,o=oG(n,DY)&&!NY[DY],r=oG(n,FY)&&!NY[FY],s=oG(n,MY)&&!NY[MY];if(o){var a=1===t.pointers.length,d=t.distance<2,l=t.deltaTime<250;if(a&&d&&l)return}if(!s||!r)return o||r&&i&ZY||s&&i&QY?this.preventSrc(e):void 0}},e.preventSrc=function(t){this.manager.session.prevented=!0,t.preventDefault()},t}();function sG(t,e){for(;t;){if(t===e)return!0;t=t.parentNode}return!1}function aG(t){var e=t.length;if(1===e)return{x:kY(t[0].clientX),y:kY(t[0].clientY)};for(var i=0,n=0,o=0;o=OY(e)?t<0?XY:YY:e<0?GY:KY}function uG(t,e,i){return{x:e/t||0,y:i/t||0}}function pG(t,e){var i=t.session,n=e.pointers,o=n.length;i.firstInput||(i.firstInput=dG(e)),o>1&&!i.firstMultiple?i.firstMultiple=dG(e):1===o&&(i.firstMultiple=!1);var r=i.firstInput,s=i.firstMultiple,a=s?s.center:r.center,d=e.center=aG(n);e.timeStamp=CY(),e.deltaTime=e.timeStamp-r.timeStamp,e.angle=cG(a,d),e.distance=lG(a,d),function(t,e){var i=e.center,n=t.offsetDelta||{},o=t.prevDelta||{},r=t.prevInput||{};e.eventType!==UY&&r.eventType!==WY||(o=t.prevDelta={x:r.deltaX||0,y:r.deltaY||0},n=t.offsetDelta={x:i.x,y:i.y}),e.deltaX=o.x+(i.x-n.x),e.deltaY=o.y+(i.y-n.y)}(i,e),e.offsetDirection=hG(e.deltaX,e.deltaY);var l,c,h=uG(e.deltaTime,e.deltaX,e.deltaY);e.overallVelocityX=h.x,e.overallVelocityY=h.y,e.overallVelocity=OY(h.x)>OY(h.y)?h.x:h.y,e.scale=s?(l=s.pointers,lG((c=n)[0],c[1],eG)/lG(l[0],l[1],eG)):1,e.rotation=s?function(t,e){return cG(e[1],e[0],eG)+cG(t[1],t[0],eG)}(s.pointers,n):0,e.maxPointers=i.prevInput?e.pointers.length>i.prevInput.maxPointers?e.pointers.length:i.prevInput.maxPointers:e.pointers.length,function(t,e){var i,n,o,r,s=t.lastInterval||e,a=e.timeStamp-s.timeStamp;if(e.eventType!==VY&&(a>$Y||void 0===s.velocity)){var d=e.deltaX-s.deltaX,l=e.deltaY-s.deltaY,c=uG(a,d,l);n=c.x,o=c.y,i=OY(c.x)>OY(c.y)?c.x:c.y,r=hG(d,l),t.lastInterval=e}else i=s.velocity,n=s.velocityX,o=s.velocityY,r=s.direction;e.velocity=i,e.velocityX=n,e.velocityY=o,e.direction=r}(i,e);var u,p=t.element,f=e.srcEvent;sG(u=f.composedPath?f.composedPath()[0]:f.path?f.path[0]:f.target,p)&&(p=u),e.target=p}function fG(t,e,i){var n=i.pointers.length,o=i.changedPointers.length,r=e&UY&&n-o==0,s=e&(WY|VY)&&n-o==0;i.isFirst=!!r,i.isFinal=!!s,r&&(t.session={}),i.eventType=e,pG(t,i),t.emit("hammer.input",i),t.recognize(i),t.session.prevInput=i}function mG(t){return t.trim().split(/\s+/g)}function vG(t,e,i){iG(mG(e),(function(e){t.addEventListener(e,i,!1)}))}function gG(t,e,i){iG(mG(e),(function(e){t.removeEventListener(e,i,!1)}))}function yG(t){var e=t.ownerDocument||t;return e.defaultView||e.parentWindow||window}var bG=function(){function t(t,e){var i=this;this.manager=t,this.callback=e,this.element=t.element,this.target=t.options.inputTarget,this.domHandler=function(e){nG(t.options.enable,[t])&&i.handler(e)},this.init()}var e=t.prototype;return e.handler=function(){},e.init=function(){this.evEl&&vG(this.element,this.evEl,this.domHandler),this.evTarget&&vG(this.target,this.evTarget,this.domHandler),this.evWin&&vG(yG(this.element),this.evWin,this.domHandler)},e.destroy=function(){this.evEl&&gG(this.element,this.evEl,this.domHandler),this.evTarget&&gG(this.target,this.evTarget,this.domHandler),this.evWin&&gG(yG(this.element),this.evWin,this.domHandler)},t}();function xG(t,e,i){if(t.indexOf&&!i)return t.indexOf(e);for(var n=0;ni[e]})):n.sort()),n}var TG={touchstart:UY,touchmove:2,touchend:WY,touchcancel:VY},IG=function(t){function e(){var i;return e.prototype.evTarget="touchstart touchmove touchend touchcancel",(i=t.apply(this,arguments)||this).targetIds={},i}return yY(e,t),e.prototype.handler=function(t){var e=TG[t.type],i=AG.call(this,t,e);i&&this.callback(this.manager,e,{pointers:i[0],changedPointers:i[1],pointerType:jY,srcEvent:t})},e}(bG);function AG(t,e){var i,n,o=CG(t.touches),r=this.targetIds;if(e&(2|UY)&&1===o.length)return r[o[0].identifier]=!0,[o,o];var s=CG(t.changedTouches),a=[],d=this.target;if(n=o.filter((function(t){return sG(t.target,d)})),e===UY)for(i=0;i-1&&n.splice(t,1)}),DG)}}function FG(t,e){t&UY?(this.primaryTouch=e.changedPointers[0].identifier,MG.call(this,e)):t&(WY|VY)&&MG.call(this,e)}function NG(t){for(var e=t.srcEvent.clientX,i=t.srcEvent.clientY,n=0;n-1&&this.requireFail.splice(e,1),this},e.hasRequireFailures=function(){return this.requireFail.length>0},e.canRecognizeWith=function(t){return!!this.simultaneous[t.id]},e.emit=function(t){var e=this,i=this.state;function n(i){e.manager.emit(i,t)}i<8&&n(e.options.event+$G(i)),n(e.options.event),t.additionalEvent&&n(t.additionalEvent),i>=8&&n(e.options.event+$G(i))},e.tryEmit=function(t){if(this.canEmit())return this.emit(t);this.state=zG},e.canEmit=function(){for(var t=0;te.threshold&&o&e.direction},i.attrTest=function(t){return VG.prototype.attrTest.call(this,t)&&(2&this.state||!(2&this.state)&&this.directionTest(t))},i.emit=function(e){this.pX=e.deltaX,this.pY=e.deltaY;var i=qG(e.direction);i&&(e.additionalEvent=this.options.event+i),t.prototype.emit.call(this,e)},e}(VG),YG=function(t){function e(e){return void 0===e&&(e={}),t.call(this,gY({event:"swipe",threshold:10,velocity:.3,direction:ZY|QY,pointers:1},e))||this}yY(e,t);var i=e.prototype;return i.getTouchAction=function(){return XG.prototype.getTouchAction.call(this)},i.attrTest=function(e){var i,n=this.options.direction;return n&(ZY|QY)?i=e.overallVelocity:n&ZY?i=e.overallVelocityX:n&QY&&(i=e.overallVelocityY),t.prototype.attrTest.call(this,e)&&n&e.offsetDirection&&e.distance>this.options.threshold&&e.maxPointers===this.options.pointers&&OY(i)>this.options.velocity&&e.eventType&WY},i.emit=function(t){var e=qG(t.offsetDirection);e&&this.manager.emit(this.options.event+e,t),this.manager.emit(this.options.event,t)},e}(VG),GG=function(t){function e(e){return void 0===e&&(e={}),t.call(this,gY({event:"pinch",threshold:0,pointers:2},e))||this}yY(e,t);var i=e.prototype;return i.getTouchAction=function(){return[DY]},i.attrTest=function(e){return t.prototype.attrTest.call(this,e)&&(Math.abs(e.scale-1)>this.options.threshold||2&this.state)},i.emit=function(e){if(1!==e.scale){var i=e.scale<1?"in":"out";e.additionalEvent=this.options.event+i}t.prototype.emit.call(this,e)},e}(VG),KG=function(t){function e(e){return void 0===e&&(e={}),t.call(this,gY({event:"rotate",threshold:0,pointers:2},e))||this}yY(e,t);var i=e.prototype;return i.getTouchAction=function(){return[DY]},i.attrTest=function(e){return t.prototype.attrTest.call(this,e)&&(Math.abs(e.rotation)>this.options.threshold||2&this.state)},e}(VG),ZG=function(t){function e(e){var i;return void 0===e&&(e={}),(i=t.call(this,gY({event:"press",pointers:1,time:251,threshold:9},e))||this)._timer=null,i._input=null,i}yY(e,t);var i=e.prototype;return i.getTouchAction=function(){return[RY]},i.process=function(t){var e=this,i=this.options,n=t.pointers.length===i.pointers,o=t.distancei.time;if(this._input=t,!o||!n||t.eventType&(WY|VY)&&!r)this.reset();else if(t.eventType&UY)this.reset(),this._timer=setTimeout((function(){e.state=8,e.tryEmit()}),i.time);else if(t.eventType&WY)return 8;return zG},i.reset=function(){clearTimeout(this._timer)},i.emit=function(t){8===this.state&&(t&&t.eventType&WY?this.manager.emit(this.options.event+"up",t):(this._input.timeStamp=CY(),this.manager.emit(this.options.event,this._input)))},e}(UG),QG={domEvents:!1,touchAction:AY,enable:!0,inputTarget:null,inputClass:null,cssProps:{userSelect:"none",touchSelect:"none",touchCallout:"none",contentZooming:"none",userDrag:"none",tapHighlightColor:"rgba(0,0,0,0)"}},JG=[[KG,{enable:!1}],[GG,{enable:!1},["rotate"]],[YG,{direction:ZY}],[XG,{direction:ZY},["swipe"]],[WG],[WG,{event:"doubletap",taps:2},["tap"]],[ZG]];function tK(t,e){var i,n=t.element;n.style&&(iG(t.options.cssProps,(function(o,r){i=SY(n.style,r),e?(t.oldCssProps[i]=n.style[i],n.style[i]=o):n.style[i]=t.oldCssProps[i]||""})),e||(t.oldCssProps={}))}var eK=function(){function t(t,e){var i,n=this;this.options=_Y({},QG,e||{}),this.options.inputTarget=this.options.inputTarget||t,this.handlers={},this.session={},this.recognizers=[],this.oldCssProps={},this.element=t,this.input=new((i=this).options.inputClass||(LY?OG:zY?IG:BY?BG:PG))(i,fG),this.touchAction=new rG(this,this.options.touchAction),tK(this,!0),iG(this.options.recognizers,(function(t){var e=n.add(new t[0](t[1]));t[2]&&e.recognizeWith(t[2]),t[3]&&e.requireFailure(t[3])}),this)}var e=t.prototype;return e.set=function(t){return _Y(this.options,t),t.touchAction&&this.touchAction.update(),t.inputTarget&&(this.input.destroy(),this.input.target=t.inputTarget,this.input.init()),this},e.stop=function(t){this.session.stopped=t?2:1},e.recognize=function(t){var e=this.session;if(!e.stopped){var i;this.touchAction.preventDefaults(t);var n=this.recognizers,o=e.curRecognizer;(!o||o&&8&o.state)&&(e.curRecognizer=null,o=null);for(var r=0;r\s*\(/gm,"{anonymous}()@"):"Unknown Stack Trace",o=window.console&&(window.console.warn||window.console.log);return o&&o.call(window.console,n,i),t.apply(this,arguments)}}var sK=rK((function(t,e,i){for(var n=Object.keys(e),o=0;o=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function pK(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i>>0,t=(o*=t)>>>0,t+=4294967296*(o-=t)}return 2.3283064365386963e-10*(t>>>0)}}(),e=t(" "),i=t(" "),n=t(" "),o=0;o2&&void 0!==arguments[2]&&arguments[2];for(var n in t)if(void 0!==e[n])if(null===e[n]||"object"!==sW(e[n]))kK(t,e,n,i);else{var o=t[n],r=e[n];EK(o)&&EK(r)&&OK(o,r,i)}}function CK(t,e,i){var n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];if(fV(i))throw new TypeError("Arrays are not supported by deepExtend");for(var o=0;o3&&void 0!==arguments[3]&&arguments[3];if(fV(i))throw new TypeError("Arrays are not supported by deepExtend");for(var o in i)if(Object.prototype.hasOwnProperty.call(i,o)&&!Hq(t).call(t,o))if(i[o]&&i[o].constructor===Object)void 0===e[o]&&(e[o]={}),e[o].constructor===Object?TK(e[o],i[o]):kK(e,i,o,n);else if(fV(i[o])){e[o]=[];for(var r=0;r2&&void 0!==arguments[2]&&arguments[2],n=arguments.length>3&&void 0!==arguments[3]&&arguments[3];for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)||!0===i)if("object"===sW(e[o])&&null!==e[o]&&qq(e[o])===Object.prototype)void 0===t[o]?t[o]=TK({},e[o],i):"object"===sW(t[o])&&null!==t[o]&&qq(t[o])===Object.prototype?TK(t[o],e[o],i):kK(t,e,o,n);else if(fV(e[o])){var r;t[o]=aV(r=e[o]).call(r)}else kK(t,e,o,n);return t}function IK(t,e){var i;return sV(i=[]).call(i,JW(t),[e])}function AK(t){return t.getBoundingClientRect().top}function RK(t,e){if(fV(t))for(var i=t.length,n=0;n3&&void 0!==arguments[3]?arguments[3]:{},o=function(t){return null!=t},r=function(t){return null!==t&&"object"===sW(t)};if(!r(t))throw new Error("Parameter mergeTarget must be an object");if(!r(e))throw new Error("Parameter options must be an object");if(!o(i))throw new Error("Parameter option must have a value");if(!r(n))throw new Error("Parameter globalOptions must be an object");var s=e[i],a=r(n)&&!function(t){for(var e in t)if(Object.prototype.hasOwnProperty.call(t,e))return!1;return!0}(n),d=a?n[i]:void 0,l=d?d.enabled:void 0;if(void 0!==s){if("boolean"==typeof s)return r(t[i])||(t[i]={}),void(t[i].enabled=s);if(null===s&&!r(t[i])){if(!o(d))return;t[i]=NX(d)}if(r(s)){var c=!0;void 0!==s.enabled?c=s.enabled:void 0!==l&&(c=d.enabled),function(t,e,i){r(t[i])||(t[i]={});var n=e[i],o=t[i];for(var s in n)Object.prototype.hasOwnProperty.call(n,s)&&(o[s]=n[s])}(t,e,i),t[i].enabled=c}}}var UK={linear:function(t){return t},easeInQuad:function(t){return t*t},easeOutQuad:function(t){return t*(2-t)},easeInOutQuad:function(t){return t<.5?2*t*t:(4-2*t)*t-1},easeInCubic:function(t){return t*t*t},easeOutCubic:function(t){return--t*t*t+1},easeInOutCubic:function(t){return t<.5?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1},easeInQuart:function(t){return t*t*t*t},easeOutQuart:function(t){return 1- --t*t*t*t},easeInOutQuart:function(t){return t<.5?8*t*t*t*t:1-8*--t*t*t*t},easeInQuint:function(t){return t*t*t*t*t},easeOutQuint:function(t){return 1+--t*t*t*t*t},easeInOutQuint:function(t){return t<.5?16*t*t*t*t*t:1+16*--t*t*t*t*t}};function WK(t,e){var i;fV(e)||(e=[e]);var n,o=uK(t);try{for(o.s();!(n=o.n()).done;){var r=n.value;if(r){i=r[e[0]];for(var s=1;s0&&void 0!==arguments[0]?arguments[0]:1;rj(this,t),this.pixelRatio=e,this.generated=!1,this.centerCoordinates={x:144.5,y:144.5},this.r=289*.49,this.color={r:255,g:255,b:255,a:1},this.hueCircle=void 0,this.initialColor={r:255,g:255,b:255,a:1},this.previousColor=void 0,this.applied=!1,this.updateCallback=function(){},this.closeCallback=function(){},this._create()}return cW(t,[{key:"insertTo",value:function(t){void 0!==this.hammer&&(this.hammer.destroy(),this.hammer=void 0),this.container=t,this.container.appendChild(this.frame),this._bindHammer(),this._setSize()}},{key:"setUpdateCallback",value:function(t){if("function"!=typeof t)throw new Error("Function attempted to set as colorPicker update callback is not a function.");this.updateCallback=t}},{key:"setCloseCallback",value:function(t){if("function"!=typeof t)throw new Error("Function attempted to set as colorPicker closing callback is not a function.");this.closeCallback=t}},{key:"_isColorString",value:function(t){if("string"==typeof t)return VK[t]}},{key:"setColor",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if("none"!==t){var i,n=this._isColorString(t);if(void 0!==n&&(t=n),!0===wK(t)){if(!0===jK(t)){var o=t.substr(4).substr(0,t.length-5).split(",");i={r:o[0],g:o[1],b:o[2],a:1}}else if(!0===function(t){return xK.test(t)}(t)){var r=t.substr(5).substr(0,t.length-6).split(",");i={r:r[0],g:r[1],b:r[2],a:r[3]}}else if(!0===zK(t)){var s=PK(t);i={r:s.r,g:s.g,b:s.b,a:1}}}else if(t instanceof Object&&void 0!==t.r&&void 0!==t.g&&void 0!==t.b){var a=void 0!==t.a?t.a:"1.0";i={r:t.r,g:t.g,b:t.b,a:a}}if(void 0===i)throw new Error("Unknown color passed to the colorPicker. Supported are strings: rgb, hex, rgba. Object: rgb ({r:r,g:g,b:b,[a:a]}). Supplied: "+jX(t));this._setColor(i,e)}}},{key:"show",value:function(){void 0!==this.closeCallback&&(this.closeCallback(),this.closeCallback=void 0),this.applied=!1,this.frame.style.display="block",this._generateHueCircle()}},{key:"_hide",value:function(){var t=this;!0===(!(arguments.length>0&&void 0!==arguments[0])||arguments[0])&&(this.previousColor=pF({},this.color)),!0===this.applied&&this.updateCallback(this.initialColor),this.frame.style.display="none",rY((function(){void 0!==t.closeCallback&&(t.closeCallback(),t.closeCallback=void 0)}),0)}},{key:"_save",value:function(){this.updateCallback(this.color),this.applied=!1,this._hide()}},{key:"_apply",value:function(){this.applied=!0,this.updateCallback(this.color),this._updatePicker(this.color)}},{key:"_loadLast",value:function(){void 0!==this.previousColor?this.setColor(this.previousColor,!1):alert("There is no last color to load...")}},{key:"_setColor",value:function(t){!0===(!(arguments.length>1&&void 0!==arguments[1])||arguments[1])&&(this.initialColor=pF({},t)),this.color=t;var e=NK(t.r,t.g,t.b),i=2*Math.PI,n=this.r*e.s,o=this.centerCoordinates.x+n*Math.sin(i*e.h),r=this.centerCoordinates.y+n*Math.cos(i*e.h);this.colorPickerSelector.style.left=o-.5*this.colorPickerSelector.clientWidth+"px",this.colorPickerSelector.style.top=r-.5*this.colorPickerSelector.clientHeight+"px",this._updatePicker(t)}},{key:"_setOpacity",value:function(t){this.color.a=t/100,this._updatePicker(this.color)}},{key:"_setBrightness",value:function(t){var e=NK(this.color.r,this.color.g,this.color.b);e.v=t/100;var i=BK(e.h,e.s,e.v);i.a=this.color.a,this.color=i,this._updatePicker()}},{key:"_updatePicker",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.color,e=NK(t.r,t.g,t.b),i=this.colorPickerCanvas.getContext("2d");void 0===this.pixelRation&&(this.pixelRatio=(window.devicePixelRatio||1)/(i.webkitBackingStorePixelRatio||i.mozBackingStorePixelRatio||i.msBackingStorePixelRatio||i.oBackingStorePixelRatio||i.backingStorePixelRatio||1)),i.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);var n=this.colorPickerCanvas.clientWidth,o=this.colorPickerCanvas.clientHeight;i.clearRect(0,0,n,o),i.putImageData(this.hueCircle,0,0),i.fillStyle="rgba(0,0,0,"+(1-e.v)+")",i.circle(this.centerCoordinates.x,this.centerCoordinates.y,this.r),vY(i).call(i),this.brightnessRange.value=100*e.v,this.opacityRange.value=100*t.a,this.initialColorDiv.style.backgroundColor="rgba("+this.initialColor.r+","+this.initialColor.g+","+this.initialColor.b+","+this.initialColor.a+")",this.newColorDiv.style.backgroundColor="rgba("+this.color.r+","+this.color.g+","+this.color.b+","+this.color.a+")"}},{key:"_setSize",value:function(){this.colorPickerCanvas.style.width="100%",this.colorPickerCanvas.style.height="100%",this.colorPickerCanvas.width=289*this.pixelRatio,this.colorPickerCanvas.height=289*this.pixelRatio}},{key:"_create",value:function(){var t,e,i,n;if(this.frame=document.createElement("div"),this.frame.className="vis-color-picker",this.colorPickerDiv=document.createElement("div"),this.colorPickerSelector=document.createElement("div"),this.colorPickerSelector.className="vis-selector",this.colorPickerDiv.appendChild(this.colorPickerSelector),this.colorPickerCanvas=document.createElement("canvas"),this.colorPickerDiv.appendChild(this.colorPickerCanvas),this.colorPickerCanvas.getContext){var o=this.colorPickerCanvas.getContext("2d");this.pixelRatio=(window.devicePixelRatio||1)/(o.webkitBackingStorePixelRatio||o.mozBackingStorePixelRatio||o.msBackingStorePixelRatio||o.oBackingStorePixelRatio||o.backingStorePixelRatio||1),this.colorPickerCanvas.getContext("2d").setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}else{var r=document.createElement("DIV");r.style.color="red",r.style.fontWeight="bold",r.style.padding="10px",r.innerText="Error: your browser does not support HTML canvas",this.colorPickerCanvas.appendChild(r)}this.colorPickerDiv.className="vis-color",this.opacityDiv=document.createElement("div"),this.opacityDiv.className="vis-opacity",this.brightnessDiv=document.createElement("div"),this.brightnessDiv.className="vis-brightness",this.arrowDiv=document.createElement("div"),this.arrowDiv.className="vis-arrow",this.opacityRange=document.createElement("input");try{this.opacityRange.type="range",this.opacityRange.min="0",this.opacityRange.max="100"}catch(t){}this.opacityRange.value="100",this.opacityRange.className="vis-range",this.brightnessRange=document.createElement("input");try{this.brightnessRange.type="range",this.brightnessRange.min="0",this.brightnessRange.max="100"}catch(t){}this.brightnessRange.value="100",this.brightnessRange.className="vis-range",this.opacityDiv.appendChild(this.opacityRange),this.brightnessDiv.appendChild(this.brightnessRange);var s=this;this.opacityRange.onchange=function(){s._setOpacity(this.value)},this.opacityRange.oninput=function(){s._setOpacity(this.value)},this.brightnessRange.onchange=function(){s._setBrightness(this.value)},this.brightnessRange.oninput=function(){s._setBrightness(this.value)},this.brightnessLabel=document.createElement("div"),this.brightnessLabel.className="vis-label vis-brightness",this.brightnessLabel.innerText="brightness:",this.opacityLabel=document.createElement("div"),this.opacityLabel.className="vis-label vis-opacity",this.opacityLabel.innerText="opacity:",this.newColorDiv=document.createElement("div"),this.newColorDiv.className="vis-new-color",this.newColorDiv.innerText="new",this.initialColorDiv=document.createElement("div"),this.initialColorDiv.className="vis-initial-color",this.initialColorDiv.innerText="initial",this.cancelButton=document.createElement("div"),this.cancelButton.className="vis-button vis-cancel",this.cancelButton.innerText="cancel",this.cancelButton.onclick=FF(t=this._hide).call(t,this,!1),this.applyButton=document.createElement("div"),this.applyButton.className="vis-button vis-apply",this.applyButton.innerText="apply",this.applyButton.onclick=FF(e=this._apply).call(e,this),this.saveButton=document.createElement("div"),this.saveButton.className="vis-button vis-save",this.saveButton.innerText="save",this.saveButton.onclick=FF(i=this._save).call(i,this),this.loadButton=document.createElement("div"),this.loadButton.className="vis-button vis-load",this.loadButton.innerText="load last",this.loadButton.onclick=FF(n=this._loadLast).call(n,this),this.frame.appendChild(this.colorPickerDiv),this.frame.appendChild(this.arrowDiv),this.frame.appendChild(this.brightnessLabel),this.frame.appendChild(this.brightnessDiv),this.frame.appendChild(this.opacityLabel),this.frame.appendChild(this.opacityDiv),this.frame.appendChild(this.newColorDiv),this.frame.appendChild(this.initialColorDiv),this.frame.appendChild(this.cancelButton),this.frame.appendChild(this.applyButton),this.frame.appendChild(this.saveButton),this.frame.appendChild(this.loadButton)}},{key:"_bindHammer",value:function(){var t=this;this.drag={},this.pinch={},this.hammer=new mK(this.colorPickerCanvas),this.hammer.get("pinch").set({enable:!0}),this.hammer.on("hammer.input",(function(e){e.isFirst&&t._moveSelector(e)})),this.hammer.on("tap",(function(e){t._moveSelector(e)})),this.hammer.on("panstart",(function(e){t._moveSelector(e)})),this.hammer.on("panmove",(function(e){t._moveSelector(e)})),this.hammer.on("panend",(function(e){t._moveSelector(e)}))}},{key:"_generateHueCircle",value:function(){if(!1===this.generated){var t=this.colorPickerCanvas.getContext("2d");void 0===this.pixelRation&&(this.pixelRatio=(window.devicePixelRatio||1)/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1)),t.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0);var e,i,n,o,r=this.colorPickerCanvas.clientWidth,s=this.colorPickerCanvas.clientHeight;t.clearRect(0,0,r,s),this.centerCoordinates={x:.5*r,y:.5*s},this.r=.49*r;var a,d=2*Math.PI/360,l=1/360,c=1/this.r;for(n=0;n<360;n++)for(o=0;o3&&void 0!==arguments[3]?arguments[3]:1,r=arguments.length>4&&void 0!==arguments[4]?arguments[4]:function(){return!1};rj(this,t),this.parent=e,this.changedOptions=[],this.container=i,this.allowCreation=!1,this.hideOption=r,this.options={},this.initialized=!1,this.popupCounter=0,this.defaultOptions={enabled:!1,filter:!0,container:void 0,showButton:!0},pF(this.options,this.defaultOptions),this.configureOptions=n,this.moduleOptions={},this.domElements=[],this.popupDiv={},this.popupLimit=5,this.popupHistory={},this.colorPicker=new qK(o),this.wrapper=void 0}return cW(t,[{key:"setOptions",value:function(t){if(void 0!==t){this.popupHistory={},this._removePopup();var e=!0;if("string"==typeof t)this.options.filter=t;else if(fV(t))this.options.filter=t.join();else if("object"===sW(t)){if(null==t)throw new TypeError("options cannot be null");void 0!==t.container&&(this.options.container=t.container),void 0!==Jq(t)&&(this.options.filter=Jq(t)),void 0!==t.showButton&&(this.options.showButton=t.showButton),void 0!==t.enabled&&(e=t.enabled)}else"boolean"==typeof t?(this.options.filter=!0,e=t):"function"==typeof t&&(this.options.filter=t,e=!0);!1===Jq(this.options)&&(e=!1),this.options.enabled=e}this._clean()}},{key:"setModuleOptions",value:function(t){this.moduleOptions=t,!0===this.options.enabled&&(this._clean(),void 0!==this.options.container&&(this.container=this.options.container),this._create())}},{key:"_create",value:function(){this._clean(),this.changedOptions=[];var t=Jq(this.options),e=0,i=!1;for(var n in this.configureOptions)Object.prototype.hasOwnProperty.call(this.configureOptions,n)&&(this.allowCreation=!1,i=!1,"function"==typeof t?i=(i=t(n,[]))||this._handleObject(this.configureOptions[n],[n],!0):!0!==t&&-1===PX(t).call(t,n)||(i=!0),!1!==i&&(this.allowCreation=!0,e>0&&this._makeItem([]),this._makeHeader(n),this._handleObject(this.configureOptions[n],[n])),e++);this._makeButton(),this._push()}},{key:"_push",value:function(){this.wrapper=document.createElement("div"),this.wrapper.className="vis-configuration-wrapper",this.container.appendChild(this.wrapper);for(var t=0;t1?i-1:0),o=1;o2&&void 0!==arguments[2]&&arguments[2],n=document.createElement("div");if(n.className="vis-configuration vis-config-label vis-config-s"+e.length,!0===i){for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(XK("i","b",t))}else n.innerText=t+":";return n}},{key:"_makeDropdown",value:function(t,e,i){var n=document.createElement("select");n.className="vis-configuration vis-config-select";var o=0;void 0!==e&&-1!==PX(t).call(t,e)&&(o=PX(t).call(t,e));for(var r=0;rr&&1!==r&&(a.max=Math.ceil(e*c),l=a.max,d="range increased"),a.value=e}else a.value=n;var h=document.createElement("input");h.className="vis-configuration vis-config-rangeinput",h.value=a.value;var u=this;a.onchange=function(){h.value=this.value,u._update(Number(this.value),i)},a.oninput=function(){h.value=this.value};var p=this._makeLabel(i[i.length-1],i),f=this._makeItem(i,p,a,h);""!==d&&this.popupHistory[f]!==l&&(this.popupHistory[f]=l,this._setupPopup(d,f))}},{key:"_makeButton",value:function(){var t=this;if(!0===this.options.showButton){var e=document.createElement("div");e.className="vis-configuration vis-config-button",e.innerText="generate options",e.onclick=function(){t._printOptions()},e.onmouseover=function(){e.className="vis-configuration vis-config-button hover"},e.onmouseout=function(){e.className="vis-configuration vis-config-button"},this.optionsContainer=document.createElement("div"),this.optionsContainer.className="vis-configuration vis-config-option-container",this.domElements.push(this.optionsContainer),this.domElements.push(e)}}},{key:"_setupPopup",value:function(t,e){var i=this;if(!0===this.initialized&&!0===this.allowCreation&&this.popupCounter1&&void 0!==arguments[1]?arguments[1]:[],i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=!1,o=Jq(this.options),r=!1;for(var s in t)if(Object.prototype.hasOwnProperty.call(t,s)){n=!0;var a=t[s],d=IK(e,s);if("function"==typeof o&&!1===(n=o(s,e))&&!fV(a)&&"string"!=typeof a&&"boolean"!=typeof a&&a instanceof Object&&(this.allowCreation=!1,n=this._handleObject(a,d,!0),this.allowCreation=!1===i),!1!==n){r=!0;var l=this._getValue(d);if(fV(a))this._handleArray(a,l,d);else if("string"==typeof a)this._makeTextInput(a,l,d);else if("boolean"==typeof a)this._makeCheckbox(a,l,d);else if(a instanceof Object){if(!this.hideOption(e,s,this.moduleOptions))if(void 0!==a.enabled){var c=IK(d,"enabled"),h=this._getValue(c);if(!0===h){var u=this._makeLabel(s,d,!0);this._makeItem(d,u),r=this._handleObject(a,d)||r}else this._makeCheckbox(a,h,d)}else{var p=this._makeLabel(s,d,!0);this._makeItem(d,p),r=this._handleObject(a,d)||r}}else console.error("dont know how to handle",a,s,d)}}return r}},{key:"_handleArray",value:function(t,e,i){"string"==typeof t[0]&&"color"===t[0]?(this._makeColorField(t,e,i),t[1]!==e&&this.changedOptions.push({path:i,value:e})):"string"==typeof t[0]?(this._makeDropdown(t,e,i),t[0]!==e&&this.changedOptions.push({path:i,value:e})):"number"==typeof t[0]&&(this._makeRange(t,e,i),t[0]!==e&&this.changedOptions.push({path:i,value:Number(e)}))}},{key:"_update",value:function(t,e){var i=this._constructOptions(t,e);this.parent.body&&this.parent.body.emitter&&this.parent.body.emitter.emit&&this.parent.body.emitter.emit("configChange",i),this.initialized=!0,this.parent.setOptions(i)}},{key:"_constructOptions",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},n=i;t="false"!==(t="true"===t||t)&&t;for(var o=0;oo-this.padding&&(a=!0),r=a?this.x-i:this.x,s=d?this.y-e:this.y}else(s=this.y-e)+e+this.padding>n&&(s=n-e-this.padding),so&&(r=o-i-this.padding),rs.distance?" in "+t.printLocation(r.path,e,"")+"Perhaps it was misplaced? Matching option found at: "+t.printLocation(s.path,s.closestMatch,""):r.distance<=8?'. Did you mean "'+r.closestMatch+'"?'+t.printLocation(r.path,e):". Did you mean one of these: "+t.print(kV(i))+t.printLocation(n,e),console.error('%cUnknown option detected: "'+e+'"'+o,QK),ZK=!0}},{key:"findInOptions",value:function(e,i,n){var o,r=arguments.length>3&&void 0!==arguments[3]&&arguments[3],s=1e9,a="",d=[],l=e.toLowerCase(),c=void 0;for(var h in i){var u=void 0;if(void 0!==i[h].__type__&&!0===r){var p=t.findInOptions(e,i[h],IK(n,h));s>p.distance&&(a=p.closestMatch,d=p.path,s=p.distance,c=p.indexMatch)}else{var f;-1!==PX(f=h.toLowerCase()).call(f,l)&&(c=h),s>(u=t.levenshteinDistance(e,h))&&(a=h,d=aV(o=n).call(o),s=u)}}return{closestMatch:a,path:d,distance:s,indexMatch:c}}},{key:"printLocation",value:function(t,e){for(var i="\n\n"+(arguments.length>2&&void 0!==arguments[2]?arguments[2]:"Problem value found at: \n")+"options = {\n",n=0;n":!0,"--":!0},hZ="",uZ=0,pZ="",fZ="",mZ=lZ.NULL;function vZ(){uZ++,pZ=hZ.charAt(uZ)}function gZ(){return hZ.charAt(uZ+1)}function yZ(t){var e=t.charCodeAt(0);return e<47?35===e||46===e:e<59?e>47:e<91?e>64:e<96?95===e:e<123&&e>96}function bZ(t,e){if(t||(t={}),e)for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}function xZ(t,e,i){for(var n=e.split("."),o=t;n.length;){var r=n.shift();n.length?(o[r]||(o[r]={}),o=o[r]):o[r]=i}}function _Z(t,e){for(var i,n,o=null,r=[t],s=t;s.parent;)r.push(s.parent),s=s.parent;if(s.nodes)for(i=0,n=s.nodes.length;i=0;i--){var a,d=r[i];d.nodes||(d.nodes=[]),-1===PX(a=d.nodes).call(a,o)&&d.nodes.push(o)}e.attr&&(o.attr=bZ(o.attr,e.attr))}function wZ(t,e){if(t.edges||(t.edges=[]),t.edges.push(e),t.edge){var i=bZ({},t.edge);e.attr=bZ(i,e.attr)}}function EZ(t,e,i,n,o){var r={from:e,to:i,type:n};return t.edge&&(r.attr=bZ({},t.edge)),r.attr=bZ(r.attr||{},o),null!=o&&o.hasOwnProperty("arrows")&&null!=o.arrows&&(r.arrows={to:{enabled:!0,type:o.arrows.type}},o.arrows=null),r}function kZ(){for(mZ=lZ.NULL,fZ="";" "===pZ||"\t"===pZ||"\n"===pZ||"\r"===pZ;)vZ();do{var t=!1;if("#"===pZ){for(var e=uZ-1;" "===hZ.charAt(e)||"\t"===hZ.charAt(e);)e--;if("\n"===hZ.charAt(e)||""===hZ.charAt(e)){for(;""!=pZ&&"\n"!=pZ;)vZ();t=!0}}if("/"===pZ&&"/"===gZ()){for(;""!=pZ&&"\n"!=pZ;)vZ();t=!0}if("/"===pZ&&"*"===gZ()){for(;""!=pZ;){if("*"===pZ&&"/"===gZ()){vZ(),vZ();break}vZ()}t=!0}for(;" "===pZ||"\t"===pZ||"\n"===pZ||"\r"===pZ;)vZ()}while(t);if(""!==pZ){var i=pZ+gZ();if(cZ[i])return mZ=lZ.DELIMITER,fZ=i,vZ(),void vZ();if(cZ[pZ])return mZ=lZ.DELIMITER,fZ=pZ,void vZ();if(yZ(pZ)||"-"===pZ){for(fZ+=pZ,vZ();yZ(pZ);)fZ+=pZ,vZ();return"false"===fZ?fZ=!1:"true"===fZ?fZ=!0:isNaN(Number(fZ))||(fZ=Number(fZ)),void(mZ=lZ.IDENTIFIER)}if('"'===pZ){for(vZ();""!=pZ&&('"'!=pZ||'"'===pZ&&'"'===gZ());)'"'===pZ?(fZ+=pZ,vZ()):"\\"===pZ&&"n"===gZ()?(fZ+="\n",vZ()):fZ+=pZ,vZ();if('"'!=pZ)throw AZ('End of string " expected');return vZ(),void(mZ=lZ.IDENTIFIER)}for(mZ=lZ.UNKNOWN;""!=pZ;)fZ+=pZ,vZ();throw new SyntaxError('Syntax error in part "'+RZ(fZ,30)+'"')}mZ=lZ.DELIMITER}function OZ(t){for(;""!==fZ&&"}"!=fZ;)CZ(t),";"===fZ&&kZ()}function CZ(t){var e=SZ(t);if(e)TZ(t,e);else{var i=function(t){if("node"===fZ)return kZ(),t.node=IZ(),"node";if("edge"===fZ)return kZ(),t.edge=IZ(),"edge";if("graph"===fZ)return kZ(),t.graph=IZ(),"graph";return null}(t);if(!i){if(mZ!=lZ.IDENTIFIER)throw AZ("Identifier expected");var n=fZ;if(kZ(),"="===fZ){if(kZ(),mZ!=lZ.IDENTIFIER)throw AZ("Identifier expected");t[n]=fZ,kZ()}else!function(t,e){var i={id:e},n=IZ();n&&(i.attr=n);_Z(t,i),TZ(t,e)}(t,n)}}}function SZ(t){var e=null;if("subgraph"===fZ&&((e={}).type="subgraph",kZ(),mZ===lZ.IDENTIFIER&&(e.id=fZ,kZ())),"{"===fZ){if(kZ(),e||(e={}),e.parent=t,e.node=t.node,e.edge=t.edge,e.graph=t.graph,OZ(e),"}"!=fZ)throw AZ("Angle bracket } expected");kZ(),delete e.node,delete e.edge,delete e.graph,delete e.parent,t.subgraphs||(t.subgraphs=[]),t.subgraphs.push(e)}return e}function TZ(t,e){for(;"->"===fZ||"--"===fZ;){var i,n=fZ;kZ();var o=SZ(t);if(o)i=o;else{if(mZ!=lZ.IDENTIFIER)throw AZ("Identifier or subgraph expected");_Z(t,{id:i=fZ}),kZ()}wZ(t,EZ(t,e,i,n,IZ())),e=i}}function IZ(){for(var t,e,i=null,n={dashed:!0,solid:!1,dotted:[1,5]},o={dot:"circle",box:"box",crow:"crow",curve:"curve",icurve:"inv_curve",normal:"triangle",inv:"inv_triangle",diamond:"diamond",tee:"bar",vee:"vee"},r=new Array,s=new Array;"["===fZ;){for(kZ(),i={};""!==fZ&&"]"!=fZ;){if(mZ!=lZ.IDENTIFIER)throw AZ("Attribute name expected");var a=fZ;if(kZ(),"="!=fZ)throw AZ("Equal sign = expected");if(kZ(),mZ!=lZ.IDENTIFIER)throw AZ("Attribute value expected");var d=fZ;"style"===a&&(d=n[d]),"arrowhead"===a&&(a="arrows",d={to:{enabled:!0,type:o[d]}}),"arrowtail"===a&&(a="arrows",d={from:{enabled:!0,type:o[d]}}),r.push({attr:i,name:a,value:d}),s.push(a),kZ(),","==fZ&&kZ()}if("]"!=fZ)throw AZ("Bracket ] expected");kZ()}if(Hq(s).call(s,"dir")){var l={arrows:{}};for(t=0;t"===t.type&&(e.arrows="to"),e};HV(o=i.edges).call(o,(function(t){var e,i,o,s;(e=t.from instanceof Object?t.from.nodes:{id:t.from},i=t.to instanceof Object?t.to.nodes:{id:t.to},t.from instanceof Object&&t.from.edges)&&HV(o=t.from.edges).call(o,(function(t){var e=r(t);n.edges.push(e)}));(function(t,e,i){fV(t)?HV(t).call(t,(function(t){fV(e)?HV(e).call(e,(function(e){i(t,e)})):i(t,e)})):fV(e)?HV(e).call(e,(function(e){i(t,e)})):i(t,e)}(e,i,(function(e,i){var o=EZ(n,e.id,i.id,t.type,t.attr),s=r(o);n.edges.push(s)})),t.to instanceof Object&&t.to.edges)&&HV(s=t.to.edges).call(s,(function(t){var e=r(t);n.edges.push(e)}))}))}return i.attr&&(n.options=i.attr),n}var FZ=Object.freeze({__proto__:null,cn:{addDescription:"单击空白处放置新节点。",addEdge:"添加连接线",addNode:"添加节点",back:"返回",close:"關閉",createEdgeError:"无法将连接线连接到群集。",del:"删除选定",deleteClusterError:"无法删除群集。",edgeDescription:"单击某个节点并将该连接线拖动到另一个节点以连接它们。",edit:"编辑",editClusterError:"无法编辑群集。",editEdge:"编辑连接线",editEdgeDescription:"单击控制节点并将它们拖到节点上连接。",editNode:"编辑节点"},cs:{addDescription:"Kluknutím do prázdného prostoru můžete přidat nový vrchol.",addEdge:"Přidat hranu",addNode:"Přidat vrchol",back:"Zpět",close:"Zavřít",createEdgeError:"Nelze připojit hranu ke shluku.",del:"Smazat výběr",deleteClusterError:"Nelze mazat shluky.",edgeDescription:"Přetažením z jednoho vrcholu do druhého můžete spojit tyto vrcholy novou hranou.",edit:"Upravit",editClusterError:"Nelze upravovat shluky.",editEdge:"Upravit hranu",editEdgeDescription:"Přetažením kontrolního vrcholu hrany ji můžete připojit k jinému vrcholu.",editNode:"Upravit vrchol"},de:{addDescription:"Klicke auf eine freie Stelle, um einen neuen Knoten zu plazieren.",addEdge:"Kante hinzufügen",addNode:"Knoten hinzufügen",back:"Zurück",close:"Schließen",createEdgeError:"Es ist nicht möglich, Kanten mit Clustern zu verbinden.",del:"Lösche Auswahl",deleteClusterError:"Cluster können nicht gelöscht werden.",edgeDescription:"Klicke auf einen Knoten und ziehe die Kante zu einem anderen Knoten, um diese zu verbinden.",edit:"Editieren",editClusterError:"Cluster können nicht editiert werden.",editEdge:"Kante editieren",editEdgeDescription:"Klicke auf die Verbindungspunkte und ziehe diese auf einen Knoten, um sie zu verbinden.",editNode:"Knoten editieren"},en:{addDescription:"Click in an empty space to place a new node.",addEdge:"Add Edge",addNode:"Add Node",back:"Back",close:"Close",createEdgeError:"Cannot link edges to a cluster.",del:"Delete selected",deleteClusterError:"Clusters cannot be deleted.",edgeDescription:"Click on a node and drag the edge to another node to connect them.",edit:"Edit",editClusterError:"Clusters cannot be edited.",editEdge:"Edit Edge",editEdgeDescription:"Click on the control points and drag them to a node to connect to it.",editNode:"Edit Node"},es:{addDescription:"Haga clic en un lugar vacío para colocar un nuevo nodo.",addEdge:"Añadir arista",addNode:"Añadir nodo",back:"Atrás",close:"Cerrar",createEdgeError:"No se puede conectar una arista a un grupo.",del:"Eliminar selección",deleteClusterError:"No es posible eliminar grupos.",edgeDescription:"Haga clic en un nodo y arrastre la arista hacia otro nodo para conectarlos.",edit:"Editar",editClusterError:"No es posible editar grupos.",editEdge:"Editar arista",editEdgeDescription:"Haga clic en un punto de control y arrastrelo a un nodo para conectarlo.",editNode:"Editar nodo"},fr:{addDescription:"Cliquez dans un endroit vide pour placer un nœud.",addEdge:"Ajouter un lien",addNode:"Ajouter un nœud",back:"Retour",close:"Fermer",createEdgeError:"Impossible de créer un lien vers un cluster.",del:"Effacer la sélection",deleteClusterError:"Les clusters ne peuvent pas être effacés.",edgeDescription:"Cliquez sur un nœud et glissez le lien vers un autre nœud pour les connecter.",edit:"Éditer",editClusterError:"Les clusters ne peuvent pas être édités.",editEdge:"Éditer le lien",editEdgeDescription:"Cliquez sur les points de contrôle et glissez-les pour connecter un nœud.",editNode:"Éditer le nœud"},it:{addDescription:"Clicca per aggiungere un nuovo nodo",addEdge:"Aggiungi un vertice",addNode:"Aggiungi un nodo",back:"Indietro",close:"Chiudere",createEdgeError:"Non si possono collegare vertici ad un cluster",del:"Cancella la selezione",deleteClusterError:"I cluster non possono essere cancellati",edgeDescription:"Clicca su un nodo e trascinalo ad un altro nodo per connetterli.",edit:"Modifica",editClusterError:"I clusters non possono essere modificati.",editEdge:"Modifica il vertice",editEdgeDescription:"Clicca sui Punti di controllo e trascinali ad un nodo per connetterli.",editNode:"Modifica il nodo"},nl:{addDescription:"Klik op een leeg gebied om een nieuwe node te maken.",addEdge:"Link toevoegen",addNode:"Node toevoegen",back:"Terug",close:"Sluiten",createEdgeError:"Kan geen link maken naar een cluster.",del:"Selectie verwijderen",deleteClusterError:"Clusters kunnen niet worden verwijderd.",edgeDescription:"Klik op een node en sleep de link naar een andere node om ze te verbinden.",edit:"Wijzigen",editClusterError:"Clusters kunnen niet worden aangepast.",editEdge:"Link wijzigen",editEdgeDescription:"Klik op de verbindingspunten en sleep ze naar een node om daarmee te verbinden.",editNode:"Node wijzigen"},pt:{addDescription:"Clique em um espaço em branco para adicionar um novo nó",addEdge:"Adicionar aresta",addNode:"Adicionar nó",back:"Voltar",close:"Fechar",createEdgeError:"Não foi possível linkar arestas a um cluster.",del:"Remover selecionado",deleteClusterError:"Clusters não puderam ser removidos.",edgeDescription:"Clique em um nó e arraste a aresta até outro nó para conectá-los",edit:"Editar",editClusterError:"Clusters não puderam ser editados.",editEdge:"Editar aresta",editEdgeDescription:"Clique nos pontos de controle e os arraste para um nó para conectá-los",editNode:"Editar nó"},ru:{addDescription:"Кликните в свободное место, чтобы добавить новый узел.",addEdge:"Добавить ребро",addNode:"Добавить узел",back:"Назад",close:"Закрывать",createEdgeError:"Невозможно соединить ребра в кластер.",del:"Удалить выбранное",deleteClusterError:"Кластеры не могут быть удалены",edgeDescription:"Кликните на узел и протяните ребро к другому узлу, чтобы соединить их.",edit:"Редактировать",editClusterError:"Кластеры недоступны для редактирования.",editEdge:"Редактировать ребро",editEdgeDescription:"Кликните на контрольные точки и перетащите их в узел, чтобы подключиться к нему.",editNode:"Редактировать узел"},uk:{addDescription:"Kлікніть на вільне місце, щоб додати новий вузол.",addEdge:"Додати край",addNode:"Додати вузол",back:"Назад",close:"Закрити",createEdgeError:"Не можливо об'єднати краї в групу.",del:"Видалити обране",deleteClusterError:"Групи не можуть бути видалені.",edgeDescription:"Клікніть на вузол і перетягніть край до іншого вузла, щоб їх з'єднати.",edit:"Редагувати",editClusterError:"Групи недоступні для редагування.",editEdge:"Редагувати край",editEdgeDescription:"Клікніть на контрольні точки і перетягніть їх у вузол, щоб підключитися до нього.",editNode:"Редагувати вузол"}});var NZ=function(){function t(){rj(this,t),this.NUM_ITERATIONS=4,this.image=new Image,this.canvas=document.createElement("canvas")}return cW(t,[{key:"init",value:function(){if(!this.initialized()){this.src=this.image.src;var t=this.image.width,e=this.image.height;this.width=t,this.height=e;var i=Math.floor(e/2),n=Math.floor(e/4),o=Math.floor(e/8),r=Math.floor(e/16),s=Math.floor(t/2),a=Math.floor(t/4),d=Math.floor(t/8),l=Math.floor(t/16);this.canvas.width=3*a,this.canvas.height=i,this.coordinates=[[0,0,s,i],[s,0,a,n],[s,n,d,o],[5*d,n,l,r]],this._fillMipMap()}}},{key:"initialized",value:function(){return void 0!==this.coordinates}},{key:"_fillMipMap",value:function(){var t=this.canvas.getContext("2d"),e=this.coordinates[0];t.drawImage(this.image,e[0],e[1],e[2],e[3]);for(var i=1;i2){e*=.5;for(var s=0;e>2&&s=this.NUM_ITERATIONS&&(s=this.NUM_ITERATIONS-1);var a=this.coordinates[s];t.drawImage(this.canvas,a[0],a[1],a[2],a[3],i,n,o,r)}else t.drawImage(this.image,i,n,o,r)}}]),t}(),BZ=function(){function t(e){rj(this,t),this.images={},this.imageBroken={},this.callback=e}return cW(t,[{key:"_tryloadBrokenUrl",value:function(t,e,i){void 0!==t&&void 0!==i&&(void 0!==e?(i.image.onerror=function(){console.error("Could not load brokenImage:",e)},i.image.src=e):console.warn("No broken url image defined"))}},{key:"_redrawWithImage",value:function(t){this.callback&&this.callback(t)}},{key:"load",value:function(t,e){var i=this,n=this.images[t];if(n)return n;var o=new NZ;return this.images[t]=o,o.image.onload=function(){i._fixImageCoordinates(o.image),o.init(),i._redrawWithImage(o)},o.image.onerror=function(){console.error("Could not load image:",t),i._tryloadBrokenUrl(t,e,o)},o.image.src=t,o}},{key:"_fixImageCoordinates",value:function(t){0===t.width&&(document.body.appendChild(t),t.width=t.offsetWidth,t.height=t.offsetHeight,document.body.removeChild(t))}}]),t}(),LZ={exports:{}},zZ=JA((function(){if("function"==typeof ArrayBuffer){var t=new ArrayBuffer(8);Object.isExtensible(t)&&Object.defineProperty(t,"a",{value:8})}})),jZ=JA,HZ=YR,$Z=fR,UZ=zZ,WZ=Object.isExtensible,VZ=jZ((function(){WZ(1)}))||UZ?function(t){return!!HZ(t)&&((!UZ||"ArrayBuffer"!==$Z(t))&&(!WZ||WZ(t)))}:WZ,qZ=!JA((function(){return Object.isExtensible(Object.preventExtensions({}))})),XZ=wM,YZ=cR,GZ=HM,KZ=YR,ZZ=qP,QZ=VD.f,JZ=Uj,tQ=qj,eQ=VZ,iQ=qZ,nQ=!1,oQ=ZP("meta"),rQ=0,sQ=function(t){QZ(t,oQ,{value:{objectID:"O"+rQ++,weakData:{}}})},aQ=LZ.exports={enable:function(){aQ.enable=function(){},nQ=!0;var t=JZ.f,e=YZ([].splice),i={};i[oQ]=1,t(i).length&&(JZ.f=function(i){for(var n=t(i),o=0,r=n.length;or;r++)if((a=g(t[r]))&&mQ(_Q,a))return a;return new xQ(!1)}n=vQ(t,o)}for(d=u?t.next:n.next;!(l=cQ(d,n)).done;){try{a=g(l.value)}catch(t){yQ(n,"throw",t)}if("object"==typeof a&&a&&mQ(_Q,a))return a}return new xQ(!1)},EQ=eP,kQ=TypeError,OQ=function(t,e){if(EQ(e,t))return t;throw new kQ("Incorrect invocation")},CQ=wM,SQ=QA,TQ=dQ,IQ=JA,AQ=cM,RQ=wQ,PQ=OQ,DQ=_R,MQ=YR,FQ=zR,NQ=HB,BQ=VD.f,LQ=OH.forEach,zQ=ER,jQ=DN.set,HQ=DN.getterFor,$Q=function(t,e,i){var n,o=-1!==t.indexOf("Map"),r=-1!==t.indexOf("Weak"),s=o?"set":"add",a=SQ[t],d=a&&a.prototype,l={};if(zQ&&DQ(a)&&(r||d.forEach&&!IQ((function(){(new a).entries().next()})))){var c=(n=e((function(e,i){jQ(PQ(e,c),{type:t,collection:new a}),FQ(i)||RQ(i,e[s],{that:e,AS_ENTRIES:o})}))).prototype,h=HQ(t);LQ(["add","clear","delete","forEach","get","has","set","keys","values","entries"],(function(t){var e="add"===t||"set"===t;!(t in d)||r&&"clear"===t||AQ(c,t,(function(i,n){var o=h(this).collection;if(!e&&r&&!MQ(i))return"get"===t&&void 0;var s=o[t](0===i?0:i,n);return e?this:s}))})),r||BQ(c,"size",{configurable:!0,get:function(){return h(this).collection.size}})}else n=i.getConstructor(e,t,o,s),TQ.enable();return NQ(n,t,!1,!0),l[t]=n,CQ({global:!0,forced:!0},l),r||i.setStrong(n,t,o),n},UQ=wB,WQ=function(t,e,i){for(var n in e)i&&i.unsafe&&t[n]?t[n]=e[n]:UQ(t,n,e[n],i);return t},VQ=tP,qQ=rH,XQ=ER,YQ=sD("species"),GQ=hB,KQ=rH,ZQ=WQ,QQ=WD,JQ=OQ,tJ=zR,eJ=wQ,iJ=yL,nJ=bL,oJ=function(t){var e=VQ(t);XQ&&e&&!e[YQ]&&qQ(e,YQ,{configurable:!0,get:function(){return this}})},rJ=ER,sJ=dQ.fastKey,aJ=DN.set,dJ=DN.getterFor,lJ={getConstructor:function(t,e,i,n){var o=t((function(t,o){JQ(t,r),aJ(t,{type:e,index:GQ(null),first:void 0,last:void 0,size:0}),rJ||(t.size=0),tJ(o)||eJ(o,t[n],{that:t,AS_ENTRIES:i})})),r=o.prototype,s=dJ(e),a=function(t,e,i){var n,o,r=s(t),a=d(t,e);return a?a.value=i:(r.last=a={index:o=sJ(e,!0),key:e,value:i,previous:n=r.last,next:void 0,removed:!1},r.first||(r.first=a),n&&(n.next=a),rJ?r.size++:t.size++,"F"!==o&&(r.index[o]=a)),t},d=function(t,e){var i,n=s(t),o=sJ(e);if("F"!==o)return n.index[o];for(i=n.first;i;i=i.next)if(i.key===e)return i};return ZQ(r,{clear:function(){for(var t=s(this),e=t.index,i=t.first;i;)i.removed=!0,i.previous&&(i.previous=i.previous.next=void 0),delete e[i.index],i=i.next;t.first=t.last=void 0,rJ?t.size=0:this.size=0},delete:function(t){var e=this,i=s(e),n=d(e,t);if(n){var o=n.next,r=n.previous;delete i.index[n.index],n.removed=!0,r&&(r.next=o),o&&(o.previous=r),i.first===n&&(i.first=o),i.last===n&&(i.last=r),rJ?i.size--:e.size--}return!!n},forEach:function(t){for(var e,i=s(this),n=QQ(t,arguments.length>1?arguments[1]:void 0);e=e?e.next:i.first;)for(n(e.value,e.key,this);e&&e.removed;)e=e.previous},has:function(t){return!!d(this,t)}}),ZQ(r,i?{get:function(t){var e=d(this,t);return e&&e.value},set:function(t,e){return a(this,0===t?0:t,e)}}:{add:function(t){return a(this,t=0===t?0:t,t)}}),rJ&&KQ(r,"size",{configurable:!0,get:function(){return s(this).size}}),o},setStrong:function(t,e,i){var n=e+" Iterator",o=dJ(e),r=dJ(n);iJ(t,e,(function(t,e){aJ(this,{type:n,target:t,state:o(t),kind:e,last:void 0})}),(function(){for(var t=r(this),e=t.kind,i=t.last;i&&i.removed;)i=i.previous;return t.target&&(t.last=i=i?i.next:t.state.first)?nJ("keys"===e?i.key:"values"===e?i.value:[i.key,i.value],!1):(t.target=void 0,nJ(void 0,!0))}),i?"entries":"values",!i,!0),oJ(e)}};$Q("Map",(function(t){return function(){return t(this,arguments.length?arguments[0]:void 0)}}),lJ);var cJ=KA(GR.Map),hJ=function(){function t(){rj(this,t),this.clear(),this._defaultIndex=0,this._groupIndex=0,this._defaultGroups=[{border:"#2B7CE9",background:"#97C2FC",highlight:{border:"#2B7CE9",background:"#D2E5FF"},hover:{border:"#2B7CE9",background:"#D2E5FF"}},{border:"#FFA500",background:"#FFFF00",highlight:{border:"#FFA500",background:"#FFFFA3"},hover:{border:"#FFA500",background:"#FFFFA3"}},{border:"#FA0A10",background:"#FB7E81",highlight:{border:"#FA0A10",background:"#FFAFB1"},hover:{border:"#FA0A10",background:"#FFAFB1"}},{border:"#41A906",background:"#7BE141",highlight:{border:"#41A906",background:"#A1EC76"},hover:{border:"#41A906",background:"#A1EC76"}},{border:"#E129F0",background:"#EB7DF4",highlight:{border:"#E129F0",background:"#F0B3F5"},hover:{border:"#E129F0",background:"#F0B3F5"}},{border:"#7C29F0",background:"#AD85E4",highlight:{border:"#7C29F0",background:"#D3BDF0"},hover:{border:"#7C29F0",background:"#D3BDF0"}},{border:"#C37F00",background:"#FFA807",highlight:{border:"#C37F00",background:"#FFCA66"},hover:{border:"#C37F00",background:"#FFCA66"}},{border:"#4220FB",background:"#6E6EFD",highlight:{border:"#4220FB",background:"#9B9BFD"},hover:{border:"#4220FB",background:"#9B9BFD"}},{border:"#FD5A77",background:"#FFC0CB",highlight:{border:"#FD5A77",background:"#FFD1D9"},hover:{border:"#FD5A77",background:"#FFD1D9"}},{border:"#4AD63A",background:"#C2FABC",highlight:{border:"#4AD63A",background:"#E6FFE3"},hover:{border:"#4AD63A",background:"#E6FFE3"}},{border:"#990000",background:"#EE0000",highlight:{border:"#BB0000",background:"#FF3333"},hover:{border:"#BB0000",background:"#FF3333"}},{border:"#FF6000",background:"#FF6000",highlight:{border:"#FF6000",background:"#FF6000"},hover:{border:"#FF6000",background:"#FF6000"}},{border:"#97C2FC",background:"#2B7CE9",highlight:{border:"#D2E5FF",background:"#2B7CE9"},hover:{border:"#D2E5FF",background:"#2B7CE9"}},{border:"#399605",background:"#255C03",highlight:{border:"#399605",background:"#255C03"},hover:{border:"#399605",background:"#255C03"}},{border:"#B70054",background:"#FF007E",highlight:{border:"#B70054",background:"#FF007E"},hover:{border:"#B70054",background:"#FF007E"}},{border:"#AD85E4",background:"#7C29F0",highlight:{border:"#D3BDF0",background:"#7C29F0"},hover:{border:"#D3BDF0",background:"#7C29F0"}},{border:"#4557FA",background:"#000EA1",highlight:{border:"#6E6EFD",background:"#000EA1"},hover:{border:"#6E6EFD",background:"#000EA1"}},{border:"#FFC0CB",background:"#FD5A77",highlight:{border:"#FFD1D9",background:"#FD5A77"},hover:{border:"#FFD1D9",background:"#FD5A77"}},{border:"#C2FABC",background:"#74D66A",highlight:{border:"#E6FFE3",background:"#74D66A"},hover:{border:"#E6FFE3",background:"#74D66A"}},{border:"#EE0000",background:"#990000",highlight:{border:"#FF3333",background:"#BB0000"},hover:{border:"#FF3333",background:"#BB0000"}}],this.options={},this.defaultOptions={useDefaultGroups:!0},pF(this.options,this.defaultOptions)}return cW(t,[{key:"setOptions",value:function(t){var e=["useDefaultGroups"];if(void 0!==t)for(var i in t)if(Object.prototype.hasOwnProperty.call(t,i)&&-1===PX(e).call(e,i)){var n=t[i];this.add(i,n)}}},{key:"clear",value:function(){this._groups=new cJ,this._groupNames=[]}},{key:"get",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=this._groups.get(t);if(void 0===i&&e)if(!1===this.options.useDefaultGroups&&this._groupNames.length>0){var n=this._groupIndex%this._groupNames.length;++this._groupIndex,(i={}).color=this._groups.get(this._groupNames[n]),this._groups.set(t,i)}else{var o=this._defaultIndex%this._defaultGroups.length;this._defaultIndex++,(i={}).color=this._defaultGroups[o],this._groups.set(t,i)}return i}},{key:"add",value:function(t,e){return this._groups.has(t)||this._groupNames.push(t),this._groups.set(t,e),e}}]),t}();wM({target:"Number",stat:!0},{isNaN:function(t){return t!=t}});var uJ=KA(GR.Number.isNaN),pJ=QA.isFinite,fJ=Number.isFinite||function(t){return"number"==typeof t&&pJ(t)};wM({target:"Number",stat:!0},{isFinite:fJ});var mJ=KA(GR.Number.isFinite),vJ=OH.some;wM({target:"Array",proto:!0,forced:!AV("some")},{some:function(t){return vJ(this,t,arguments.length>1?arguments[1]:void 0)}});var gJ=TF("Array").some,yJ=eP,bJ=gJ,xJ=Array.prototype,_J=function(t){var e=t.some;return t===xJ||yJ(xJ,t)&&e===xJ.some?bJ:e},wJ=KA(_J),EJ=KA(GR.Object.getOwnPropertySymbols),kJ={exports:{}},OJ=wM,CJ=JA,SJ=VR,TJ=wR.f,IJ=ER;OJ({target:"Object",stat:!0,forced:!IJ||CJ((function(){TJ(1)})),sham:!IJ},{getOwnPropertyDescriptor:function(t,e){return TJ(SJ(t),e)}});var AJ=GR.Object,RJ=kJ.exports=function(t,e){return AJ.getOwnPropertyDescriptor(t,e)};AJ.getOwnPropertyDescriptor.sham&&(RJ.sham=!0);var PJ=kJ.exports,DJ=KA(PJ),MJ=pV,FJ=VR,NJ=wR,BJ=rz;wM({target:"Object",stat:!0,sham:!ER},{getOwnPropertyDescriptors:function(t){for(var e,i,n=FJ(t),o=NJ.f,r=MJ(n),s={},a=0;r.length>a;)void 0!==(i=o(n,e=r[a++]))&&BJ(s,e,i);return s}});var LJ=KA(GR.Object.getOwnPropertyDescriptors),zJ={exports:{}},jJ=wM,HJ=ER,$J=jN.f;jJ({target:"Object",stat:!0,forced:Object.defineProperties!==$J,sham:!HJ},{defineProperties:$J});var UJ=GR.Object,WJ=zJ.exports=function(t,e){return UJ.defineProperties(t,e)};UJ.defineProperties.sham&&(WJ.sham=!0);var VJ=KA(zJ.exports),qJ=KA(uj);function XJ(t,e,i){return(e=dW(e))in t?pj(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}var YJ=QA,GJ=JA,KJ=oN,ZJ=dX.trim,QJ=tX,JJ=cR("".charAt),t0=YJ.parseFloat,e0=YJ.Symbol,i0=e0&&e0.iterator,n0=1/t0(QJ+"-0")!=-1/0||i0&&!GJ((function(){t0(Object(i0))}))?function(t){var e=ZJ(KJ(t)),i=t0(e);return 0===i&&"-"===JJ(e,0)?-0:i}:t0;wM({global:!0,forced:parseFloat!==n0},{parseFloat:n0});var o0=KA(GR.parseFloat),r0=wM,s0=JA,a0=qj.f;r0({target:"Object",stat:!0,forced:s0((function(){return!Object.getOwnPropertyNames(1)}))},{getOwnPropertyNames:a0});var d0=GR.Object,l0=function(t){return d0.getOwnPropertyNames(t)},c0=KA(l0);function h0(t,e){var i=["node","edge","label"],n=!0,o=WK(e,"chosen");if("boolean"==typeof o)n=o;else if("object"===sW(o)){if(-1===PX(i).call(i,t))throw new Error("choosify: subOption '"+t+"' should be one of '"+i.join("', '")+"'");var r=WK(e,["chosen",t]);"boolean"!=typeof r&&"function"!=typeof r||(n=r)}return n}function u0(t,e,i){if(t.width<=0||t.height<=0)return!1;if(void 0!==i){var n={x:e.x-i.x,y:e.y-i.y};if(0!==i.angle){var o=-i.angle;e={x:Math.cos(o)*n.x-Math.sin(o)*n.y,y:Math.sin(o)*n.x+Math.cos(o)*n.y}}else e=n}var r=t.x+t.width,s=t.y+t.width;return t.lefte.x&&t.tope.y}function p0(t){return"string"==typeof t&&""!==t}function f0(t,e,i,n){var o=n.x,r=n.y;if("function"==typeof n.distanceToBorder){var s=n.distanceToBorder(t,e),a=Math.sin(e)*s,d=Math.cos(e)*s;d===s?(o+=s,r=n.y):a===s?(o=n.x,r-=s):(o+=d,r-=a)}else n.shape.width>n.shape.height?(o=n.x+.5*n.shape.width,r=n.y-i):(o=n.x+i,r=n.y-.5*n.shape.height);return{x:o,y:r}}var m0=TF("Array").values,v0=eN,g0=qP,y0=eP,b0=m0,x0=Array.prototype,_0={DOMTokenList:!0,NodeList:!0},w0=function(t){var e=t.values;return t===x0||y0(x0,t)&&e===x0.values||g0(_0,v0(t))?b0:e},E0=KA(w0),k0=function(){function t(e){rj(this,t),this.measureText=e,this.current=0,this.width=0,this.height=0,this.lines=[]}return cW(t,[{key:"_add",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"normal";void 0===this.lines[t]&&(this.lines[t]={width:0,height:0,blocks:[]});var n=e;void 0!==e&&""!==e||(n=" ");var o=this.measureText(n,i),r=pF({},E0(o));r.text=e,r.width=o.width,r.mod=i,void 0!==e&&""!==e||(r.width=0),this.lines[t].blocks.push(r),this.lines[t].width+=r.width}},{key:"curWidth",value:function(){var t=this.lines[this.current];return void 0===t?0:t.width}},{key:"append",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"normal";this._add(this.current,t,e)}},{key:"newLine",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"normal";this._add(this.current,t,e),this.current++}},{key:"determineLineHeights",value:function(){for(var t=0;tt&&(t=n.width),e+=n.height}this.width=t,this.height=e}},{key:"removeEmptyBlocks",value:function(){for(var t=[],e=0;e"://,""://,""://,"":/<\/b>/,"":/<\/i>/,"":/<\/code>/,"*":/\*/,_:/_/,"`":/`/,afterBold:/[^*]/,afterItal:/[^_]/,afterMono:/[^`]/},C0=function(){function t(e){rj(this,t),this.text=e,this.bold=!1,this.ital=!1,this.mono=!1,this.spacing=!1,this.position=0,this.buffer="",this.modStack=[],this.blocks=[]}return cW(t,[{key:"mod",value:function(){return 0===this.modStack.length?"normal":this.modStack[0]}},{key:"modName",value:function(){return 0===this.modStack.length?"normal":"mono"===this.modStack[0]?"mono":this.bold&&this.ital?"boldital":this.bold?"bold":this.ital?"ital":void 0}},{key:"emitBlock",value:function(){this.spacing&&(this.add(" "),this.spacing=!1),this.buffer.length>0&&(this.blocks.push({text:this.buffer,mod:this.modName()}),this.buffer="")}},{key:"add",value:function(t){" "===t&&(this.spacing=!0),this.spacing&&(this.buffer+=" ",this.spacing=!1)," "!=t&&(this.buffer+=t)}},{key:"parseWS",value:function(t){return!!/[ \t]/.test(t)&&(this.mono?this.add(t):this.spacing=!0,!0)}},{key:"setTag",value:function(t){this.emitBlock(),this[t]=!0,this.modStack.unshift(t)}},{key:"unsetTag",value:function(t){this.emitBlock(),this[t]=!1,this.modStack.shift()}},{key:"parseStartTag",value:function(t,e){return!(this.mono||this[t]||!this.match(e))&&(this.setTag(t),!0)}},{key:"match",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1],i=QW(this.prepareRegExp(t),2),n=i[0],o=i[1],r=n.test(this.text.substr(this.position,o));return r&&e&&(this.position+=o-1),r}},{key:"parseEndTag",value:function(t,e,i){var n=this.mod()===t;return!(!(n="mono"===t?n&&this.mono:n&&!this.mono)||!this.match(e))&&(void 0!==i?(this.position===this.text.length-1||this.match(i,!1))&&this.unsetTag(t):this.unsetTag(t),!0)}},{key:"replace",value:function(t,e){return!!this.match(t)&&(this.add(e),this.position+=length-1,!0)}},{key:"prepareRegExp",value:function(t){var e,i;if(t instanceof RegExp)i=t,e=1;else{var n=O0[t];i=void 0!==n?n:new RegExp(t),e=t.length}return[i,e]}}]),t}(),S0=function(){function t(e,i,n,o){var r=this;rj(this,t),this.ctx=e,this.parent=i,this.selected=n,this.hover=o;this.lines=new k0((function(t,i){if(void 0===t)return 0;var s=r.parent.getFormattingValues(e,n,o,i),a=0;""!==t&&(a=r.ctx.measureText(t).width);return{width:a,values:s}}))}return cW(t,[{key:"process",value:function(t){if(!p0(t))return this.lines.finalize();var e=this.parent.fontOptions;t=(t=t.replace(/\r\n/g,"\n")).replace(/\r/g,"\n");var i=String(t).split("\n"),n=i.length;if(e.multi)for(var o=0;o0)for(var s=0;s0)for(var u=0;u")||e.parseStartTag("ital","")||e.parseStartTag("mono","")||e.parseEndTag("bold","")||e.parseEndTag("ital","")||e.parseEndTag("mono",""))||i(n)||e.add(n),e.position++}return e.emitBlock(),e.blocks}},{key:"splitMarkdownBlocks",value:function(t){for(var e=this,i=new C0(t),n=!0,o=function(t){return!!/\\/.test(t)&&(i.positionthis.parent.fontOptions.maxWdt}},{key:"getLongestFit",value:function(t){for(var e="",i=0;i1&&void 0!==arguments[1]?arguments[1]:"normal",i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];this.parent.getFormattingValues(this.ctx,this.selected,this.hover,e);for(var n=(t=(t=t.replace(/^( +)/g,"$1\r")).replace(/([^\r][^ ]*)( +)/g,"$1\r$2\r")).split("\r");n.length>0;){var o=this.getLongestFit(n);if(0===o){var r=n[0],s=this.getLongestFitWord(r);this.lines.newLine(aV(r).call(r,0,s),e),n[0]=aV(r).call(r,s)}else{var a=o;" "===n[o-1]?o--:" "===n[a]&&a++;var d=aV(n).call(n,0,o).join("");o==n.length&&i?this.lines.append(d,e):this.lines.newLine(d,e),n=aV(n).call(n,a)}}}}]),t}(),T0=["bold","ital","boldital","mono"],I0=function(){function t(e,i){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];rj(this,t),this.body=e,this.pointToSelf=!1,this.baseSize=void 0,this.fontOptions={},this.setOptions(i),this.size={top:0,left:0,width:0,height:0,yLine:0},this.isEdgeLabel=n}return cW(t,[{key:"setOptions",value:function(t){if(this.elementOptions=t,this.initFontOptions(t.font),p0(t.label)?this.labelDirty=!0:t.label=void 0,void 0!==t.font&&null!==t.font)if("string"==typeof t.font)this.baseSize=this.fontOptions.size;else if("object"===sW(t.font)){var e=t.font.size;void 0!==e&&(this.baseSize=e)}}},{key:"initFontOptions",value:function(e){var i=this;RK(T0,(function(t){i.fontOptions[t]={}})),t.parseFontString(this.fontOptions,e)?this.fontOptions.vadjust=0:RK(e,(function(t,e){null!=t&&"object"!==sW(t)&&(i.fontOptions[e]=t)}))}},{key:"constrain",value:function(t){var e={constrainWidth:!1,maxWdt:-1,minWdt:-1,constrainHeight:!1,minHgt:-1,valign:"middle"},i=WK(t,"widthConstraint");if("number"==typeof i)e.maxWdt=Number(i),e.minWdt=Number(i);else if("object"===sW(i)){var n=WK(t,["widthConstraint","maximum"]);"number"==typeof n&&(e.maxWdt=Number(n));var o=WK(t,["widthConstraint","minimum"]);"number"==typeof o&&(e.minWdt=Number(o))}var r=WK(t,"heightConstraint");if("number"==typeof r)e.minHgt=Number(r);else if("object"===sW(r)){var s=WK(t,["heightConstraint","minimum"]);"number"==typeof s&&(e.minHgt=Number(s));var a=WK(t,["heightConstraint","valign"]);"string"==typeof a&&("top"!==a&&"bottom"!==a||(e.valign=a))}return e}},{key:"update",value:function(t,e){this.setOptions(t,!0),this.propagateFonts(e),TK(this.fontOptions,this.constrain(e)),this.fontOptions.chooser=h0("label",e)}},{key:"adjustSizes",value:function(t){var e=t?t.right+t.left:0;this.fontOptions.constrainWidth&&(this.fontOptions.maxWdt-=e,this.fontOptions.minWdt-=e);var i=t?t.top+t.bottom:0;this.fontOptions.constrainHeight&&(this.fontOptions.minHgt-=i)}},{key:"addFontOptionsToPile",value:function(t,e){for(var i=0;i5&&void 0!==arguments[5]?arguments[5]:"middle";if(void 0!==this.elementOptions.label){var s=this.fontOptions.size*this.body.view.scale;this.elementOptions.label&&s=this.elementOptions.scaling.label.maxVisible&&(s=Number(this.elementOptions.scaling.label.maxVisible)/this.body.view.scale),this.calculateLabelSize(t,n,o,e,i,r),this._drawBackground(t),this._drawText(t,e,this.size.yLine,r,s))}}},{key:"_drawBackground",value:function(t){if(void 0!==this.fontOptions.background&&"none"!==this.fontOptions.background){t.fillStyle=this.fontOptions.background;var e=this.getSize();t.fillRect(e.left,e.top,e.width,e.height)}}},{key:"_drawText",value:function(t,e,i){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"middle",o=arguments.length>4?arguments[4]:void 0,r=QW(this._setAlignment(t,e,i,n),2);e=r[0],i=r[1],t.textAlign="left",e-=this.size.width/2,this.fontOptions.valign&&this.size.height>this.size.labelHeight&&("top"===this.fontOptions.valign&&(i-=(this.size.height-this.size.labelHeight)/2),"bottom"===this.fontOptions.valign&&(i+=(this.size.height-this.size.labelHeight)/2));for(var s=0;s0&&(t.lineWidth=c.strokeWidth,t.strokeStyle=p,t.lineJoin="round"),t.fillStyle=u,c.strokeWidth>0&&t.strokeText(c.text,e+d,i+c.vadjust),t.fillText(c.text,e+d,i+c.vadjust),d+=c.width}i+=a.height}}}},{key:"_setAlignment",value:function(t,e,i,n){if(this.isEdgeLabel&&"horizontal"!==this.fontOptions.align&&!1===this.pointToSelf){e=0,i=0;"top"===this.fontOptions.align?(t.textBaseline="alphabetic",i-=4):"bottom"===this.fontOptions.align?(t.textBaseline="hanging",i+=4):t.textBaseline="middle"}else t.textBaseline=n;return[e,i]}},{key:"_getColor",value:function(t,e,i){var n=t||"#000000",o=i||"#ffffff";if(e<=this.elementOptions.scaling.label.drawThreshold){var r=Math.max(0,Math.min(1,1-(this.elementOptions.scaling.label.drawThreshold-e)));n=DK(n,r),o=DK(o,r)}return[n,o]}},{key:"getTextSize",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return this._processLabel(t,e,i),{width:this.size.width,height:this.size.height,lineCount:this.lineCount}}},{key:"getSize",value:function(){var t=this.size.left,e=this.size.top-1;if(this.isEdgeLabel){var i=.5*-this.size.width;switch(this.fontOptions.align){case"middle":t=i,e=.5*-this.size.height;break;case"top":t=i,e=-(this.size.height+2);break;case"bottom":t=i,e=2}}return{left:t,top:e,width:this.size.width,height:this.size.height}}},{key:"calculateLabelSize",value:function(t,e,i){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:0,o=arguments.length>4&&void 0!==arguments[4]?arguments[4]:0,r=arguments.length>5&&void 0!==arguments[5]?arguments[5]:"middle";this._processLabel(t,e,i),this.size.left=n-.5*this.size.width,this.size.top=o-.5*this.size.height,this.size.yLine=o+.5*(1-this.lineCount)*this.fontOptions.size,"hanging"===r&&(this.size.top+=.5*this.fontOptions.size,this.size.top+=4,this.size.yLine+=4)}},{key:"getFormattingValues",value:function(t,e,i,n){var o=function(t,e,i){return"normal"===e?"mod"===i?"":t[i]:void 0!==t[e][i]?t[e][i]:t[i]},r={color:o(this.fontOptions,n,"color"),size:o(this.fontOptions,n,"size"),face:o(this.fontOptions,n,"face"),mod:o(this.fontOptions,n,"mod"),vadjust:o(this.fontOptions,n,"vadjust"),strokeWidth:this.fontOptions.strokeWidth,strokeColor:this.fontOptions.strokeColor};(e||i)&&("normal"===n&&!0===this.fontOptions.chooser&&this.elementOptions.labelHighlightBold?r.mod="bold":"function"==typeof this.fontOptions.chooser&&this.fontOptions.chooser(r,this.elementOptions.id,e,i));var s="";return void 0!==r.mod&&""!==r.mod&&(s+=r.mod+" "),s+=r.size+"px "+r.face,t.font=s.replace(/"/g,""),r.font=t.font,r.height=r.size,r}},{key:"differentState",value:function(t,e){return t!==this.selectedState||e!==this.hoverState}},{key:"_processLabelText",value:function(t,e,i,n){return new S0(t,this,e,i).process(n)}},{key:"_processLabel",value:function(t,e,i){if(!1!==this.labelDirty||this.differentState(e,i)){var n=this._processLabelText(t,e,i,this.elementOptions.label);this.fontOptions.minWdt>0&&n.width0&&n.height0&&(this.enableBorderDashes(t,e),t.stroke(),this.disableBorderDashes(t,e)),t.restore()}},{key:"performFill",value:function(t,e){t.save(),t.fillStyle=e.color,this.enableShadow(t,e),vY(t).call(t),this.disableShadow(t,e),t.restore(),this.performStroke(t,e)}},{key:"_addBoundingBoxMargin",value:function(t){this.boundingBox.left-=t,this.boundingBox.top-=t,this.boundingBox.bottom+=t,this.boundingBox.right+=t}},{key:"_updateBoundingBox",value:function(t,e,i,n,o){void 0!==i&&this.resize(i,n,o),this.left=t-this.width/2,this.top=e-this.height/2,this.boundingBox.left=this.left,this.boundingBox.top=this.top,this.boundingBox.bottom=this.top+this.height,this.boundingBox.right=this.left+this.width}},{key:"updateBoundingBox",value:function(t,e,i,n,o){this._updateBoundingBox(t,e,i,n,o)}},{key:"getDimensionsFromLabel",value:function(t,e,i){this.textSize=this.labelModule.getTextSize(t,e,i);var n=this.textSize.width,o=this.textSize.height;return 0===n&&(n=14,o=14),{width:n,height:o}}}]),t}();function n1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var o1=function(t){Q0(i,t);var e=n1(i);function i(t,n,o){var r;return rj(this,i),(r=e.call(this,t,n,o))._setMargins(o),r}return cW(i,[{key:"resize",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(this.needsRefresh(e,i)){var n=this.getDimensionsFromLabel(t,e,i);this.width=n.width+this.margin.right+this.margin.left,this.height=n.height+this.margin.top+this.margin.bottom,this.radius=this.width/2}}},{key:"draw",value:function(t,e,i,n,o,r){this.resize(t,n,o),this.left=e-this.width/2,this.top=i-this.height/2,this.initContextForDraw(t,r),BF(t,this.left,this.top,this.width,this.height,r.borderRadius),this.performFill(t,r),this.updateBoundingBox(e,i,t,n,o),this.labelModule.draw(t,this.left+this.textSize.width/2+this.margin.left,this.top+this.textSize.height/2+this.margin.top,n,o)}},{key:"updateBoundingBox",value:function(t,e,i,n,o){this._updateBoundingBox(t,e,i,n,o);var r=this.options.shapeProperties.borderRadius;this._addBoundingBoxMargin(r)}},{key:"distanceToBorder",value:function(t,e){t&&this.resize(t);var i=this.options.borderWidth;return Math.min(Math.abs(this.width/2/Math.cos(e)),Math.abs(this.height/2/Math.sin(e)))+i}}]),i}(i1);function r1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var s1=function(t){Q0(i,t);var e=r1(i);function i(t,n,o){var r;return rj(this,i),(r=e.call(this,t,n,o)).labelOffset=0,r.selected=!1,r}return cW(i,[{key:"setOptions",value:function(t,e,i){this.options=t,void 0===e&&void 0===i||this.setImages(e,i)}},{key:"setImages",value:function(t,e){e&&this.selected?(this.imageObj=e,this.imageObjAlt=t):(this.imageObj=t,this.imageObjAlt=e)}},{key:"switchImages",value:function(t){var e=t&&!this.selected||!t&&this.selected;if(this.selected=t,void 0!==this.imageObjAlt&&e){var i=this.imageObj;this.imageObj=this.imageObjAlt,this.imageObjAlt=i}}},{key:"_getImagePadding",value:function(){var t={top:0,right:0,bottom:0,left:0};if(this.options.imagePadding){var e=this.options.imagePadding;"object"==sW(e)?(t.top=e.top,t.right=e.right,t.bottom=e.bottom,t.left=e.left):(t.top=e,t.right=e,t.bottom=e,t.left=e)}return t}},{key:"_resizeImage",value:function(){var t,e;if(!1===this.options.shapeProperties.useImageSize){var i=1,n=1;this.imageObj.width&&this.imageObj.height&&(this.imageObj.width>this.imageObj.height?i=this.imageObj.width/this.imageObj.height:n=this.imageObj.height/this.imageObj.width),t=2*this.options.size*i,e=2*this.options.size*n}else{var o=this._getImagePadding();t=this.imageObj.width+o.left+o.right,e=this.imageObj.height+o.top+o.bottom}this.width=t,this.height=e,this.radius=.5*this.width}},{key:"_drawRawCircle",value:function(t,e,i,n){this.initContextForDraw(t,n),NF(t,e,i,n.size),this.performFill(t,n)}},{key:"_drawImageAtPosition",value:function(t,e){if(0!=this.imageObj.width){t.globalAlpha=void 0!==e.opacity?e.opacity:1,this.enableShadow(t,e);var i=1;!0===this.options.shapeProperties.interpolation&&(i=this.imageObj.width/this.width/this.body.view.scale);var n=this._getImagePadding(),o=this.left+n.left,r=this.top+n.top,s=this.width-n.left-n.right,a=this.height-n.top-n.bottom;this.imageObj.drawImageAtPosition(t,i,o,r,s,a),this.disableShadow(t,e)}}},{key:"_drawImageLabel",value:function(t,e,i,n,o){var r=0;if(void 0!==this.height){r=.5*this.height;var s=this.labelModule.getTextSize(t,n,o);s.lineCount>=1&&(r+=s.height/2)}var a=i+r;this.options.label&&(this.labelOffset=r),this.labelModule.draw(t,e,a,n,o,"hanging")}}]),i}(i1);function a1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var d1=function(t){Q0(i,t);var e=a1(i);function i(t,n,o){var r;return rj(this,i),(r=e.call(this,t,n,o))._setMargins(o),r}return cW(i,[{key:"resize",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(this.needsRefresh(e,i)){var n=this.getDimensionsFromLabel(t,e,i),o=Math.max(n.width+this.margin.right+this.margin.left,n.height+this.margin.top+this.margin.bottom);this.options.size=o/2,this.width=o,this.height=o,this.radius=this.width/2}}},{key:"draw",value:function(t,e,i,n,o,r){this.resize(t,n,o),this.left=e-this.width/2,this.top=i-this.height/2,this._drawRawCircle(t,e,i,r),this.updateBoundingBox(e,i),this.labelModule.draw(t,this.left+this.textSize.width/2+this.margin.left,i,n,o)}},{key:"updateBoundingBox",value:function(t,e){this.boundingBox.top=e-this.options.size,this.boundingBox.left=t-this.options.size,this.boundingBox.right=t+this.options.size,this.boundingBox.bottom=e+this.options.size}},{key:"distanceToBorder",value:function(t){return t&&this.resize(t),.5*this.width}}]),i}(s1);function l1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var c1=function(t){Q0(i,t);var e=l1(i);function i(t,n,o,r,s){var a;return rj(this,i),(a=e.call(this,t,n,o)).setImages(r,s),a}return cW(i,[{key:"resize",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(void 0===this.imageObj.src||void 0===this.imageObj.width||void 0===this.imageObj.height){var n=2*this.options.size;return this.width=n,this.height=n,void(this.radius=.5*this.width)}this.needsRefresh(e,i)&&this._resizeImage()}},{key:"draw",value:function(t,e,i,n,o,r){this.switchImages(n),this.resize();var s=e,a=i;"top-left"===this.options.shapeProperties.coordinateOrigin?(this.left=e,this.top=i,s+=this.width/2,a+=this.height/2):(this.left=e-this.width/2,this.top=i-this.height/2),this._drawRawCircle(t,s,a,r),t.save(),t.clip(),this._drawImageAtPosition(t,r),t.restore(),this._drawImageLabel(t,s,a,n,o),this.updateBoundingBox(e,i)}},{key:"updateBoundingBox",value:function(t,e){"top-left"===this.options.shapeProperties.coordinateOrigin?(this.boundingBox.top=e,this.boundingBox.left=t,this.boundingBox.right=t+2*this.options.size,this.boundingBox.bottom=e+2*this.options.size):(this.boundingBox.top=e-this.options.size,this.boundingBox.left=t-this.options.size,this.boundingBox.right=t+this.options.size,this.boundingBox.bottom=e+this.options.size),this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelOffset)}},{key:"distanceToBorder",value:function(t){return t&&this.resize(t),.5*this.width}}]),i}(s1);function h1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var u1=function(t){Q0(i,t);var e=h1(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"resize",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover,n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{size:this.options.size};if(this.needsRefresh(e,i)){var o,r;this.labelModule.getTextSize(t,e,i);var s=2*n.size;this.width=null!==(o=this.customSizeWidth)&&void 0!==o?o:s,this.height=null!==(r=this.customSizeHeight)&&void 0!==r?r:s,this.radius=.5*this.width}}},{key:"_drawShape",value:function(t,e,i,n,o,r,s,a){var d,l=this;return this.resize(t,r,s,a),this.left=n-this.width/2,this.top=o-this.height/2,this.initContextForDraw(t,a),(d=e,Object.prototype.hasOwnProperty.call(HF,d)?HF[d]:function(t){for(var e=arguments.length,i=new Array(e>1?e-1:0),n=1;n0&&(this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelModule.size.height))}}]),i}(i1);function p1(t,e){var i=kV(t);if(EJ){var n=EJ(t);e&&(n=Jq(n).call(n,(function(e){return DJ(t,e).enumerable}))),i.push.apply(i,n)}return i}function f1(t){for(var e=1;e1&&void 0!==arguments[1]?arguments[1]:this.selected,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(this.needsRefresh(e,i)){var n=this.getDimensionsFromLabel(t,e,i);this.height=2*n.height,this.width=n.width+n.height,this.radius=.5*this.width}}},{key:"draw",value:function(t,e,i,n,o,r){this.resize(t,n,o),this.left=e-.5*this.width,this.top=i-.5*this.height,this.initContextForDraw(t,r),LF(t,this.left,this.top,this.width,this.height),this.performFill(t,r),this.updateBoundingBox(e,i,t,n,o),this.labelModule.draw(t,e,i,n,o)}},{key:"distanceToBorder",value:function(t,e){t&&this.resize(t);var i=.5*this.width,n=.5*this.height,o=Math.sin(e)*i,r=Math.cos(e)*n;return i*n/Math.sqrt(o*o+r*r)}}]),i}(i1);function O1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var C1=function(t){Q0(i,t);var e=O1(i);function i(t,n,o){var r;return rj(this,i),(r=e.call(this,t,n,o))._setMargins(o),r}return cW(i,[{key:"resize",value:function(t,e,i){this.needsRefresh(e,i)&&(this.iconSize={width:Number(this.options.icon.size),height:Number(this.options.icon.size)},this.width=this.iconSize.width+this.margin.right+this.margin.left,this.height=this.iconSize.height+this.margin.top+this.margin.bottom,this.radius=.5*this.width)}},{key:"draw",value:function(t,e,i,n,o,r){var s=this;return this.resize(t,n,o),this.options.icon.size=this.options.icon.size||50,this.left=e-this.width/2,this.top=i-this.height/2,this._icon(t,e,i,n,o,r),{drawExternalLabel:function(){if(void 0!==s.options.label){s.labelModule.draw(t,s.left+s.iconSize.width/2+s.margin.left,i+s.height/2+5,n)}s.updateBoundingBox(e,i)}}}},{key:"updateBoundingBox",value:function(t,e){if(this.boundingBox.top=e-.5*this.options.icon.size,this.boundingBox.left=t-.5*this.options.icon.size,this.boundingBox.right=t+.5*this.options.icon.size,this.boundingBox.bottom=e+.5*this.options.icon.size,void 0!==this.options.label&&this.labelModule.size.width>0){this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelModule.size.height+5)}}},{key:"_icon",value:function(t,e,i,n,o,r){var s=Number(this.options.icon.size);void 0!==this.options.icon.code?(t.font=[null!=this.options.icon.weight?this.options.icon.weight:n?"bold":"",(null!=this.options.icon.weight&&n?5:0)+s+"px",this.options.icon.face].join(" "),t.fillStyle=this.options.icon.color||"black",t.textAlign="center",t.textBaseline="middle",this.enableShadow(t,r),t.fillText(this.options.icon.code,e,i),this.disableShadow(t,r)):console.error("When using the icon shape, you need to define the code in the icon options object. This can be done per node or globally.")}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(i1);function S1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var T1=function(t){Q0(i,t);var e=S1(i);function i(t,n,o,r,s){var a;return rj(this,i),(a=e.call(this,t,n,o)).setImages(r,s),a}return cW(i,[{key:"resize",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.selected,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:this.hover;if(void 0===this.imageObj.src||void 0===this.imageObj.width||void 0===this.imageObj.height){var n=2*this.options.size;return this.width=n,void(this.height=n)}this.needsRefresh(e,i)&&this._resizeImage()}},{key:"draw",value:function(t,e,i,n,o,r){t.save(),this.switchImages(n),this.resize();var s=e,a=i;if("top-left"===this.options.shapeProperties.coordinateOrigin?(this.left=e,this.top=i,s+=this.width/2,a+=this.height/2):(this.left=e-this.width/2,this.top=i-this.height/2),!0===this.options.shapeProperties.useBorderWithImage){var d=this.options.borderWidth,l=this.options.borderWidthSelected||2*this.options.borderWidth,c=(n?l:d)/this.body.view.scale;t.lineWidth=Math.min(this.width,c),t.beginPath();var h=n?this.options.color.highlight.border:o?this.options.color.hover.border:this.options.color.border,u=n?this.options.color.highlight.background:o?this.options.color.hover.background:this.options.color.background;void 0!==r.opacity&&(h=DK(h,r.opacity),u=DK(u,r.opacity)),t.strokeStyle=h,t.fillStyle=u,t.rect(this.left-.5*t.lineWidth,this.top-.5*t.lineWidth,this.width+t.lineWidth,this.height+t.lineWidth),vY(t).call(t),this.performStroke(t,r),t.closePath()}this._drawImageAtPosition(t,r),this._drawImageLabel(t,s,a,n,o),this.updateBoundingBox(e,i),t.restore()}},{key:"updateBoundingBox",value:function(t,e){this.resize(),"top-left"===this.options.shapeProperties.coordinateOrigin?(this.left=t,this.top=e):(this.left=t-this.width/2,this.top=e-this.height/2),this.boundingBox.left=this.left,this.boundingBox.top=this.top,this.boundingBox.bottom=this.top+this.height,this.boundingBox.right=this.left+this.width,void 0!==this.options.label&&this.labelModule.size.width>0&&(this.boundingBox.left=Math.min(this.boundingBox.left,this.labelModule.size.left),this.boundingBox.right=Math.max(this.boundingBox.right,this.labelModule.size.left+this.labelModule.size.width),this.boundingBox.bottom=Math.max(this.boundingBox.bottom,this.boundingBox.bottom+this.labelOffset))}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(s1);function I1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var A1=function(t){Q0(i,t);var e=I1(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"draw",value:function(t,e,i,n,o,r){return this._drawShape(t,"square",2,e,i,n,o,r)}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(u1);function R1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var P1=function(t){Q0(i,t);var e=R1(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"draw",value:function(t,e,i,n,o,r){return this._drawShape(t,"hexagon",4,e,i,n,o,r)}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(u1);function D1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var M1=function(t){Q0(i,t);var e=D1(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"draw",value:function(t,e,i,n,o,r){return this._drawShape(t,"star",4,e,i,n,o,r)}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(u1);function F1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var N1=function(t){Q0(i,t);var e=F1(i);function i(t,n,o){var r;return rj(this,i),(r=e.call(this,t,n,o))._setMargins(o),r}return cW(i,[{key:"resize",value:function(t,e,i){this.needsRefresh(e,i)&&(this.textSize=this.labelModule.getTextSize(t,e,i),this.width=this.textSize.width+this.margin.right+this.margin.left,this.height=this.textSize.height+this.margin.top+this.margin.bottom,this.radius=.5*this.width)}},{key:"draw",value:function(t,e,i,n,o,r){this.resize(t,n,o),this.left=e-this.width/2,this.top=i-this.height/2,this.enableShadow(t,r),this.labelModule.draw(t,this.left+this.textSize.width/2+this.margin.left,this.top+this.textSize.height/2+this.margin.top,n,o),this.disableShadow(t,r),this.updateBoundingBox(e,i,t,n,o)}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(i1);function B1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var L1=function(t){Q0(i,t);var e=B1(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"draw",value:function(t,e,i,n,o,r){return this._drawShape(t,"triangle",3,e,i,n,o,r)}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(u1);function z1(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var j1=function(t){Q0(i,t);var e=z1(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"draw",value:function(t,e,i,n,o,r){return this._drawShape(t,"triangleDown",3,e,i,n,o,r)}},{key:"distanceToBorder",value:function(t,e){return this._distanceToBorder(t,e)}}]),i}(u1);function H1(t,e){var i=kV(t);if(EJ){var n=EJ(t);e&&(n=Jq(n).call(n,(function(e){return DJ(t,e).enumerable}))),i.push.apply(i,n)}return i}function $1(t){for(var e=1;et.left&&this.shape.topt.top}},{key:"isBoundingBoxOverlappingWith",value:function(t){return this.shape.boundingBox.leftt.left&&this.shape.boundingBox.topt.top}}],[{key:"checkOpacity",value:function(t){return 0<=t&&t<=1}},{key:"checkCoordinateOrigin",value:function(t){return void 0===t||"center"===t||"top-left"===t}},{key:"updateGroupOptions",value:function(e,i,n){var o;if(void 0!==n){var r=e.group;if(void 0!==i&&void 0!==i.group&&r!==i.group)throw new Error("updateGroupOptions: group values in options don't match.");if("number"==typeof r||"string"==typeof r&&""!=r){var s=n.get(r);void 0!==s.opacity&&void 0===i.opacity&&(t.checkOpacity(s.opacity)||(console.error("Invalid option for node opacity. Value must be between 0 and 1, found: "+s.opacity),s.opacity=void 0));var a=Jq(o=c0(i)).call(o,(function(t){return null!=i[t]}));a.push("font"),SK(a,e,s),e.color=FK(e.color)}}}},{key:"parseOptions",value:function(e,i){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2],o=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},r=arguments.length>4?arguments[4]:void 0;if(SK(["color","fixed","shadow"],e,i,n),t.checkMass(i),void 0!==e.opacity&&(t.checkOpacity(e.opacity)||(console.error("Invalid option for node opacity. Value must be between 0 and 1, found: "+e.opacity),e.opacity=void 0)),void 0!==i.opacity&&(t.checkOpacity(i.opacity)||(console.error("Invalid option for node opacity. Value must be between 0 and 1, found: "+i.opacity),i.opacity=void 0)),i.shapeProperties&&!t.checkCoordinateOrigin(i.shapeProperties.coordinateOrigin)&&console.error("Invalid option for node coordinateOrigin, found: "+i.shapeProperties.coordinateOrigin),$K(e,i,"shadow",o),void 0!==i.color&&null!==i.color){var s=FK(i.color);OK(e.color,s)}else!0===n&&null===i.color&&(e.color=HK(o.color));void 0!==i.fixed&&null!==i.fixed&&("boolean"==typeof i.fixed?(e.fixed.x=i.fixed,e.fixed.y=i.fixed):(void 0!==i.fixed.x&&"boolean"==typeof i.fixed.x&&(e.fixed.x=i.fixed.x),void 0!==i.fixed.y&&"boolean"==typeof i.fixed.y&&(e.fixed.y=i.fixed.y))),!0===n&&null===i.font&&(e.font=HK(o.font)),t.updateGroupOptions(e,i,r),void 0!==i.scaling&&$K(e.scaling,i.scaling,"label",o.scaling)}},{key:"checkMass",value:function(t,e){if(void 0!==t.mass&&t.mass<=0){var i="";void 0!==e&&(i=" in node id: "+e),console.error("%cNegative or zero mass disallowed"+i+", setting mass to 1.",oZ),t.mass=1}}}]),t}();function W1(t,e){var i=void 0!==tV&&oj(t)||t["@@iterator"];if(!i){if(fV(t)||(i=function(t,e){var i;if(!t)return;if("string"==typeof t)return V1(t,e);var n=aV(i=Object.prototype.toString.call(t)).call(i,8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Bz(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return V1(t,e)}(t))||e){i&&(t=i);var n=0,o=function(){};return{s:o,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function V1(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i1?console.error("Invalid option for node opacity. Value must be between 0 and 1, found: "+t.opacity):this.options.opacity=t.opacity),void 0!==t.shape)for(var e in this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,e)&&this.body.nodes[e].updateShape();if(void 0!==t.font||void 0!==t.widthConstraint||void 0!==t.heightConstraint)for(var i=0,n=kV(this.body.nodes);i1&&void 0!==arguments[1]&&arguments[1],i=this.body.data.nodes;if(YA("id",t))this.body.data.nodes=t;else if(fV(t))this.body.data.nodes=new XA,this.body.data.nodes.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.body.data.nodes=new XA}if(i&&RK(this.nodesListeners,(function(t,e){i.off(e,t)})),this.body.nodes={},this.body.data.nodes){var n=this;RK(this.nodesListeners,(function(t,e){n.body.data.nodes.on(e,t)}));var o=this.body.data.nodes.getIds();this.add(o,!0)}!1===e&&this.body.emitter.emit("_dataChanged")}},{key:"add",value:function(t){for(var e,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=[],o=0;o1&&void 0!==arguments[1]?arguments[1]:U1)(t,this.body,this.images,this.groups,this.options,this.defaultOptions)}},{key:"refresh",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]&&arguments[0];RK(this.body.nodes,(function(i,n){var o=t.body.data.nodes.get(n);void 0!==o&&(!0===e&&i.setOptions({x:null,y:null}),i.setOptions({fixed:!1}),i.setOptions(o))}))}},{key:"getPositions",value:function(t){var e={};if(void 0!==t){if(!0===fV(t)){for(var i=0;i0?(n=i/a)*n:i;return a===1/0?1/0:a*s2(o)}});var a2=KA(GR.Math.hypot);function d2(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var l2=function(){function t(){rj(this,t)}return cW(t,null,[{key:"transform",value:function(t,e){fV(t)||(t=[t]);for(var i=e.point.x,n=e.point.y,o=e.angle,r=e.length,s=0;s4&&void 0!==arguments[4]?arguments[4]:this.getViaNode();t.strokeStyle=this.getColor(t,e),t.lineWidth=e.width,!1!==e.dashes?this._drawDashedLine(t,e,o):this._drawLine(t,e,o)}},{key:"_drawLine",value:function(t,e,i,n,o){if(this.from!=this.to)this._line(t,e,i,n,o);else{var r=QW(this._getCircleData(t),3),s=r[0],a=r[1],d=r[2];this._circle(t,e,s,a,d)}}},{key:"_drawDashedLine",value:function(t,e,i,n,o){t.lineCap="round";var r=fV(e.dashes)?e.dashes:[5,5];if(void 0!==t.setLineDash){if(t.save(),t.setLineDash(r),t.lineDashOffset=0,this.from!=this.to)this._line(t,e,i);else{var s=QW(this._getCircleData(t),3),a=s[0],d=s[1],l=s[2];this._circle(t,e,a,d,l)}t.setLineDash([0]),t.lineDashOffset=0,t.restore()}else{if(this.from!=this.to)jF(t,this.from.x,this.from.y,this.to.x,this.to.y,r);else{var c=QW(this._getCircleData(t),3),h=c[0],u=c[1],p=c[2];this._circle(t,e,h,u,p)}this.enableShadow(t,e),t.stroke(),this.disableShadow(t,e)}}},{key:"findBorderPosition",value:function(t,e,i){return this.from!=this.to?this._findBorderPosition(t,e,i):this._findBorderPositionCircle(t,e,i)}},{key:"findBorderPositions",value:function(t){if(this.from!=this.to)return{from:this._findBorderPosition(this.from,t),to:this._findBorderPosition(this.to,t)};var e,i=QW(aV(e=this._getCircleData(t)).call(e,0,2),2),n=i[0],o=i[1];return{from:this._findBorderPositionCircle(this.from,t,{x:n,y:o,low:.25,high:.6,direction:-1}),to:this._findBorderPositionCircle(this.from,t,{x:n,y:o,low:.6,high:.8,direction:1})}}},{key:"_getCircleData",value:function(t){var e=this.options.selfReference.size;void 0!==t&&void 0===this.from.shape.width&&this.from.shape.resize(t);var i=f0(t,this.options.selfReference.angle,e,this.from);return[i.x,i.y,e]}},{key:"_pointOnCircle",value:function(t,e,i,n){var o=2*n*Math.PI;return{x:t+i*Math.cos(o),y:e-i*Math.sin(o)}}},{key:"_findBorderPositionCircle",value:function(t,e,i){var n,o=i.x,r=i.y,s=i.low,a=i.high,d=i.direction,l=this.options.selfReference.size,c=.5*(s+a),h=0;!0===this.options.arrowStrikethrough&&(-1===d?h=this.options.endPointOffset.from:1===d&&(h=this.options.endPointOffset.to));var u=0;do{c=.5*(s+a),n=this._pointOnCircle(o,r,l,c);var p=Math.atan2(t.y-n.y,t.x-n.x),f=t.distanceToBorder(e,p)+h-Math.sqrt(Math.pow(n.x-t.x,2)+Math.pow(n.y-t.y,2));if(Math.abs(f)<.05)break;f>0?d>0?s=c:a=c:d>0?a=c:s=c,++u}while(s<=a&&u<10);return k2(k2({},n),{},{t:c})}},{key:"getLineWidth",value:function(t,e){return!0===t?Math.max(this.selectionWidth,.3/this._body.view.scale):!0===e?Math.max(this.hoverWidth,.3/this._body.view.scale):Math.max(this.options.width,.3/this._body.view.scale)}},{key:"getColor",value:function(t,e){if(!1!==e.inheritsColor){if("both"===e.inheritsColor&&this.from.id!==this.to.id){var i=t.createLinearGradient(this.from.x,this.from.y,this.to.x,this.to.y),n=this.from.options.color.highlight.border,o=this.to.options.color.highlight.border;return!1===this.from.selected&&!1===this.to.selected?(n=DK(this.from.options.color.border,e.opacity),o=DK(this.to.options.color.border,e.opacity)):!0===this.from.selected&&!1===this.to.selected?o=this.to.options.color.border:!1===this.from.selected&&!0===this.to.selected&&(n=this.from.options.color.border),i.addColorStop(0,n),i.addColorStop(1,o),i}return"to"===e.inheritsColor?DK(this.to.options.color.border,e.opacity):DK(this.from.options.color.border,e.opacity)}return DK(e.color,e.opacity)}},{key:"_circle",value:function(t,e,i,n,o){this.enableShadow(t,e);var r=0,s=2*Math.PI;if(!this.options.selfReference.renderBehindTheNode){var a=this.options.selfReference.angle,d=this.options.selfReference.angle+Math.PI,l=this._findBorderPositionCircle(this.from,t,{x:i,y:n,low:a,high:d,direction:-1}),c=this._findBorderPositionCircle(this.from,t,{x:i,y:n,low:a,high:d,direction:1});r=Math.atan2(l.y-n,l.x-i),s=Math.atan2(c.y-n,c.x-i)}t.beginPath(),t.arc(i,n,o,r,s,!1),t.stroke(),this.disableShadow(t,e)}},{key:"getDistanceToEdge",value:function(t,e,i,n,o,r){if(this.from!=this.to)return this._getDistanceToEdge(t,e,i,n,o,r);var s=QW(this._getCircleData(void 0),3),a=s[0],d=s[1],l=s[2],c=a-o,h=d-r;return Math.abs(Math.sqrt(c*c+h*h)-l)}},{key:"_getDistanceToLine",value:function(t,e,i,n,o,r){var s=i-t,a=n-e,d=((o-t)*s+(r-e)*a)/(s*s+a*a);d>1?d=1:d<0&&(d=0);var l=t+d*s-o,c=e+d*a-r;return Math.sqrt(l*l+c*c)}},{key:"getArrowData",value:function(t,e,i,n,o,r){var s,a,d,l,c,h,u,p=r.width;"from"===e?(d=this.from,l=this.to,c=r.fromArrowScale<0,h=Math.abs(r.fromArrowScale),u=r.fromArrowType):"to"===e?(d=this.to,l=this.from,c=r.toArrowScale<0,h=Math.abs(r.toArrowScale),u=r.toArrowType):(d=this.to,l=this.from,c=r.middleArrowScale<0,h=Math.abs(r.middleArrowScale),u=r.middleArrowType);var f=15*h+3*p;if(d!=l){var m=f/a2(d.x-l.x,d.y-l.y);if("middle"!==e)if(!0===this.options.smooth.enabled){var v=this._findBorderPosition(d,t,{via:i}),g=this.getPoint(v.t+m*("from"===e?1:-1),i);s=Math.atan2(v.y-g.y,v.x-g.x),a=v}else s=Math.atan2(d.y-l.y,d.x-l.x),a=this._findBorderPosition(d,t);else{var y=(c?-m:m)/2,b=this.getPoint(.5+y,i),x=this.getPoint(.5-y,i);s=Math.atan2(b.y-x.y,b.x-x.x),a=this.getPoint(.5,i)}}else{var _=QW(this._getCircleData(t),3),w=_[0],E=_[1],k=_[2];if("from"===e){var O=this.options.selfReference.angle,C=this.options.selfReference.angle+Math.PI,S=this._findBorderPositionCircle(this.from,t,{x:w,y:E,low:O,high:C,direction:-1});s=-2*S.t*Math.PI+1.5*Math.PI+.1*Math.PI,a=S}else if("to"===e){var T=this.options.selfReference.angle,I=this.options.selfReference.angle+Math.PI,A=this._findBorderPositionCircle(this.from,t,{x:w,y:E,low:T,high:I,direction:1});s=-2*A.t*Math.PI+1.5*Math.PI-1.1*Math.PI,a=A}else{var R=this.options.selfReference.angle/(2*Math.PI);a=this._pointOnCircle(w,E,k,R),s=-2*R*Math.PI+1.5*Math.PI+.1*Math.PI}}return{point:a,core:{x:a.x-.9*f*Math.cos(s),y:a.y-.9*f*Math.sin(s)},angle:s,length:f,type:u}}},{key:"drawArrowHead",value:function(t,e,i,n,o){t.strokeStyle=this.getColor(t,e),t.fillStyle=t.strokeStyle,t.lineWidth=e.width,w2.draw(t,o)&&(this.enableShadow(t,e),vY(t).call(t),this.disableShadow(t,e))}},{key:"enableShadow",value:function(t,e){!0===e.shadow&&(t.shadowColor=e.shadowColor,t.shadowBlur=e.shadowSize,t.shadowOffsetX=e.shadowX,t.shadowOffsetY=e.shadowY)}},{key:"disableShadow",value:function(t,e){!0===e.shadow&&(t.shadowColor="rgba(0,0,0,0)",t.shadowBlur=0,t.shadowOffsetX=0,t.shadowOffsetY=0)}},{key:"drawBackground",value:function(t,e){if(!1!==e.background){var i={strokeStyle:t.strokeStyle,lineWidth:t.lineWidth,dashes:t.dashes};t.strokeStyle=e.backgroundColor,t.lineWidth=e.backgroundSize,this.setStrokeDashed(t,e.backgroundDashes),t.stroke(),t.strokeStyle=i.strokeStyle,t.lineWidth=i.lineWidth,t.dashes=i.dashes,this.setStrokeDashed(t,e.dashes)}}},{key:"setStrokeDashed",value:function(t,e){if(!1!==e)if(void 0!==t.setLineDash){var i=fV(e)?e:[5,5];t.setLineDash(i)}else console.warn("setLineDash is not supported in this browser. The dashed stroke cannot be used.");else void 0!==t.setLineDash?t.setLineDash([]):console.warn("setLineDash is not supported in this browser. The dashed stroke cannot be used.")}}]),t}();function C2(t,e){var i=kV(t);if(EJ){var n=EJ(t);e&&(n=Jq(n).call(n,(function(e){return DJ(t,e).enumerable}))),i.push.apply(i,n)}return i}function S2(t){for(var e=1;e2&&void 0!==arguments[2]?arguments[2]:this._getViaCoordinates(),r=!1,s=1,a=0,d=this.to,l=this.options.endPointOffset?this.options.endPointOffset.to:0;t.id===this.from.id&&(d=this.from,r=!0,l=this.options.endPointOffset?this.options.endPointOffset.from:0),!1===this.options.arrowStrikethrough&&(l=0);var c=0;do{n=.5*(a+s),i=this.getPoint(n,o);var h=Math.atan2(d.y-i.y,d.x-i.x),u=d.distanceToBorder(e,h)+l-Math.sqrt(Math.pow(i.x-d.x,2)+Math.pow(i.y-d.y,2));if(Math.abs(u)<.2)break;u<0?!1===r?a=n:s=n:!1===r?s=n:a=n,++c}while(a<=s&&c<10);return S2(S2({},i),{},{t:n})}},{key:"_getDistanceToBezierEdge",value:function(t,e,i,n,o,r,s){var a,d,l,c,h,u=1e9,p=t,f=e;for(d=1;d<10;d++)l=.1*d,c=Math.pow(1-l,2)*t+2*l*(1-l)*s.x+Math.pow(l,2)*i,h=Math.pow(1-l,2)*e+2*l*(1-l)*s.y+Math.pow(l,2)*n,d>0&&(u=(a=this._getDistanceToLine(p,f,c,h,o,r))1&&void 0!==arguments[1]?arguments[1]:this.via;if(this.from===this.to){var i=QW(this._getCircleData(),3),n=i[0],o=i[1],r=i[2],s=2*Math.PI*(1-t);return{x:n+r*Math.sin(s),y:o+r-r*(1-Math.cos(s))}}return{x:Math.pow(1-t,2)*this.fromPoint.x+2*t*(1-t)*e.x+Math.pow(t,2)*this.toPoint.x,y:Math.pow(1-t,2)*this.fromPoint.y+2*t*(1-t)*e.y+Math.pow(t,2)*this.toPoint.y}}},{key:"_findBorderPosition",value:function(t,e){return this._findBorderPositionBezier(t,e,this.via)}},{key:"_getDistanceToEdge",value:function(t,e,i,n,o,r){return this._getDistanceToBezierEdge(t,e,i,n,o,r,this.via)}}]),i}(I2);function P2(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var D2=function(t){Q0(i,t);var e=P2(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"_line",value:function(t,e,i){this._bezierCurve(t,e,i)}},{key:"getViaNode",value:function(){return this._getViaCoordinates()}},{key:"_getViaCoordinates",value:function(){var t,e,i=this.options.smooth.roundness,n=this.options.smooth.type,o=Math.abs(this.from.x-this.to.x),r=Math.abs(this.from.y-this.to.y);if("discrete"===n||"diagonalCross"===n){var s,a;s=a=o<=r?i*r:i*o,this.from.x>this.to.x&&(s=-s),this.from.y>=this.to.y&&(a=-a);var d=this.from.x+s,l=this.from.y+a;return"discrete"===n&&(o<=r?d=othis.to.x&&(t=-t),this.from.y>=this.to.y&&(e=-e);var x=this.from.x+t,_=this.from.y+e;return o<=r?x=this.from.x<=this.to.x?this.to.xx?this.to.x:x:_=this.from.y>=this.to.y?this.to.y>_?this.to.y:_:this.to.y<_?this.to.y:_,{x:x,y:_}}},{key:"_findBorderPosition",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{};return this._findBorderPositionBezier(t,e,i.via)}},{key:"_getDistanceToEdge",value:function(t,e,i,n,o,r){var s=arguments.length>6&&void 0!==arguments[6]?arguments[6]:this._getViaCoordinates();return this._getDistanceToBezierEdge(t,e,i,n,o,r,s)}},{key:"getPoint",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this._getViaCoordinates(),i=t;return{x:Math.pow(1-i,2)*this.fromPoint.x+2*i*(1-i)*e.x+Math.pow(i,2)*this.toPoint.x,y:Math.pow(1-i,2)*this.fromPoint.y+2*i*(1-i)*e.y+Math.pow(i,2)*this.toPoint.y}}}]),i}(I2);function M2(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var F2=function(t){Q0(i,t);var e=M2(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"_getDistanceToBezierEdge2",value:function(t,e,i,n,o,r,s,a){for(var d=1e9,l=t,c=e,h=[0,0,0,0],u=1;u<10;u++){var p=.1*u;h[0]=Math.pow(1-p,3),h[1]=3*p*Math.pow(1-p,2),h[2]=3*Math.pow(p,2)*(1-p),h[3]=Math.pow(p,3);var f=h[0]*t+h[1]*s.x+h[2]*a.x+h[3]*i,m=h[0]*e+h[1]*s.y+h[2]*a.y+h[3]*n;if(u>0){var v=this._getDistanceToLine(l,c,f,m,o,r);d=vMath.abs(r)||!0===this.options.smooth.forceDirection||"horizontal"===this.options.smooth.forceDirection)&&"vertical"!==this.options.smooth.forceDirection?(e=this.from.y,n=this.to.y,t=this.from.x-s*o,i=this.to.x+s*o):(e=this.from.y-s*r,n=this.to.y+s*r,t=this.from.x,i=this.to.x),[{x:t,y:e},{x:i,y:n}]}},{key:"getViaNode",value:function(){return this._getViaCoordinates()}},{key:"_findBorderPosition",value:function(t,e){return this._findBorderPositionBezier(t,e)}},{key:"_getDistanceToEdge",value:function(t,e,i,n,o,r){var s=QW(arguments.length>6&&void 0!==arguments[6]?arguments[6]:this._getViaCoordinates(),2),a=s[0],d=s[1];return this._getDistanceToBezierEdge2(t,e,i,n,o,r,a,d)}},{key:"getPoint",value:function(t){var e=QW(arguments.length>1&&void 0!==arguments[1]?arguments[1]:this._getViaCoordinates(),2),i=e[0],n=e[1],o=t,r=[Math.pow(1-o,3),3*o*Math.pow(1-o,2),3*Math.pow(o,2)*(1-o),Math.pow(o,3)];return{x:r[0]*this.fromPoint.x+r[1]*i.x+r[2]*n.x+r[3]*this.toPoint.x,y:r[0]*this.fromPoint.y+r[1]*i.y+r[2]*n.y+r[3]*this.toPoint.y}}}]),i}(F2);function L2(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var z2=function(t){Q0(i,t);var e=L2(i);function i(t,n,o){return rj(this,i),e.call(this,t,n,o)}return cW(i,[{key:"_line",value:function(t,e){t.beginPath(),t.moveTo(this.fromPoint.x,this.fromPoint.y),t.lineTo(this.toPoint.x,this.toPoint.y),this.enableShadow(t,e),t.stroke(),this.disableShadow(t,e)}},{key:"getViaNode",value:function(){}},{key:"getPoint",value:function(t){return{x:(1-t)*this.fromPoint.x+t*this.toPoint.x,y:(1-t)*this.fromPoint.y+t*this.toPoint.y}}},{key:"_findBorderPosition",value:function(t,e){var i=this.to,n=this.from;t.id===this.from.id&&(i=this.from,n=this.to);var o=Math.atan2(i.y-n.y,i.x-n.x),r=i.x-n.x,s=i.y-n.y,a=Math.sqrt(r*r+s*s),d=(a-t.distanceToBorder(e,o))/a;return{x:(1-d)*n.x+d*i.x,y:(1-d)*n.y+d*i.y,t:0}}},{key:"_getDistanceToEdge",value:function(t,e,i,n,o,r){return this._getDistanceToLine(t,e,i,n,o,r)}}]),i}(O2),j2=function(){function t(e,i,n,o,r){if(rj(this,t),void 0===i)throw new Error("No body provided");this.options=HK(o),this.globalOptions=o,this.defaultOptions=r,this.body=i,this.imagelist=n,this.id=void 0,this.fromId=void 0,this.toId=void 0,this.selected=!1,this.hover=!1,this.labelDirty=!0,this.baseWidth=this.options.width,this.baseFontSize=this.options.font.size,this.from=void 0,this.to=void 0,this.edgeType=void 0,this.connected=!1,this.labelModule=new I0(this.body,this.options,!0),this.setOptions(e)}return cW(t,[{key:"setOptions",value:function(e){if(e){var i=void 0!==e.physics&&this.options.physics!==e.physics||void 0!==e.hidden&&(this.options.hidden||!1)!==(e.hidden||!1)||void 0!==e.from&&this.options.from!==e.from||void 0!==e.to&&this.options.to!==e.to;t.parseOptions(this.options,e,!0,this.globalOptions),void 0!==e.id&&(this.id=e.id),void 0!==e.from&&(this.fromId=e.from),void 0!==e.to&&(this.toId=e.to),void 0!==e.title&&(this.title=e.title),void 0!==e.value&&(e.value=o0(e.value));var n=[e,this.options,this.defaultOptions];return this.chooser=h0("edge",n),this.updateLabelModule(e),i=this.updateEdgeType()||i,this._setInteractionWidths(),this.connect(),i}}},{key:"getFormattingValues",value:function(){var t=!0===this.options.arrows.to||!0===this.options.arrows.to.enabled,e=!0===this.options.arrows.from||!0===this.options.arrows.from.enabled,i=!0===this.options.arrows.middle||!0===this.options.arrows.middle.enabled,n=this.options.color.inherit,o={toArrow:t,toArrowScale:this.options.arrows.to.scaleFactor,toArrowType:this.options.arrows.to.type,toArrowSrc:this.options.arrows.to.src,toArrowImageWidth:this.options.arrows.to.imageWidth,toArrowImageHeight:this.options.arrows.to.imageHeight,middleArrow:i,middleArrowScale:this.options.arrows.middle.scaleFactor,middleArrowType:this.options.arrows.middle.type,middleArrowSrc:this.options.arrows.middle.src,middleArrowImageWidth:this.options.arrows.middle.imageWidth,middleArrowImageHeight:this.options.arrows.middle.imageHeight,fromArrow:e,fromArrowScale:this.options.arrows.from.scaleFactor,fromArrowType:this.options.arrows.from.type,fromArrowSrc:this.options.arrows.from.src,fromArrowImageWidth:this.options.arrows.from.imageWidth,fromArrowImageHeight:this.options.arrows.from.imageHeight,arrowStrikethrough:this.options.arrowStrikethrough,color:n?void 0:this.options.color.color,inheritsColor:n,opacity:this.options.color.opacity,hidden:this.options.hidden,length:this.options.length,shadow:this.options.shadow.enabled,shadowColor:this.options.shadow.color,shadowSize:this.options.shadow.size,shadowX:this.options.shadow.x,shadowY:this.options.shadow.y,dashes:this.options.dashes,width:this.options.width,background:this.options.background.enabled,backgroundColor:this.options.background.color,backgroundSize:this.options.background.size,backgroundDashes:this.options.background.dashes};if(this.selected||this.hover)if(!0===this.chooser){if(this.selected){var r=this.options.selectionWidth;"function"==typeof r?o.width=r(o.width):"number"==typeof r&&(o.width+=r),o.width=Math.max(o.width,.3/this.body.view.scale),o.color=this.options.color.highlight,o.shadow=this.options.shadow.enabled}else if(this.hover){var s=this.options.hoverWidth;"function"==typeof s?o.width=s(o.width):"number"==typeof s&&(o.width+=s),o.width=Math.max(o.width,.3/this.body.view.scale),o.color=this.options.color.hover,o.shadow=this.options.shadow.enabled}}else"function"==typeof this.chooser&&(this.chooser(o,this.options.id,this.selected,this.hover),void 0!==o.color&&(o.inheritsColor=!1),!1===o.shadow&&(o.shadowColor===this.options.shadow.color&&o.shadowSize===this.options.shadow.size&&o.shadowX===this.options.shadow.x&&o.shadowY===this.options.shadow.y||(o.shadow=!0)));else o.shadow=this.options.shadow.enabled,o.width=Math.max(o.width,.3/this.body.view.scale);return o}},{key:"updateLabelModule",value:function(t){var e=[t,this.options,this.globalOptions,this.defaultOptions];this.labelModule.update(this.options,e),void 0!==this.labelModule.baseSize&&(this.baseFontSize=this.labelModule.baseSize)}},{key:"updateEdgeType",value:function(){var t=this.options.smooth,e=!1,i=!0;return void 0!==this.edgeType&&((this.edgeType instanceof R2&&!0===t.enabled&&"dynamic"===t.type||this.edgeType instanceof B2&&!0===t.enabled&&"cubicBezier"===t.type||this.edgeType instanceof D2&&!0===t.enabled&&"dynamic"!==t.type&&"cubicBezier"!==t.type||this.edgeType instanceof z2&&!1===t.type.enabled)&&(i=!1),!0===i&&(e=this.cleanup())),!0===i?!0===t.enabled?"dynamic"===t.type?(e=!0,this.edgeType=new R2(this.options,this.body,this.labelModule)):"cubicBezier"===t.type?this.edgeType=new B2(this.options,this.body,this.labelModule):this.edgeType=new D2(this.options,this.body,this.labelModule):this.edgeType=new z2(this.options,this.body,this.labelModule):this.edgeType.setOptions(this.options),e}},{key:"connect",value:function(){this.disconnect(),this.from=this.body.nodes[this.fromId]||void 0,this.to=this.body.nodes[this.toId]||void 0,this.connected=void 0!==this.from&&void 0!==this.to,!0===this.connected?(this.from.attachEdge(this),this.to.attachEdge(this)):(this.from&&this.from.detachEdge(this),this.to&&this.to.detachEdge(this)),this.edgeType.connect()}},{key:"disconnect",value:function(){this.from&&(this.from.detachEdge(this),this.from=void 0),this.to&&(this.to.detachEdge(this),this.to=void 0),this.connected=!1}},{key:"getTitle",value:function(){return this.title}},{key:"isSelected",value:function(){return this.selected}},{key:"getValue",value:function(){return this.options.value}},{key:"setValueRange",value:function(t,e,i){if(void 0!==this.options.value){var n=this.options.scaling.customScalingFunction(t,e,i,this.options.value),o=this.options.scaling.max-this.options.scaling.min;if(!0===this.options.scaling.label.enabled){var r=this.options.scaling.label.max-this.options.scaling.label.min;this.options.font.size=this.options.scaling.label.min+n*r}this.options.width=this.options.scaling.min+n*o}else this.options.width=this.baseWidth,this.options.font.size=this.baseFontSize;this._setInteractionWidths(),this.updateLabelModule()}},{key:"_setInteractionWidths",value:function(){"function"==typeof this.options.hoverWidth?this.edgeType.hoverWidth=this.options.hoverWidth(this.options.width):this.edgeType.hoverWidth=this.options.hoverWidth+this.options.width,"function"==typeof this.options.selectionWidth?this.edgeType.selectionWidth=this.options.selectionWidth(this.options.width):this.edgeType.selectionWidth=this.options.selectionWidth+this.options.width}},{key:"draw",value:function(t){var e=this.getFormattingValues();if(!e.hidden){var i=this.edgeType.getViaNode();this.edgeType.drawLine(t,e,this.selected,this.hover,i),this.drawLabel(t,i)}}},{key:"drawArrows",value:function(t){var e=this.getFormattingValues();if(!e.hidden){var i=this.edgeType.getViaNode(),n={};this.edgeType.fromPoint=this.edgeType.from,this.edgeType.toPoint=this.edgeType.to,e.fromArrow&&(n.from=this.edgeType.getArrowData(t,"from",i,this.selected,this.hover,e),!1===e.arrowStrikethrough&&(this.edgeType.fromPoint=n.from.core),e.fromArrowSrc&&(n.from.image=this.imagelist.load(e.fromArrowSrc)),e.fromArrowImageWidth&&(n.from.imageWidth=e.fromArrowImageWidth),e.fromArrowImageHeight&&(n.from.imageHeight=e.fromArrowImageHeight)),e.toArrow&&(n.to=this.edgeType.getArrowData(t,"to",i,this.selected,this.hover,e),!1===e.arrowStrikethrough&&(this.edgeType.toPoint=n.to.core),e.toArrowSrc&&(n.to.image=this.imagelist.load(e.toArrowSrc)),e.toArrowImageWidth&&(n.to.imageWidth=e.toArrowImageWidth),e.toArrowImageHeight&&(n.to.imageHeight=e.toArrowImageHeight)),e.middleArrow&&(n.middle=this.edgeType.getArrowData(t,"middle",i,this.selected,this.hover,e),e.middleArrowSrc&&(n.middle.image=this.imagelist.load(e.middleArrowSrc)),e.middleArrowImageWidth&&(n.middle.imageWidth=e.middleArrowImageWidth),e.middleArrowImageHeight&&(n.middle.imageHeight=e.middleArrowImageHeight)),e.fromArrow&&this.edgeType.drawArrowHead(t,e,this.selected,this.hover,n.from),e.middleArrow&&this.edgeType.drawArrowHead(t,e,this.selected,this.hover,n.middle),e.toArrow&&this.edgeType.drawArrowHead(t,e,this.selected,this.hover,n.to)}}},{key:"drawLabel",value:function(t,e){if(void 0!==this.options.label){var i,n=this.from,o=this.to;if(this.labelModule.differentState(this.selected,this.hover)&&this.labelModule.getTextSize(t,this.selected,this.hover),n.id!=o.id){this.labelModule.pointToSelf=!1,i=this.edgeType.getPoint(.5,e),t.save();var r=this._getRotation(t);0!=r.angle&&(t.translate(r.x,r.y),t.rotate(r.angle)),this.labelModule.draw(t,i.x,i.y,this.selected,this.hover),t.restore()}else{this.labelModule.pointToSelf=!0;var s=f0(t,this.options.selfReference.angle,this.options.selfReference.size,n);i=this._pointOnCircle(s.x,s.y,this.options.selfReference.size,this.options.selfReference.angle),this.labelModule.draw(t,i.x,i.y,this.selected,this.hover)}}}},{key:"getItemsOnPoint",value:function(t){var e=[];if(this.labelModule.visible()){var i=this._getRotation();u0(this.labelModule.getSize(),t,i)&&e.push({edgeId:this.id,labelId:0})}var n={left:t.x,top:t.y};return this.isOverlappingWith(n)&&e.push({edgeId:this.id}),e}},{key:"isOverlappingWith",value:function(t){if(this.connected){var e=this.from.x,i=this.from.y,n=this.to.x,o=this.to.y,r=t.left,s=t.top;return this.edgeType.getDistanceToEdge(e,i,n,o,r,s)<10}return!1}},{key:"_getRotation",value:function(t){var e=this.edgeType.getViaNode(),i=this.edgeType.getPoint(.5,e);void 0!==t&&this.labelModule.calculateLabelSize(t,this.selected,this.hover,i.x,i.y);var n={x:i.x,y:this.labelModule.size.yLine,angle:0};if(!this.labelModule.visible())return n;if("horizontal"===this.options.font.align)return n;var o=this.from.y-this.to.y,r=this.from.x-this.to.x,s=Math.atan2(o,r);return(s<-1&&r<0||s>0&&r<0)&&(s+=Math.PI),n.angle=s,n}},{key:"_pointOnCircle",value:function(t,e,i,n){return{x:t+i*Math.cos(n),y:e-i*Math.sin(n)}}},{key:"select",value:function(){this.selected=!0}},{key:"unselect",value:function(){this.selected=!1}},{key:"cleanup",value:function(){return this.edgeType.cleanup()}},{key:"remove",value:function(){this.cleanup(),this.disconnect(),delete this.body.edges[this.id]}},{key:"endPointsValid",value:function(){return void 0!==this.body.nodes[this.fromId]&&void 0!==this.body.nodes[this.toId]}}],[{key:"parseOptions",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]&&arguments[2],n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},o=arguments.length>4&&void 0!==arguments[4]&&arguments[4];if(CK(["endPointOffset","arrowStrikethrough","id","from","hidden","hoverWidth","labelHighlightBold","length","line","opacity","physics","scaling","selectionWidth","selfReferenceSize","selfReference","to","title","value","width","font","chosen","widthConstraint"],t,e,i),void 0!==e.endPointOffset&&void 0!==e.endPointOffset.from&&(mJ(e.endPointOffset.from)?t.endPointOffset.from=e.endPointOffset.from:(t.endPointOffset.from=void 0!==n.endPointOffset.from?n.endPointOffset.from:0,console.error("endPointOffset.from is not a valid number"))),void 0!==e.endPointOffset&&void 0!==e.endPointOffset.to&&(mJ(e.endPointOffset.to)?t.endPointOffset.to=e.endPointOffset.to:(t.endPointOffset.to=void 0!==n.endPointOffset.to?n.endPointOffset.to:0,console.error("endPointOffset.to is not a valid number"))),p0(e.label)?t.label=e.label:p0(t.label)||(t.label=void 0),$K(t,e,"smooth",n),$K(t,e,"shadow",n),$K(t,e,"background",n),void 0!==e.dashes&&null!==e.dashes?t.dashes=e.dashes:!0===i&&null===e.dashes&&(t.dashes=NX(n.dashes)),void 0!==e.scaling&&null!==e.scaling?(void 0!==e.scaling.min&&(t.scaling.min=e.scaling.min),void 0!==e.scaling.max&&(t.scaling.max=e.scaling.max),$K(t.scaling,e.scaling,"label",n.scaling)):!0===i&&null===e.scaling&&(t.scaling=NX(n.scaling)),void 0!==e.arrows&&null!==e.arrows)if("string"==typeof e.arrows){var r=e.arrows.toLowerCase();t.arrows.to.enabled=-1!=PX(r).call(r,"to"),t.arrows.middle.enabled=-1!=PX(r).call(r,"middle"),t.arrows.from.enabled=-1!=PX(r).call(r,"from")}else{if("object"!==sW(e.arrows))throw new Error("The arrow newOptions can only be an object or a string. Refer to the documentation. You used:"+jX(e.arrows));$K(t.arrows,e.arrows,"to",n.arrows),$K(t.arrows,e.arrows,"middle",n.arrows),$K(t.arrows,e.arrows,"from",n.arrows)}else!0===i&&null===e.arrows&&(t.arrows=NX(n.arrows));if(void 0!==e.color&&null!==e.color){var s=wK(e.color)?{color:e.color,highlight:e.color,hover:e.color,inherit:!1,opacity:1}:e.color,a=t.color;if(o)TK(a,n.color,!1,i);else for(var d in a)Object.prototype.hasOwnProperty.call(a,d)&&delete a[d];if(wK(a))a.color=a,a.highlight=a,a.hover=a,a.inherit=!1,void 0===s.opacity&&(a.opacity=1);else{var l=!1;void 0!==s.color&&(a.color=s.color,l=!0),void 0!==s.highlight&&(a.highlight=s.highlight,l=!0),void 0!==s.hover&&(a.hover=s.hover,l=!0),void 0!==s.inherit&&(a.inherit=s.inherit),void 0!==s.opacity&&(a.opacity=Math.min(1,Math.max(0,s.opacity))),!0===l?a.inherit=!1:void 0===a.inherit&&(a.inherit="from")}}else!0===i&&null===e.color&&(t.color=HK(n.color));!0===i&&null===e.font&&(t.font=HK(n.font)),Object.prototype.hasOwnProperty.call(e,"selfReferenceSize")&&(console.warn("The selfReferenceSize property has been deprecated. Please use selfReference property instead. The selfReference can be set like thise selfReference:{size:30, angle:Math.PI / 4}"),t.selfReference.size=e.selfReferenceSize)}}]),t}(),H2=function(){function t(e,i,n){var o,r=this;rj(this,t),this.body=e,this.images=i,this.groups=n,this.body.functions.createEdge=FF(o=this.create).call(o,this),this.edgesListeners={add:function(t,e){r.add(e.items)},update:function(t,e){r.update(e.items)},remove:function(t,e){r.remove(e.items)}},this.options={},this.defaultOptions={arrows:{to:{enabled:!1,scaleFactor:1,type:"arrow"},middle:{enabled:!1,scaleFactor:1,type:"arrow"},from:{enabled:!1,scaleFactor:1,type:"arrow"}},endPointOffset:{from:0,to:0},arrowStrikethrough:!0,color:{color:"#848484",highlight:"#848484",hover:"#848484",inherit:"from",opacity:1},dashes:!1,font:{color:"#343434",size:14,face:"arial",background:"none",strokeWidth:2,strokeColor:"#ffffff",align:"horizontal",multi:!1,vadjust:0,bold:{mod:"bold"},boldital:{mod:"bold italic"},ital:{mod:"italic"},mono:{mod:"",size:15,face:"courier new",vadjust:2}},hidden:!1,hoverWidth:1.5,label:void 0,labelHighlightBold:!0,length:void 0,physics:!0,scaling:{min:1,max:15,label:{enabled:!0,min:14,max:30,maxVisible:30,drawThreshold:5},customScalingFunction:function(t,e,i,n){if(e===t)return.5;var o=1/(e-t);return Math.max(0,(n-t)*o)}},selectionWidth:1.5,selfReference:{size:20,angle:Math.PI/4,renderBehindTheNode:!0},shadow:{enabled:!1,color:"rgba(0,0,0,0.5)",size:10,x:5,y:5},background:{enabled:!1,color:"rgba(111,111,111,1)",size:10,dashes:!1},smooth:{enabled:!0,type:"dynamic",forceDirection:"none",roundness:.5},title:void 0,width:1,value:void 0},TK(this.options,this.defaultOptions),this.bindEventListeners()}return cW(t,[{key:"bindEventListeners",value:function(){var t,e,i=this;this.body.emitter.on("_forceDisableDynamicCurves",(function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];"dynamic"===t&&(t="continuous");var n=!1;for(var o in i.body.edges)if(Object.prototype.hasOwnProperty.call(i.body.edges,o)){var r=i.body.edges[o],s=i.body.data.edges.get(o);if(null!=s){var a=s.smooth;void 0!==a&&!0===a.enabled&&"dynamic"===a.type&&(void 0===t?r.setOptions({smooth:!1}):r.setOptions({smooth:{type:t}}),n=!0)}}!0===e&&!0===n&&i.body.emitter.emit("_dataChanged")})),this.body.emitter.on("_dataUpdated",(function(){i.reconnectEdges()})),this.body.emitter.on("refreshEdges",FF(t=this.refresh).call(t,this)),this.body.emitter.on("refresh",FF(e=this.refresh).call(e,this)),this.body.emitter.on("destroy",(function(){RK(i.edgesListeners,(function(t,e){i.body.data.edges&&i.body.data.edges.off(e,t)})),delete i.body.functions.createEdge,delete i.edgesListeners.add,delete i.edgesListeners.update,delete i.edgesListeners.remove,delete i.edgesListeners}))}},{key:"setOptions",value:function(t){if(void 0!==t){j2.parseOptions(this.options,t,!0,this.defaultOptions,!0);var e=!1;if(void 0!==t.smooth)for(var i in this.body.edges)Object.prototype.hasOwnProperty.call(this.body.edges,i)&&(e=this.body.edges[i].updateEdgeType()||e);if(void 0!==t.font)for(var n in this.body.edges)Object.prototype.hasOwnProperty.call(this.body.edges,n)&&this.body.edges[n].updateLabelModule();void 0===t.hidden&&void 0===t.physics&&!0!==e||this.body.emitter.emit("_dataChanged")}}},{key:"setData",value:function(t){var e=this,i=arguments.length>1&&void 0!==arguments[1]&&arguments[1],n=this.body.data.edges;if(YA("id",t))this.body.data.edges=t;else if(fV(t))this.body.data.edges=new XA,this.body.data.edges.add(t);else{if(t)throw new TypeError("Array or DataSet expected");this.body.data.edges=new XA}if(n&&RK(this.edgesListeners,(function(t,e){n.off(e,t)})),this.body.edges={},this.body.data.edges){RK(this.edgesListeners,(function(t,i){e.body.data.edges.on(i,t)}));var o=this.body.data.edges.getIds();this.add(o,!0)}this.body.emitter.emit("_adjustEdgesForHierarchicalLayout"),!1===i&&this.body.emitter.emit("_dataChanged")}},{key:"add",value:function(t){for(var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1],i=this.body.edges,n=this.body.data.edges,o=0;o1&&void 0!==arguments[1])||arguments[1];if(0!==t.length){var i=this.body.edges;RK(t,(function(t){var e=i[t];void 0!==e&&e.remove()})),e&&this.body.emitter.emit("_dataChanged")}}},{key:"refresh",value:function(){var t=this;RK(this.body.edges,(function(e,i){var n=t.body.data.edges.get(i);void 0!==n&&e.setOptions(n)}))}},{key:"create",value:function(t){return new j2(t,this.body,this.images,this.options,this.defaultOptions)}},{key:"reconnectEdges",value:function(){var t,e=this.body.nodes,i=this.body.edges;for(t in e)Object.prototype.hasOwnProperty.call(e,t)&&(e[t].edges=[]);for(t in i)if(Object.prototype.hasOwnProperty.call(i,t)){var n=i[t];n.from=null,n.to=null,n.connect()}}},{key:"getConnectedNodes",value:function(t){var e=[];if(void 0!==this.body.edges[t]){var i=this.body.edges[t];void 0!==i.fromId&&e.push(i.fromId),void 0!==i.toId&&e.push(i.toId)}return e}},{key:"_updateState",value:function(){this._addMissingEdges(),this._removeInvalidEdges()}},{key:"_removeInvalidEdges",value:function(){var t=this,e=[];RK(this.body.edges,(function(i,n){var o=t.body.nodes[i.toId],r=t.body.nodes[i.fromId];void 0!==o&&!0===o.isCluster||void 0!==r&&!0===r.isCluster||void 0!==o&&void 0!==r||e.push(n)})),this.remove(e,!1)}},{key:"_addMissingEdges",value:function(){var t=this.body.data.edges;if(null!=t){var e=this.body.edges,i=[];HV(t).call(t,(function(t,n){void 0===e[n]&&i.push(n)})),this.add(i,!0)}}}]),t}(),$2=function(){function t(e,i,n){rj(this,t),this.body=e,this.physicsBody=i,this.barnesHutTree,this.setOptions(n),this._rng=fK("BARNES HUT SOLVER")}return cW(t,[{key:"setOptions",value:function(t){this.options=t,this.thetaInversed=1/this.options.theta,this.overlapAvoidanceFactor=1-Math.max(0,Math.min(1,this.options.avoidOverlap))}},{key:"solve",value:function(){if(0!==this.options.gravitationalConstant&&this.physicsBody.physicsNodeIndices.length>0){var t,e=this.body.nodes,i=this.physicsBody.physicsNodeIndices,n=i.length,o=this._formBarnesHutTree(e,i);this.barnesHutTree=o;for(var r=0;r0&&this._getForceContributions(o.root,t)}}},{key:"_getForceContributions",value:function(t,e){this._getForceContribution(t.children.NW,e),this._getForceContribution(t.children.NE,e),this._getForceContribution(t.children.SW,e),this._getForceContribution(t.children.SE,e)}},{key:"_getForceContribution",value:function(t,e){if(t.childrenCount>0){var i=t.centerOfMass.x-e.x,n=t.centerOfMass.y-e.y,o=Math.sqrt(i*i+n*n);o*t.calcSize>this.thetaInversed?this._calculateForces(o,i,n,e,t):4===t.childrenCount?this._getForceContributions(t,e):t.children.data.id!=e.id&&this._calculateForces(o,i,n,e,t)}}},{key:"_calculateForces",value:function(t,e,i,n,o){0===t&&(e=t=.1),this.overlapAvoidanceFactor<1&&n.shape.radius&&(t=Math.max(.1+this.overlapAvoidanceFactor*n.shape.radius,t-n.shape.radius));var r=this.options.gravitationalConstant*o.mass*n.options.mass/Math.pow(t,3),s=e*r,a=i*r;this.physicsBody.forces[n.id].x+=s,this.physicsBody.forces[n.id].y+=a}},{key:"_formBarnesHutTree",value:function(t,e){for(var i,n=e.length,o=t[e[0]].x,r=t[e[0]].y,s=t[e[0]].x,a=t[e[0]].y,d=1;d0&&(cs&&(s=c),ha&&(a=h))}var u=Math.abs(s-o)-Math.abs(a-r);u>0?(r-=.5*u,a+=.5*u):(o+=.5*u,s-=.5*u);var p=Math.max(1e-5,Math.abs(s-o)),f=.5*p,m=.5*(o+s),v=.5*(r+a),g={root:{centerOfMass:{x:0,y:0},mass:0,range:{minX:m-f,maxX:m+f,minY:v-f,maxY:v+f},size:p,calcSize:1/p,children:{data:null},maxWidth:0,level:0,childrenCount:4}};this._splitBranch(g.root);for(var y=0;y0&&this._placeInTree(g.root,i);return g}},{key:"_updateBranchMass",value:function(t,e){var i=t.centerOfMass,n=t.mass+e.options.mass,o=1/n;i.x=i.x*t.mass+e.x*e.options.mass,i.x*=o,i.y=i.y*t.mass+e.y*e.options.mass,i.y*=o,t.mass=n;var r=Math.max(Math.max(e.height,e.radius),e.width);t.maxWidth=t.maxWidthe.x?o.maxY>e.y?"NW":"SW":o.maxY>e.y?"NE":"SE",this._placeInRegion(t,e,n)}},{key:"_placeInRegion",value:function(t,e,i){var n=t.children[i];switch(n.childrenCount){case 0:n.children.data=e,n.childrenCount=1,this._updateBranchMass(n,e);break;case 1:n.children.data.x===e.x&&n.children.data.y===e.y?(e.x+=this._rng(),e.y+=this._rng()):(this._splitBranch(n),this._placeInTree(n,e));break;case 4:this._placeInTree(n,e)}}},{key:"_splitBranch",value:function(t){var e=null;1===t.childrenCount&&(e=t.children.data,t.mass=0,t.centerOfMass.x=0,t.centerOfMass.y=0),t.childrenCount=4,t.children.data=null,this._insertRegion(t,"NW"),this._insertRegion(t,"NE"),this._insertRegion(t,"SW"),this._insertRegion(t,"SE"),null!=e&&this._placeInTree(t,e)}},{key:"_insertRegion",value:function(t,e){var i,n,o,r,s=.5*t.size;switch(e){case"NW":i=t.range.minX,n=t.range.minX+s,o=t.range.minY,r=t.range.minY+s;break;case"NE":i=t.range.minX+s,n=t.range.maxX,o=t.range.minY,r=t.range.minY+s;break;case"SW":i=t.range.minX,n=t.range.minX+s,o=t.range.minY+s,r=t.range.maxY;break;case"SE":i=t.range.minX+s,n=t.range.maxX,o=t.range.minY+s,r=t.range.maxY}t.children[e]={centerOfMass:{x:0,y:0},mass:0,range:{minX:i,maxX:n,minY:o,maxY:r},size:.5*t.size,calcSize:2*t.calcSize,children:{data:null},maxWidth:0,level:t.level+1,childrenCount:0}}},{key:"_debug",value:function(t,e){void 0!==this.barnesHutTree&&(t.lineWidth=1,this._drawBranch(this.barnesHutTree.root,t,e))}},{key:"_drawBranch",value:function(t,e,i){void 0===i&&(i="#FF0000"),4===t.childrenCount&&(this._drawBranch(t.children.NW,e),this._drawBranch(t.children.NE,e),this._drawBranch(t.children.SE,e),this._drawBranch(t.children.SW,e)),e.strokeStyle=i,e.beginPath(),e.moveTo(t.range.minX,t.range.minY),e.lineTo(t.range.maxX,t.range.minY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.minY),e.lineTo(t.range.maxX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.maxX,t.range.maxY),e.lineTo(t.range.minX,t.range.maxY),e.stroke(),e.beginPath(),e.moveTo(t.range.minX,t.range.maxY),e.lineTo(t.range.minX,t.range.minY),e.stroke()}}]),t}(),U2=function(){function t(e,i,n){rj(this,t),this._rng=fK("REPULSION SOLVER"),this.body=e,this.physicsBody=i,this.setOptions(n)}return cW(t,[{key:"setOptions",value:function(t){this.options=t}},{key:"solve",value:function(){for(var t,e,i,n,o,r,s,a,d=this.body.nodes,l=this.physicsBody.physicsNodeIndices,c=this.physicsBody.forces,h=this.options.nodeDistance,u=-2/3/h,p=4/3,f=0;f0){var r=o.edges.length+1,s=this.options.centralGravity*r*o.options.mass;n[o.id].x=e*s,n[o.id].y=i*s}}}]),i}(X2),Q2=function(){function t(e){rj(this,t),this.body=e,this.physicsBody={physicsNodeIndices:[],physicsEdgeIndices:[],forces:{},velocities:{}},this.physicsEnabled=!0,this.simulationInterval=1e3/60,this.requiresTimeout=!0,this.previousStates={},this.referenceState={},this.freezeCache={},this.renderTimer=void 0,this.adaptiveTimestep=!1,this.adaptiveTimestepEnabled=!1,this.adaptiveCounter=0,this.adaptiveInterval=3,this.stabilized=!1,this.startedStabilization=!1,this.stabilizationIterations=0,this.ready=!1,this.options={},this.defaultOptions={enabled:!0,barnesHut:{theta:.5,gravitationalConstant:-2e3,centralGravity:.3,springLength:95,springConstant:.04,damping:.09,avoidOverlap:0},forceAtlas2Based:{theta:.5,gravitationalConstant:-50,centralGravity:.01,springConstant:.08,springLength:100,damping:.4,avoidOverlap:0},repulsion:{centralGravity:.2,springLength:200,springConstant:.05,nodeDistance:100,damping:.09,avoidOverlap:0},hierarchicalRepulsion:{centralGravity:0,springLength:100,springConstant:.01,nodeDistance:120,damping:.09},maxVelocity:50,minVelocity:.75,solver:"barnesHut",stabilization:{enabled:!0,iterations:1e3,updateInterval:50,onlyDynamicEdges:!1,fit:!0},timestep:.5,adaptiveTimestep:!0,wind:{x:0,y:0}},pF(this.options,this.defaultOptions),this.timestep=.5,this.layoutFailed=!1,this.bindEventListeners()}return cW(t,[{key:"bindEventListeners",value:function(){var t=this;this.body.emitter.on("initPhysics",(function(){t.initPhysics()})),this.body.emitter.on("_layoutFailed",(function(){t.layoutFailed=!0})),this.body.emitter.on("resetPhysics",(function(){t.stopSimulation(),t.ready=!1})),this.body.emitter.on("disablePhysics",(function(){t.physicsEnabled=!1,t.stopSimulation()})),this.body.emitter.on("restorePhysics",(function(){t.setOptions(t.options),!0===t.ready&&t.startSimulation()})),this.body.emitter.on("startSimulation",(function(){!0===t.ready&&t.startSimulation()})),this.body.emitter.on("stopSimulation",(function(){t.stopSimulation()})),this.body.emitter.on("destroy",(function(){t.stopSimulation(!1),t.body.emitter.off()})),this.body.emitter.on("_dataChanged",(function(){t.updatePhysicsData()}))}},{key:"setOptions",value:function(t){if(void 0!==t)if(!1===t)this.options.enabled=!1,this.physicsEnabled=!1,this.stopSimulation();else if(!0===t)this.options.enabled=!0,this.physicsEnabled=!0,this.startSimulation();else{this.physicsEnabled=!0,SK(["stabilization"],this.options,t),$K(this.options,t,"stabilization"),void 0===t.enabled&&(this.options.enabled=!0),!1===this.options.enabled&&(this.physicsEnabled=!1,this.stopSimulation());var e=this.options.wind;e&&(("number"!=typeof e.x||uJ(e.x))&&(e.x=0),("number"!=typeof e.y||uJ(e.y))&&(e.y=0)),this.timestep=this.options.timestep}this.init()}},{key:"init",value:function(){var t;"forceAtlas2Based"===this.options.solver?(t=this.options.forceAtlas2Based,this.nodesSolver=new G2(this.body,this.physicsBody,t),this.edgesSolver=new V2(this.body,this.physicsBody,t),this.gravitySolver=new Z2(this.body,this.physicsBody,t)):"repulsion"===this.options.solver?(t=this.options.repulsion,this.nodesSolver=new U2(this.body,this.physicsBody,t),this.edgesSolver=new V2(this.body,this.physicsBody,t),this.gravitySolver=new X2(this.body,this.physicsBody,t)):"hierarchicalRepulsion"===this.options.solver?(t=this.options.hierarchicalRepulsion,this.nodesSolver=new W2(this.body,this.physicsBody,t),this.edgesSolver=new q2(this.body,this.physicsBody,t),this.gravitySolver=new X2(this.body,this.physicsBody,t)):(t=this.options.barnesHut,this.nodesSolver=new $2(this.body,this.physicsBody,t),this.edgesSolver=new V2(this.body,this.physicsBody,t),this.gravitySolver=new X2(this.body,this.physicsBody,t)),this.modelOptions=t}},{key:"initPhysics",value:function(){!0===this.physicsEnabled&&!0===this.options.enabled?!0===this.options.stabilization.enabled?this.stabilize():(this.stabilized=!1,this.ready=!0,this.body.emitter.emit("fit",{},this.layoutFailed),this.startSimulation()):(this.ready=!0,this.body.emitter.emit("fit"))}},{key:"startSimulation",value:function(){var t;!0===this.physicsEnabled&&!0===this.options.enabled?(this.stabilized=!1,this.adaptiveTimestep=!1,this.body.emitter.emit("_resizeNodes"),void 0===this.viewFunction&&(this.viewFunction=FF(t=this.simulationStep).call(t,this),this.body.emitter.on("initRedraw",this.viewFunction),this.body.emitter.emit("_startRendering"))):this.body.emitter.emit("_redraw")}},{key:"stopSimulation",value:function(){var t=!(arguments.length>0&&void 0!==arguments[0])||arguments[0];this.stabilized=!0,!0===t&&this._emitStabilized(),void 0!==this.viewFunction&&(this.body.emitter.off("initRedraw",this.viewFunction),this.viewFunction=void 0,!0===t&&this.body.emitter.emit("_stopRendering"))}},{key:"simulationStep",value:function(){var t=TV();this.physicsTick(),(TV()-t<.4*this.simulationInterval||!0===this.runDoubleSpeed)&&!1===this.stabilized&&(this.physicsTick(),this.runDoubleSpeed=!0),!0===this.stabilized&&this.stopSimulation()}},{key:"_emitStabilized",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.stabilizationIterations;(this.stabilizationIterations>1||!0===this.startedStabilization)&&rY((function(){t.body.emitter.emit("stabilized",{iterations:e}),t.startedStabilization=!1,t.stabilizationIterations=0}),0)}},{key:"physicsStep",value:function(){this.gravitySolver.solve(),this.nodesSolver.solve(),this.edgesSolver.solve(),this.moveNodes()}},{key:"adjustTimeStep",value:function(){!0===this._evaluateStepQuality()?this.timestep=1.2*this.timestep:this.timestep/1.2.3))return!1;return!0}},{key:"moveNodes",value:function(){for(var t=this.physicsBody.physicsNodeIndices,e=0,i=0,n=0;nn&&(t=t>0?n:-n),t}},{key:"_performStep",value:function(t){var e=this.body.nodes[t],i=this.physicsBody.forces[t];this.options.wind&&(i.x+=this.options.wind.x,i.y+=this.options.wind.y);var n=this.physicsBody.velocities[t];return this.previousStates[t]={x:e.x,y:e.y,vx:n.x,vy:n.y},!1===e.options.fixed.x?(n.x=this.calculateComponentVelocity(n.x,i.x,e.options.mass),e.x+=n.x*this.timestep):(i.x=0,n.x=0),!1===e.options.fixed.y?(n.y=this.calculateComponentVelocity(n.y,i.y,e.options.mass),e.y+=n.y*this.timestep):(i.y=0,n.y=0),Math.sqrt(Math.pow(n.x,2)+Math.pow(n.y,2))}},{key:"_freezeNodes",value:function(){var t=this.body.nodes;for(var e in t)if(Object.prototype.hasOwnProperty.call(t,e)&&t[e].x&&t[e].y){var i=t[e].options.fixed;this.freezeCache[e]={x:i.x,y:i.y},i.x=!0,i.y=!0}}},{key:"_restoreFrozenNodes",value:function(){var t=this.body.nodes;for(var e in t)Object.prototype.hasOwnProperty.call(t,e)&&void 0!==this.freezeCache[e]&&(t[e].options.fixed.x=this.freezeCache[e].x,t[e].options.fixed.y=this.freezeCache[e].y);this.freezeCache={}}},{key:"stabilize",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.stabilization.iterations;"number"!=typeof e&&(e=this.options.stabilization.iterations,console.error("The stabilize method needs a numeric amount of iterations. Switching to default: ",e)),0!==this.physicsBody.physicsNodeIndices.length?(this.adaptiveTimestep=this.options.adaptiveTimestep,this.body.emitter.emit("_resizeNodes"),this.stopSimulation(),this.stabilized=!1,this.body.emitter.emit("_blockRedraw"),this.targetIterations=e,!0===this.options.stabilization.onlyDynamicEdges&&this._freezeNodes(),this.stabilizationIterations=0,rY((function(){return t._stabilizationBatch()}),0)):this.ready=!0}},{key:"_startStabilizing",value:function(){return!0!==this.startedStabilization&&(this.body.emitter.emit("startStabilizing"),this.startedStabilization=!0,!0)}},{key:"_stabilizationBatch",value:function(){var t=this,e=function(){return!1===t.stabilized&&t.stabilizationIterations1&&void 0!==arguments[1]?arguments[1]:[],n=1e9,o=-1e9,r=1e9,s=-1e9;if(i.length>0)for(var a=0;a(e=t[i[a]]).shape.boundingBox.left&&(r=e.shape.boundingBox.left),se.shape.boundingBox.top&&(n=e.shape.boundingBox.top),o1&&void 0!==arguments[1]?arguments[1]:[],n=1e9,o=-1e9,r=1e9,s=-1e9;if(i.length>0)for(var a=0;a(e=t[i[a]]).x&&(r=e.x),se.y&&(n=e.y),o=t&&i.push(o.id)}for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:{},i=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(void 0===e.joinCondition)throw new Error("Cannot call clusterByNodeData without a joinCondition function in the options.");e=this._checkOptions(e);var n={},o={};RK(this.body.nodes,(function(i,r){i.options&&!0===e.joinCondition(i.options)&&(n[r]=i,RK(i.edges,(function(e){void 0===t.clusteredEdges[e.id]&&(o[e.id]=e)})))})),this._cluster(n,o,e,i)}},{key:"clusterByEdgeCount",value:function(t,e){var i=this,n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];e=this._checkOptions(e);for(var o,r,s,a=[],d={},l=function(){var n={},l={},h=i.body.nodeIndices[c],u=i.body.nodes[h];if(void 0===d[h]){s=0,r=[];for(var p=0;p0&&kV(l).length>0&&!0===m){var y=function(){for(var t=0;t1&&void 0!==arguments[1])||arguments[1];this.clusterByEdgeCount(1,t,e)}},{key:"clusterBridges",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];this.clusterByEdgeCount(2,t,e)}},{key:"clusterByConnection",value:function(t,e){var i,n=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(void 0===t)throw new Error("No nodeId supplied to clusterByConnection!");if(void 0===this.body.nodes[t])throw new Error("The nodeId given to clusterByConnection does not exist!");var o=this.body.nodes[t];void 0===(e=this._checkOptions(e,o)).clusterNodeProperties.x&&(e.clusterNodeProperties.x=o.x),void 0===e.clusterNodeProperties.y&&(e.clusterNodeProperties.y=o.y),void 0===e.clusterNodeProperties.fixed&&(e.clusterNodeProperties.fixed={},e.clusterNodeProperties.fixed.x=o.options.fixed.x,e.clusterNodeProperties.fixed.y=o.options.fixed.y);var r={},s={},a=o.id,d=r5.cloneOptions(o);r[a]=o;for(var l=0;l-1&&(s[g.id]=g)}this._cluster(r,s,e,n)}},{key:"_createClusterEdges",value:function(t,e,i,n){for(var o,r,s,a,d,l,c=kV(t),h=[],u=0;u0&&void 0!==arguments[0]?arguments[0]:{};return void 0===t.clusterEdgeProperties&&(t.clusterEdgeProperties={}),void 0===t.clusterNodeProperties&&(t.clusterNodeProperties={}),t}},{key:"_cluster",value:function(t,e,i){var n=!(arguments.length>3&&void 0!==arguments[3])||arguments[3],o=[];for(var r in t)Object.prototype.hasOwnProperty.call(t,r)&&void 0!==this.clusteredNodes[r]&&o.push(r);for(var s=0;so?e.x:o,r=e.ys?e.y:s;return{x:.5*(n+o),y:.5*(r+s)}}},{key:"openCluster",value:function(t,e){var i=!(arguments.length>2&&void 0!==arguments[2])||arguments[2];if(void 0===t)throw new Error("No clusterNodeId supplied to openCluster.");var n=this.body.nodes[t];if(void 0===n)throw new Error("The clusterNodeId supplied to openCluster does not exist.");if(!0!==n.isCluster||void 0===n.containedNodes||void 0===n.containedEdges)throw new Error("The node:"+t+" is not a valid cluster.");var o=this.findNode(t),r=PX(o).call(o,t)-1;if(r>=0){var s=o[r];return this.body.nodes[s]._openChildCluster(t),delete this.body.nodes[t],void(!0===i&&this.body.emitter.emit("_dataChanged"))}var a=n.containedNodes,d=n.containedEdges;if(void 0!==e&&void 0!==e.releaseFunction&&"function"==typeof e.releaseFunction){var l={},c={x:n.x,y:n.y};for(var h in a)if(Object.prototype.hasOwnProperty.call(a,h)){var u=this.body.nodes[h];l[h]={x:u.x,y:u.y}}var p=e.releaseFunction(c,l);for(var f in a)if(Object.prototype.hasOwnProperty.call(a,f)){var m=this.body.nodes[f];void 0!==p[f]&&(m.x=void 0===p[f].x?n.x:p[f].x,m.y=void 0===p[f].y?n.y:p[f].y)}}else RK(a,(function(t){!1===t.options.fixed.x&&(t.x=n.x),!1===t.options.fixed.y&&(t.y=n.y)}));for(var v in a)if(Object.prototype.hasOwnProperty.call(a,v)){var g=this.body.nodes[v];g.vx=n.vx,g.vy=n.vy,g.setOptions({physics:!0}),delete this.clusteredNodes[v]}for(var y=[],b=0;b0&&o<100;){var r=e.pop();if(void 0!==r){var s=this.body.edges[r];if(void 0!==s){o++;var a=s.clusteringEdgeReplacingIds;if(void 0===a)n.push(r);else for(var d=0;dn&&(n=r.edges.length),t+=r.edges.length,e+=Math.pow(r.edges.length,2),i+=1}t/=i;var s=(e/=i)-Math.pow(t,2),a=Math.sqrt(s),d=Math.floor(t+2*a);return d>n&&(d=n),d}},{key:"_createClusteredEdge",value:function(t,e,i,n,o){var r=r5.cloneOptions(i,"edge");TK(r,n),r.from=t,r.to=e,r.id="clusterEdge:"+o5(),void 0!==o&&TK(r,o);var s=this.body.functions.createEdge(r);return s.clusteringEdgeReplacingIds=[i.id],s.connect(),this.body.edges[s.id]=s,s}},{key:"_clusterEdges",value:function(t,e,i,n){if(e instanceof j2){var o=e,r={};r[o.id]=o,e=r}if(t instanceof U1){var s=t,a={};a[s.id]=s,t=a}if(null==i)throw new Error("_clusterEdges: parameter clusterNode required");for(var d in void 0===n&&(n=i.clusterEdgeProperties),this._createClusterEdges(t,e,i,n),e)if(Object.prototype.hasOwnProperty.call(e,d)&&void 0!==this.body.edges[d]){var l=this.body.edges[d];this._backupEdgeOptions(l),l.setOptions({physics:!1})}for(var c in t)Object.prototype.hasOwnProperty.call(t,c)&&(this.clusteredNodes[c]={clusterId:i.id,node:this.body.nodes[c]},this.body.nodes[c].setOptions({physics:!1}))}},{key:"_getClusterNodeForNode",value:function(t){if(void 0!==t){var e=this.clusteredNodes[t];if(void 0!==e){var i=e.clusterId;if(void 0!==i)return this.body.nodes[i]}}}},{key:"_filter",value:function(t,e){var i=[];return RK(t,(function(t){e(t)&&i.push(t)})),i}},{key:"_updateState",value:function(){var t,e=this,i=[],n={},o=function(t){RK(e.body.nodes,(function(e){!0===e.isCluster&&t(e)}))};for(t in this.clusteredNodes){if(Object.prototype.hasOwnProperty.call(this.clusteredNodes,t))void 0===this.body.nodes[t]&&i.push(t)}o((function(t){for(var e=0;e0}t.endPointsValid()&&o||(n[i]=i)})),o((function(t){RK(n,(function(i){delete t.containedEdges[i],RK(t.edges,(function(o,r){o.id!==i?o.clusteringEdgeReplacingIds=e._filter(o.clusteringEdgeReplacingIds,(function(t){return!n[t]})):t.edges[r]=null})),t.edges=e._filter(t.edges,(function(t){return null!==t}))}))})),RK(n,(function(t){delete e.clusteredEdges[t]})),RK(n,(function(t){delete e.body.edges[t]})),RK(kV(this.body.edges),(function(t){var i=e.body.edges[t],n=e._isClusteredNode(i.fromId)||e._isClusteredNode(i.toId);if(n!==e._isClusteredEdge(i.id))if(n){var o=e._getClusterNodeForNode(i.fromId);void 0!==o&&e._clusterEdges(e.body.nodes[i.fromId],i,o);var r=e._getClusterNodeForNode(i.toId);void 0!==r&&e._clusterEdges(e.body.nodes[i.toId],i,r)}else delete e._clusterEdges[t],e._restoreEdge(i)}));for(var s=!1,a=!0,d=function(){var t=[];o((function(e){var i=kV(e.containedNodes).length,n=!0===e.options.allowSingleNodeCluster;(n&&i<1||!n&&i<2)&&t.push(e.id)}));for(var i=0;i0,s=s||a};a;)d();s&&this._updateState()}},{key:"_isClusteredNode",value:function(t){return void 0!==this.clusteredNodes[t]}},{key:"_isClusteredEdge",value:function(t){return void 0!==this.clusteredEdges[t]}}]),t}();var l5=function(){function t(e,i){var n;rj(this,t),void 0!==window&&(n=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame),window.requestAnimationFrame=void 0===n?function(t){t()}:n,this.body=e,this.canvas=i,this.redrawRequested=!1,this.renderTimer=void 0,this.requiresTimeout=!0,this.renderingActive=!1,this.renderRequests=0,this.allowRedraw=!0,this.dragging=!1,this.zooming=!1,this.options={},this.defaultOptions={hideEdgesOnDrag:!1,hideEdgesOnZoom:!1,hideNodesOnDrag:!1},pF(this.options,this.defaultOptions),this._determineBrowserMethod(),this.bindEventListeners()}return cW(t,[{key:"bindEventListeners",value:function(){var t,e=this;this.body.emitter.on("dragStart",(function(){e.dragging=!0})),this.body.emitter.on("dragEnd",(function(){e.dragging=!1})),this.body.emitter.on("zoom",(function(){e.zooming=!0,window.clearTimeout(e.zoomTimeoutId),e.zoomTimeoutId=rY((function(){var t;e.zooming=!1,FF(t=e._requestRedraw).call(t,e)()}),250)})),this.body.emitter.on("_resizeNodes",(function(){e._resizeNodes()})),this.body.emitter.on("_redraw",(function(){!1===e.renderingActive&&e._redraw()})),this.body.emitter.on("_blockRedraw",(function(){e.allowRedraw=!1})),this.body.emitter.on("_allowRedraw",(function(){e.allowRedraw=!0,e.redrawRequested=!1})),this.body.emitter.on("_requestRedraw",FF(t=this._requestRedraw).call(t,this)),this.body.emitter.on("_startRendering",(function(){e.renderRequests+=1,e.renderingActive=!0,e._startRendering()})),this.body.emitter.on("_stopRendering",(function(){e.renderRequests-=1,e.renderingActive=e.renderRequests>0,e.renderTimer=void 0})),this.body.emitter.on("destroy",(function(){e.renderRequests=0,e.allowRedraw=!1,e.renderingActive=!1,!0===e.requiresTimeout?clearTimeout(e.renderTimer):window.cancelAnimationFrame(e.renderTimer),e.body.emitter.off()}))}},{key:"setOptions",value:function(t){if(void 0!==t){CK(["hideEdgesOnDrag","hideEdgesOnZoom","hideNodesOnDrag"],this.options,t)}}},{key:"_requestNextFrame",value:function(t,e){if("undefined"!=typeof window){var i,n=window;return!0===this.requiresTimeout?i=rY(t,e):n.requestAnimationFrame&&(i=n.requestAnimationFrame(t)),i}}},{key:"_startRendering",value:function(){var t;!0===this.renderingActive&&(void 0===this.renderTimer&&(this.renderTimer=this._requestNextFrame(FF(t=this._renderStep).call(t,this),this.simulationInterval)))}},{key:"_renderStep",value:function(){!0===this.renderingActive&&(this.renderTimer=void 0,!0===this.requiresTimeout&&this._startRendering(),this._redraw(),!1===this.requiresTimeout&&this._startRendering())}},{key:"redraw",value:function(){this.body.emitter.emit("setSize"),this._redraw()}},{key:"_requestRedraw",value:function(){var t=this;!0!==this.redrawRequested&&!1===this.renderingActive&&!0===this.allowRedraw&&(this.redrawRequested=!0,this._requestNextFrame((function(){t._redraw(!1)}),0))}},{key:"_redraw",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!0===this.allowRedraw){this.body.emitter.emit("initRedraw"),this.redrawRequested=!1;var e={drawExternalLabels:null};0!==this.canvas.frame.canvas.width&&0!==this.canvas.frame.canvas.height||this.canvas.setSize(),this.canvas.setTransform();var i=this.canvas.getContext(),n=this.canvas.frame.canvas.clientWidth,o=this.canvas.frame.canvas.clientHeight;if(i.clearRect(0,0,n,o),0===this.canvas.frame.clientWidth)return;if(i.save(),i.translate(this.body.view.translation.x,this.body.view.translation.y),i.scale(this.body.view.scale,this.body.view.scale),i.beginPath(),this.body.emitter.emit("beforeDrawing",i),i.closePath(),!1===t&&(!1===this.dragging||!0===this.dragging&&!1===this.options.hideEdgesOnDrag)&&(!1===this.zooming||!0===this.zooming&&!1===this.options.hideEdgesOnZoom)&&this._drawEdges(i),!1===this.dragging||!0===this.dragging&&!1===this.options.hideNodesOnDrag){var r=this._drawNodes(i,t).drawExternalLabels;e.drawExternalLabels=r}!1===t&&(!1===this.dragging||!0===this.dragging&&!1===this.options.hideEdgesOnDrag)&&(!1===this.zooming||!0===this.zooming&&!1===this.options.hideEdgesOnZoom)&&this._drawArrows(i),null!=e.drawExternalLabels&&e.drawExternalLabels(),!1===t&&this._drawSelectionBox(i),i.beginPath(),this.body.emitter.emit("afterDrawing",i),i.closePath(),i.restore(),!0===t&&i.clearRect(0,0,n,o)}}},{key:"_resizeNodes",value:function(){this.canvas.setTransform();var t=this.canvas.getContext();t.save(),t.translate(this.body.view.translation.x,this.body.view.translation.y),t.scale(this.body.view.scale,this.body.view.scale);var e,i=this.body.nodes;for(var n in i)Object.prototype.hasOwnProperty.call(i,n)&&((e=i[n]).resize(t),e.updateBoundingBox(t,e.selected));t.restore()}},{key:"_drawNodes",value:function(t){for(var e,i,n=arguments.length>1&&void 0!==arguments[1]&&arguments[1],o=this.body.nodes,r=this.body.nodeIndices,s=[],a=[],d=this.canvas.DOMtoCanvas({x:-20,y:-20}),l=this.canvas.DOMtoCanvas({x:this.canvas.frame.canvas.clientWidth+20,y:this.canvas.frame.canvas.clientHeight+20}),c={top:d.y,left:d.x,bottom:l.y,right:l.x},h=[],u=0;u0&&void 0!==arguments[0]?arguments[0]:this.pixelRatio;!0===this.initialized&&(this.cameraState.previousWidth=this.frame.canvas.width/t,this.cameraState.previousHeight=this.frame.canvas.height/t,this.cameraState.scale=this.body.view.scale,this.cameraState.position=this.DOMtoCanvas({x:.5*this.frame.canvas.width/t,y:.5*this.frame.canvas.height/t}))}},{key:"_setCameraState",value:function(){if(void 0!==this.cameraState.scale&&0!==this.frame.canvas.clientWidth&&0!==this.frame.canvas.clientHeight&&0!==this.pixelRatio&&this.cameraState.previousWidth>0&&this.cameraState.previousHeight>0){var t=this.frame.canvas.width/this.pixelRatio/this.cameraState.previousWidth,e=this.frame.canvas.height/this.pixelRatio/this.cameraState.previousHeight,i=this.cameraState.scale;1!=t&&1!=e?i=.5*this.cameraState.scale*(t+e):1!=t?i=this.cameraState.scale*t:1!=e&&(i=this.cameraState.scale*e),this.body.view.scale=i;var n=this.DOMtoCanvas({x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight}),o={x:n.x-this.cameraState.position.x,y:n.y-this.cameraState.position.y};this.body.view.translation.x+=o.x*this.body.view.scale,this.body.view.translation.y+=o.y*this.body.view.scale}}},{key:"_prepareValue",value:function(t){if("number"==typeof t)return t+"px";if("string"==typeof t){if(-1!==PX(t).call(t,"%")||-1!==PX(t).call(t,"px"))return t;if(-1===PX(t).call(t,"%"))return t+"px"}throw new Error("Could not use the value supplied for width or height:"+t)}},{key:"_create",value:function(){for(;this.body.container.hasChildNodes();)this.body.container.removeChild(this.body.container.firstChild);if(this.frame=document.createElement("div"),this.frame.className="vis-network",this.frame.style.position="relative",this.frame.style.overflow="hidden",this.frame.tabIndex=0,this.frame.canvas=document.createElement("canvas"),this.frame.canvas.style.position="relative",this.frame.appendChild(this.frame.canvas),this.frame.canvas.getContext)this._setPixelRatio(),this.setTransform();else{var t=document.createElement("DIV");t.style.color="red",t.style.fontWeight="bold",t.style.padding="10px",t.innerText="Error: your browser does not support HTML canvas",this.frame.canvas.appendChild(t)}this.body.container.appendChild(this.frame),this.body.view.scale=1,this.body.view.translation={x:.5*this.frame.canvas.clientWidth,y:.5*this.frame.canvas.clientHeight},this._bindHammer()}},{key:"_bindHammer",value:function(){var t=this;void 0!==this.hammer&&this.hammer.destroy(),this.drag={},this.pinch={},this.hammer=new iZ(this.frame.canvas),this.hammer.get("pinch").set({enable:!0}),this.hammer.get("pan").set({threshold:5,direction:iZ.DIRECTION_ALL}),h5(this.hammer,(function(e){t.body.eventListeners.onTouch(e)})),this.hammer.on("tap",(function(e){t.body.eventListeners.onTap(e)})),this.hammer.on("doubletap",(function(e){t.body.eventListeners.onDoubleTap(e)})),this.hammer.on("press",(function(e){t.body.eventListeners.onHold(e)})),this.hammer.on("panstart",(function(e){t.body.eventListeners.onDragStart(e)})),this.hammer.on("panmove",(function(e){t.body.eventListeners.onDrag(e)})),this.hammer.on("panend",(function(e){t.body.eventListeners.onDragEnd(e)})),this.hammer.on("pinch",(function(e){t.body.eventListeners.onPinch(e)})),this.frame.canvas.addEventListener("wheel",(function(e){t.body.eventListeners.onMouseWheel(e)})),this.frame.canvas.addEventListener("mousemove",(function(e){t.body.eventListeners.onMouseMove(e)})),this.frame.canvas.addEventListener("contextmenu",(function(e){t.body.eventListeners.onContext(e)})),this.hammerFrame=new iZ(this.frame),u5(this.hammerFrame,(function(e){t.body.eventListeners.onRelease(e)}))}},{key:"setSize",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.options.width,e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.options.height;t=this._prepareValue(t),e=this._prepareValue(e);var i=!1,n=this.frame.canvas.width,o=this.frame.canvas.height,r=this.pixelRatio;if(this._setPixelRatio(),t!=this.options.width||e!=this.options.height||this.frame.style.width!=t||this.frame.style.height!=e)this._getCameraState(r),this.frame.style.width=t,this.frame.style.height=e,this.frame.canvas.style.width="100%",this.frame.canvas.style.height="100%",this.frame.canvas.width=Math.round(this.frame.canvas.clientWidth*this.pixelRatio),this.frame.canvas.height=Math.round(this.frame.canvas.clientHeight*this.pixelRatio),this.options.width=t,this.options.height=e,this.canvasViewCenter={x:.5*this.frame.clientWidth,y:.5*this.frame.clientHeight},i=!0;else{var s=Math.round(this.frame.canvas.clientWidth*this.pixelRatio),a=Math.round(this.frame.canvas.clientHeight*this.pixelRatio);this.frame.canvas.width===s&&this.frame.canvas.height===a||this._getCameraState(r),this.frame.canvas.width!==s&&(this.frame.canvas.width=s,i=!0),this.frame.canvas.height!==a&&(this.frame.canvas.height=a,i=!0)}return!0===i&&(this.body.emitter.emit("resize",{width:Math.round(this.frame.canvas.width/this.pixelRatio),height:Math.round(this.frame.canvas.height/this.pixelRatio),oldWidth:Math.round(n/this.pixelRatio),oldHeight:Math.round(o/this.pixelRatio)}),this._setCameraState()),this.initialized=!0,i}},{key:"getContext",value:function(){return this.frame.canvas.getContext("2d")}},{key:"_determinePixelRatio",value:function(){var t=this.getContext();if(void 0===t)throw new Error("Could not get canvax context");var e=1;return"undefined"!=typeof window&&(e=window.devicePixelRatio||1),e/(t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1)}},{key:"_setPixelRatio",value:function(){this.pixelRatio=this._determinePixelRatio()}},{key:"setTransform",value:function(){var t=this.getContext();if(void 0===t)throw new Error("Could not get canvax context");t.setTransform(this.pixelRatio,0,0,this.pixelRatio,0,0)}},{key:"_XconvertDOMtoCanvas",value:function(t){return(t-this.body.view.translation.x)/this.body.view.scale}},{key:"_XconvertCanvasToDOM",value:function(t){return t*this.body.view.scale+this.body.view.translation.x}},{key:"_YconvertDOMtoCanvas",value:function(t){return(t-this.body.view.translation.y)/this.body.view.scale}},{key:"_YconvertCanvasToDOM",value:function(t){return t*this.body.view.scale+this.body.view.translation.y}},{key:"canvasToDOM",value:function(t){return{x:this._XconvertCanvasToDOM(t.x),y:this._YconvertCanvasToDOM(t.y)}}},{key:"DOMtoCanvas",value:function(t){return{x:this._XconvertDOMtoCanvas(t.x),y:this._YconvertDOMtoCanvas(t.y)}}}]),t}();var f5=function(){function t(e,i){var n,o,r=this;rj(this,t),this.body=e,this.canvas=i,this.animationSpeed=1/this.renderRefreshRate,this.animationEasingFunction="easeInOutQuint",this.easingTime=0,this.sourceScale=0,this.targetScale=0,this.sourceTranslation=0,this.targetTranslation=0,this.lockedOnNodeId=void 0,this.lockedOnNodeOffset=void 0,this.touchTime=0,this.viewFunction=void 0,this.body.emitter.on("fit",FF(n=this.fit).call(n,this)),this.body.emitter.on("animationFinished",(function(){r.body.emitter.emit("_stopRendering")})),this.body.emitter.on("unlockNode",FF(o=this.releaseNode).call(o,this))}return cW(t,[{key:"setOptions",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};this.options=t}},{key:"fit",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]&&arguments[1];t=function(t,e){var i=pF({nodes:e,minZoomLevel:Number.MIN_VALUE,maxZoomLevel:1},null!=t?t:{});if(!fV(i.nodes))throw new TypeError("Nodes has to be an array of ids.");if(0===i.nodes.length&&(i.nodes=e),!("number"==typeof i.minZoomLevel&&i.minZoomLevel>0))throw new TypeError("Min zoom level has to be a number higher than zero.");if(!("number"==typeof i.maxZoomLevel&&i.minZoomLevel<=i.maxZoomLevel))throw new TypeError("Max zoom level has to be a number higher than min zoom level.");return i}(t,this.body.nodeIndices);var i,n,o=this.canvas.frame.canvas.clientWidth,r=this.canvas.frame.canvas.clientHeight;if(0===o||0===r)n=1,i=r5.getRange(this.body.nodes,t.nodes);else if(!0===e){var s=0;for(var a in this.body.nodes){if(Object.prototype.hasOwnProperty.call(this.body.nodes,a))!0===this.body.nodes[a].predefinedPosition&&(s+=1)}if(s>.5*this.body.nodeIndices.length)return void this.fit(t,!1);i=r5.getRange(this.body.nodes,t.nodes),n=12.662/(this.body.nodeIndices.length+7.4147)+.0964822,n*=Math.min(o/600,r/600)}else{this.body.emitter.emit("_resizeNodes"),i=r5.getRange(this.body.nodes,t.nodes);var d=o/(1.1*Math.abs(i.maxX-i.minX)),l=r/(1.1*Math.abs(i.maxY-i.minY));n=d<=l?d:l}n>t.maxZoomLevel?n=t.maxZoomLevel:n1&&void 0!==arguments[1]?arguments[1]:{};if(void 0!==this.body.nodes[t]){var i={x:this.body.nodes[t].x,y:this.body.nodes[t].y};e.position=i,e.lockedOnNode=t,this.moveTo(e)}else console.error("Node: "+t+" cannot be found.")}},{key:"moveTo",value:function(t){if(void 0!==t){if(null!=t.offset){if(null!=t.offset.x){if(t.offset.x=+t.offset.x,!mJ(t.offset.x))throw new TypeError('The option "offset.x" has to be a finite number.')}else t.offset.x=0;if(null!=t.offset.y){if(t.offset.y=+t.offset.y,!mJ(t.offset.y))throw new TypeError('The option "offset.y" has to be a finite number.')}else t.offset.x=0}else t.offset={x:0,y:0};if(null!=t.position){if(null!=t.position.x){if(t.position.x=+t.position.x,!mJ(t.position.x))throw new TypeError('The option "position.x" has to be a finite number.')}else t.position.x=0;if(null!=t.position.y){if(t.position.y=+t.position.y,!mJ(t.position.y))throw new TypeError('The option "position.y" has to be a finite number.')}else t.position.x=0}else t.position=this.getViewPosition();if(null!=t.scale){if(t.scale=+t.scale,!(t.scale>0))throw new TypeError('The option "scale" has to be a number greater than zero.')}else t.scale=this.body.view.scale;void 0===t.animation&&(t.animation={duration:0}),!1===t.animation&&(t.animation={duration:0}),!0===t.animation&&(t.animation={}),void 0===t.animation.duration&&(t.animation.duration=1e3),void 0===t.animation.easingFunction&&(t.animation.easingFunction="easeInOutQuad"),this.animateView(t)}else t={}}},{key:"animateView",value:function(t){if(void 0!==t){this.animationEasingFunction=t.animation.easingFunction,this.releaseNode(),!0===t.locked&&(this.lockedOnNodeId=t.lockedOnNode,this.lockedOnNodeOffset=t.offset),0!=this.easingTime&&this._transitionRedraw(!0),this.sourceScale=this.body.view.scale,this.sourceTranslation=this.body.view.translation,this.targetScale=t.scale,this.body.view.scale=this.targetScale;var e,i,n=this.canvas.DOMtoCanvas({x:.5*this.canvas.frame.canvas.clientWidth,y:.5*this.canvas.frame.canvas.clientHeight}),o=n.x-t.position.x,r=n.y-t.position.y;if(this.targetTranslation={x:this.sourceTranslation.x+o*this.targetScale+t.offset.x,y:this.sourceTranslation.y+r*this.targetScale+t.offset.y},0===t.animation.duration)if(null!=this.lockedOnNodeId)this.viewFunction=FF(e=this._lockedRedraw).call(e,this),this.body.emitter.on("initRedraw",this.viewFunction);else this.body.view.scale=this.targetScale,this.body.view.translation=this.targetTranslation,this.body.emitter.emit("_requestRedraw");else this.animationSpeed=1/(60*t.animation.duration*.001)||1/60,this.animationEasingFunction=t.animation.easingFunction,this.viewFunction=FF(i=this._transitionRedraw).call(i,this),this.body.emitter.on("initRedraw",this.viewFunction),this.body.emitter.emit("_startRendering")}}},{key:"_lockedRedraw",value:function(){var t=this.body.nodes[this.lockedOnNodeId].x,e=this.body.nodes[this.lockedOnNodeId].y,i=this.canvas.DOMtoCanvas({x:.5*this.canvas.frame.canvas.clientWidth,y:.5*this.canvas.frame.canvas.clientHeight}),n=i.x-t,o=i.y-e,r=this.body.view.translation,s={x:r.x+n*this.body.view.scale+this.lockedOnNodeOffset.x,y:r.y+o*this.body.view.scale+this.lockedOnNodeOffset.y};this.body.view.translation=s}},{key:"releaseNode",value:function(){void 0!==this.lockedOnNodeId&&void 0!==this.viewFunction&&(this.body.emitter.off("initRedraw",this.viewFunction),this.lockedOnNodeId=void 0,this.lockedOnNodeOffset=void 0)}},{key:"_transitionRedraw",value:function(){var t=arguments.length>0&&void 0!==arguments[0]&&arguments[0];this.easingTime+=this.animationSpeed,this.easingTime=!0===t?1:this.easingTime;var e=UK[this.animationEasingFunction](this.easingTime);if(this.body.view.scale=this.sourceScale+(this.targetScale-this.sourceScale)*e,this.body.view.translation={x:this.sourceTranslation.x+(this.targetTranslation.x-this.sourceTranslation.x)*e,y:this.sourceTranslation.y+(this.targetTranslation.y-this.sourceTranslation.y)*e},this.easingTime>=1){var i;if(this.body.emitter.off("initRedraw",this.viewFunction),this.easingTime=0,null!=this.lockedOnNodeId)this.viewFunction=FF(i=this._lockedRedraw).call(i,this),this.body.emitter.on("initRedraw",this.viewFunction);this.body.emitter.emit("animationFinished")}}},{key:"getScale",value:function(){return this.body.view.scale}},{key:"getViewPosition",value:function(){return this.canvas.DOMtoCanvas({x:.5*this.canvas.frame.canvas.clientWidth,y:.5*this.canvas.frame.canvas.clientHeight})}}]),t}();function m5(t){var e,i=t&&t.preventDefault||!1,n=t&&t.container||window,o={},r={keydown:{},keyup:{}},s={};for(e=97;e<=122;e++)s[String.fromCharCode(e)]={code:e-97+65,shift:!1};for(e=65;e<=90;e++)s[String.fromCharCode(e)]={code:e,shift:!0};for(e=0;e<=9;e++)s[""+e]={code:48+e,shift:!1};for(e=1;e<=12;e++)s["F"+e]={code:111+e,shift:!1};for(e=0;e<=9;e++)s["num"+e]={code:96+e,shift:!1};s["num*"]={code:106,shift:!1},s["num+"]={code:107,shift:!1},s["num-"]={code:109,shift:!1},s["num/"]={code:111,shift:!1},s["num."]={code:110,shift:!1},s.left={code:37,shift:!1},s.up={code:38,shift:!1},s.right={code:39,shift:!1},s.down={code:40,shift:!1},s.space={code:32,shift:!1},s.enter={code:13,shift:!1},s.shift={code:16,shift:void 0},s.esc={code:27,shift:!1},s.backspace={code:8,shift:!1},s.tab={code:9,shift:!1},s.ctrl={code:17,shift:!1},s.alt={code:18,shift:!1},s.delete={code:46,shift:!1},s.pageup={code:33,shift:!1},s.pagedown={code:34,shift:!1},s["="]={code:187,shift:!1},s["-"]={code:189,shift:!1},s["]"]={code:221,shift:!1},s["["]={code:219,shift:!1};var a=function(t){l(t,"keydown")},d=function(t){l(t,"keyup")},l=function(t,e){if(void 0!==r[e][t.keyCode]){for(var n=r[e][t.keyCode],o=0;o700&&(this.body.emitter.emit("fit",{duration:700}),this.touchTime=(new Date).valueOf())}},{key:"_stopMovement",value:function(){for(var t in this.boundFunctions)Object.prototype.hasOwnProperty.call(this.boundFunctions,t)&&(this.body.emitter.off("initRedraw",this.boundFunctions[t]),this.body.emitter.emit("_stopRendering"));this.boundFunctions={}}},{key:"_moveUp",value:function(){this.body.view.translation.y+=this.options.keyboard.speed.y}},{key:"_moveDown",value:function(){this.body.view.translation.y-=this.options.keyboard.speed.y}},{key:"_moveLeft",value:function(){this.body.view.translation.x+=this.options.keyboard.speed.x}},{key:"_moveRight",value:function(){this.body.view.translation.x-=this.options.keyboard.speed.x}},{key:"_zoomIn",value:function(){var t=this.body.view.scale,e=this.body.view.scale*(1+this.options.keyboard.speed.zoom),i=this.body.view.translation,n=e/t,o=(1-n)*this.canvas.canvasViewCenter.x+i.x*n,r=(1-n)*this.canvas.canvasViewCenter.y+i.y*n;this.body.view.scale=e,this.body.view.translation={x:o,y:r},this.body.emitter.emit("zoom",{direction:"+",scale:this.body.view.scale,pointer:null})}},{key:"_zoomOut",value:function(){var t=this.body.view.scale,e=this.body.view.scale/(1+this.options.keyboard.speed.zoom),i=this.body.view.translation,n=e/t,o=(1-n)*this.canvas.canvasViewCenter.x+i.x*n,r=(1-n)*this.canvas.canvasViewCenter.y+i.y*n;this.body.view.scale=e,this.body.view.translation={x:o,y:r},this.body.emitter.emit("zoom",{direction:"-",scale:this.body.view.scale,pointer:null})}},{key:"configureKeyboardBindings",value:function(){var t,e,i,n,o,r,s,a,d,l,c,h,u,p,f,m,v,g,y,b,x,_,w,E,k=this;(void 0!==this.keycharm&&this.keycharm.destroy(),!0===this.options.keyboard.enabled)&&(!0===this.options.keyboard.bindToWindow?this.keycharm=m5({container:window,preventDefault:!0}):this.keycharm=m5({container:this.canvas.frame,preventDefault:!0}),this.keycharm.reset(),!0===this.activated&&(FF(t=this.keycharm).call(t,"up",(function(){k.bindToRedraw("_moveUp")}),"keydown"),FF(e=this.keycharm).call(e,"down",(function(){k.bindToRedraw("_moveDown")}),"keydown"),FF(i=this.keycharm).call(i,"left",(function(){k.bindToRedraw("_moveLeft")}),"keydown"),FF(n=this.keycharm).call(n,"right",(function(){k.bindToRedraw("_moveRight")}),"keydown"),FF(o=this.keycharm).call(o,"=",(function(){k.bindToRedraw("_zoomIn")}),"keydown"),FF(r=this.keycharm).call(r,"num+",(function(){k.bindToRedraw("_zoomIn")}),"keydown"),FF(s=this.keycharm).call(s,"num-",(function(){k.bindToRedraw("_zoomOut")}),"keydown"),FF(a=this.keycharm).call(a,"-",(function(){k.bindToRedraw("_zoomOut")}),"keydown"),FF(d=this.keycharm).call(d,"[",(function(){k.bindToRedraw("_zoomOut")}),"keydown"),FF(l=this.keycharm).call(l,"]",(function(){k.bindToRedraw("_zoomIn")}),"keydown"),FF(c=this.keycharm).call(c,"pageup",(function(){k.bindToRedraw("_zoomIn")}),"keydown"),FF(h=this.keycharm).call(h,"pagedown",(function(){k.bindToRedraw("_zoomOut")}),"keydown"),FF(u=this.keycharm).call(u,"up",(function(){k.unbindFromRedraw("_moveUp")}),"keyup"),FF(p=this.keycharm).call(p,"down",(function(){k.unbindFromRedraw("_moveDown")}),"keyup"),FF(f=this.keycharm).call(f,"left",(function(){k.unbindFromRedraw("_moveLeft")}),"keyup"),FF(m=this.keycharm).call(m,"right",(function(){k.unbindFromRedraw("_moveRight")}),"keyup"),FF(v=this.keycharm).call(v,"=",(function(){k.unbindFromRedraw("_zoomIn")}),"keyup"),FF(g=this.keycharm).call(g,"num+",(function(){k.unbindFromRedraw("_zoomIn")}),"keyup"),FF(y=this.keycharm).call(y,"num-",(function(){k.unbindFromRedraw("_zoomOut")}),"keyup"),FF(b=this.keycharm).call(b,"-",(function(){k.unbindFromRedraw("_zoomOut")}),"keyup"),FF(x=this.keycharm).call(x,"[",(function(){k.unbindFromRedraw("_zoomOut")}),"keyup"),FF(_=this.keycharm).call(_,"]",(function(){k.unbindFromRedraw("_zoomIn")}),"keyup"),FF(w=this.keycharm).call(w,"pageup",(function(){k.unbindFromRedraw("_zoomIn")}),"keyup"),FF(E=this.keycharm).call(E,"pagedown",(function(){k.unbindFromRedraw("_zoomOut")}),"keyup")))}}]),t}();function g5(t,e){var i=void 0!==tV&&oj(t)||t["@@iterator"];if(!i){if(fV(t)||(i=function(t,e){var i;if(!t)return;if("string"==typeof t)return y5(t,e);var n=aV(i=Object.prototype.toString.call(t)).call(i,8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Bz(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return y5(t,e)}(t))||e){i&&(t=i);var n=0,o=function(){};return{s:o,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function y5(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i50&&(this.drag.pointer=this.getPointer(t.center),this.drag.pinched=!1,this.pinch.scale=this.body.view.scale,this.touchTime=(new Date).valueOf())}},{key:"onTap",value:function(t){var e=this.getPointer(t.center),i=this.selectionHandler.options.multiselect&&(t.changedPointers[0].ctrlKey||t.changedPointers[0].metaKey);this.checkSelectionChanges(e,i),this.selectionHandler.commitAndEmit(e,t),this.selectionHandler.generateClickEvent("click",t,e)}},{key:"onDoubleTap",value:function(t){var e=this.getPointer(t.center);this.selectionHandler.generateClickEvent("doubleClick",t,e)}},{key:"onHold",value:function(t){var e=this.getPointer(t.center),i=this.selectionHandler.options.multiselect;this.checkSelectionChanges(e,i),this.selectionHandler.commitAndEmit(e,t),this.selectionHandler.generateClickEvent("click",t,e),this.selectionHandler.generateClickEvent("hold",t,e)}},{key:"onRelease",value:function(t){if((new Date).valueOf()-this.touchTime>10){var e=this.getPointer(t.center);this.selectionHandler.generateClickEvent("release",t,e),this.touchTime=(new Date).valueOf()}}},{key:"onContext",value:function(t){var e=this.getPointer({x:t.clientX,y:t.clientY});this.selectionHandler.generateClickEvent("oncontext",t,e)}},{key:"checkSelectionChanges",value:function(t){!0===(arguments.length>1&&void 0!==arguments[1]&&arguments[1])?this.selectionHandler.selectAdditionalOnPoint(t):this.selectionHandler.selectOnPoint(t)}},{key:"_determineDifference",value:function(t,e){var i=function(t,e){for(var i=[],n=0;n=o.minX&&i.x<=o.maxX&&i.y>=o.minY&&i.y<=o.maxY}));HV(r).call(r,(function(t){return e.selectionHandler.selectObject(e.body.nodes[t])}));var s=this.getPointer(t.center);this.selectionHandler.commitAndEmit(s,t),this.selectionHandler.generateClickEvent("dragEnd",t,this.getPointer(t.center),void 0,!0),this.body.emitter.emit("_requestRedraw")}else{var a=this.drag.selection;a&&a.length?(HV(a).call(a,(function(t){t.node.options.fixed.x=t.xFixed,t.node.options.fixed.y=t.yFixed})),this.selectionHandler.generateClickEvent("dragEnd",t,this.getPointer(t.center)),this.body.emitter.emit("startSimulation")):(this.selectionHandler.generateClickEvent("dragEnd",t,this.getPointer(t.center),void 0,!0),this.body.emitter.emit("_requestRedraw"))}}},{key:"onPinch",value:function(t){var e=this.getPointer(t.center);this.drag.pinched=!0,void 0===this.pinch.scale&&(this.pinch.scale=1);var i=this.pinch.scale*t.scale;this.zoom(i,e)}},{key:"zoom",value:function(t,e){if(!0===this.options.zoomView){var i=this.body.view.scale;t<1e-5&&(t=1e-5),t>10&&(t=10);var n=void 0;void 0!==this.drag&&!0===this.drag.dragging&&(n=this.canvas.DOMtoCanvas(this.drag.pointer));var o=this.body.view.translation,r=t/i,s=(1-r)*e.x+o.x*r,a=(1-r)*e.y+o.y*r;if(this.body.view.scale=t,this.body.view.translation={x:s,y:a},null!=n){var d=this.canvas.canvasToDOM(n);this.drag.pointer.x=d.x,this.drag.pointer.y=d.y}this.body.emitter.emit("_requestRedraw"),i0&&(this.popupObj=l[c[c.length-1]],r=!0)}if(void 0===this.popupObj&&!1===r){for(var u,p=this.body.edgeIndices,f=this.body.edges,m=[],v=0;v0&&(this.popupObj=f[m[m.length-1]],s="edge")}void 0!==this.popupObj?this.popupObj.id!==o&&(void 0===this.popup&&(this.popup=new nZ(this.canvas.frame)),this.popup.popupTargetType=s,this.popup.popupTargetId=this.popupObj.id,this.popup.setPosition(t.x+3,t.y-5),this.popup.setText(this.popupObj.getTitle()),this.popup.show(),this.body.emitter.emit("showPopup",this.popupObj.id)):void 0!==this.popup&&(this.popup.hide(),this.body.emitter.emit("hidePopup"))}},{key:"_checkHidePopup",value:function(t){var e=this.selectionHandler._pointerToPositionObject(t),i=!1;if("node"===this.popup.popupTargetType){if(void 0!==this.body.nodes[this.popup.popupTargetId]&&!0===(i=this.body.nodes[this.popup.popupTargetId].isOverlappingWith(e))){var n=this.selectionHandler.getNodeAt(t);i=void 0!==n&&n.id===this.popup.popupTargetId}}else void 0===this.selectionHandler.getNodeAt(t)&&void 0!==this.body.edges[this.popup.popupTargetId]&&(i=this.body.edges[this.popup.popupTargetId].isOverlappingWith(e));!1===i&&(this.popupObj=void 0,this.popup.hide(),this.body.emitter.emit("hidePopup"))}}]),t}();$Q("Set",(function(t){return function(){return t(this,arguments.length?arguments[0]:void 0)}}),lJ);var x5=KA(GR.Set),_5=cR,w5=WQ,E5=dQ.getWeakData,k5=OQ,O5=KD,C5=zR,S5=YR,T5=wQ,I5=qP,A5=DN.set,R5=DN.getterFor,P5=OH.find,D5=OH.findIndex,M5=_5([].splice),F5=0,N5=function(t){return t.frozen||(t.frozen=new B5)},B5=function(){this.entries=[]},L5=function(t,e){return P5(t.entries,(function(t){return t[0]===e}))};B5.prototype={get:function(t){var e=L5(this,t);if(e)return e[1]},has:function(t){return!!L5(this,t)},set:function(t,e){var i=L5(this,t);i?i[1]=e:this.entries.push([t,e])},delete:function(t){var e=D5(this.entries,(function(e){return e[0]===t}));return~e&&M5(this.entries,e,1),!!~e}};var z5,j5={getConstructor:function(t,e,i,n){var o=t((function(t,o){k5(t,r),A5(t,{type:e,id:F5++,frozen:void 0}),C5(o)||T5(o,t[n],{that:t,AS_ENTRIES:i})})),r=o.prototype,s=R5(e),a=function(t,e,i){var n=s(t),o=E5(O5(e),!0);return!0===o?N5(n).set(e,i):o[n.id]=i,t};return w5(r,{delete:function(t){var e=s(this);if(!S5(t))return!1;var i=E5(t);return!0===i?N5(e).delete(t):i&&I5(i,e.id)&&delete i[e.id]},has:function(t){var e=s(this);if(!S5(t))return!1;var i=E5(t);return!0===i?N5(e).has(t):i&&I5(i,e.id)}}),w5(r,i?{get:function(t){var e=s(this);if(S5(t)){var i=E5(t);return!0===i?N5(e).get(t):i?i[e.id]:void 0}},set:function(t,e){return a(this,t,e)}}:{add:function(t){return a(this,t,!0)}}),o}},H5=qZ,$5=QA,U5=cR,W5=WQ,V5=dQ,q5=$Q,X5=j5,Y5=YR,G5=DN.enforce,K5=JA,Z5=vN,Q5=Object,J5=Array.isArray,t3=Q5.isExtensible,e3=Q5.isFrozen,i3=Q5.isSealed,n3=Q5.freeze,o3=Q5.seal,r3={},s3={},a3=!$5.ActiveXObject&&"ActiveXObject"in $5,d3=function(t){return function(){return t(this,arguments.length?arguments[0]:void 0)}},l3=q5("WeakMap",d3,X5),c3=l3.prototype,h3=U5(c3.set);if(Z5)if(a3){z5=X5.getConstructor(d3,"WeakMap",!0),V5.enable();var u3=U5(c3.delete),p3=U5(c3.has),f3=U5(c3.get);W5(c3,{delete:function(t){if(Y5(t)&&!t3(t)){var e=G5(this);return e.frozen||(e.frozen=new z5),u3(this,t)||e.frozen.delete(t)}return u3(this,t)},has:function(t){if(Y5(t)&&!t3(t)){var e=G5(this);return e.frozen||(e.frozen=new z5),p3(this,t)||e.frozen.has(t)}return p3(this,t)},get:function(t){if(Y5(t)&&!t3(t)){var e=G5(this);return e.frozen||(e.frozen=new z5),p3(this,t)?f3(this,t):e.frozen.get(t)}return f3(this,t)},set:function(t,e){if(Y5(t)&&!t3(t)){var i=G5(this);i.frozen||(i.frozen=new z5),p3(this,t)?h3(this,t,e):i.frozen.set(t,e)}else h3(this,t,e);return this}})}else H5&&K5((function(){var t=n3([]);return h3(new l3,t,1),!e3(t)}))&&W5(c3,{set:function(t,e){var i;return J5(t)&&(e3(t)?i=r3:i3(t)&&(i=s3)),h3(this,t,e),i===r3&&n3(t),i===s3&&o3(t),this}});var m3,v3,g3,y3,b3,x3=KA(GR.WeakMap);function _3(t,e,i,n){if("a"===i&&!n)throw new TypeError("Private accessor was defined without a getter");if("function"==typeof e?t!==e||!n:!e.has(t))throw new TypeError("Cannot read private member from an object whose class did not declare it");return"m"===i?n:"a"===i?n.call(t):n?n.value:e.get(t)}function w3(t,e,i,n,o){if("function"==typeof e?t!==e||!o:!e.has(t))throw new TypeError("Cannot write private member to an object whose class did not declare it");return e.set(t,i),i}function E3(t,e){var i=void 0!==tV&&oj(t)||t["@@iterator"];if(!i){if(fV(t)||(i=function(t,e){var i;if(!t)return;if("string"==typeof t)return k3(t,e);var n=aV(i=Object.prototype.toString.call(t)).call(i,8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Bz(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return k3(t,e)}(t))||e){i&&(t=i);var n=0,o=function(){};return{s:o,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function k3(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i0&&void 0!==arguments[0]?arguments[0]:function(){};rj(this,t),g3.set(this,new C3),y3.set(this,new C3),b3.set(this,void 0),w3(this,b3,e)}return cW(t,[{key:"sizeNodes",get:function(){return _3(this,g3,"f").size}},{key:"sizeEdges",get:function(){return _3(this,y3,"f").size}},{key:"getNodes",value:function(){return _3(this,g3,"f").getSelection()}},{key:"getEdges",value:function(){return _3(this,y3,"f").getSelection()}},{key:"addNodes",value:function(){var t;(t=_3(this,g3,"f")).add.apply(t,arguments)}},{key:"addEdges",value:function(){var t;(t=_3(this,y3,"f")).add.apply(t,arguments)}},{key:"deleteNodes",value:function(t){_3(this,g3,"f").delete(t)}},{key:"deleteEdges",value:function(t){_3(this,y3,"f").delete(t)}},{key:"clear",value:function(){_3(this,g3,"f").clear(),_3(this,y3,"f").clear()}},{key:"commit",value:function(){for(var t,e,i={nodes:_3(this,g3,"f").commit(),edges:_3(this,y3,"f").commit()},n=arguments.length,o=new Array(n),r=0;r=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function I3(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i4&&void 0!==arguments[4]&&arguments[4],r=this._initBaseEvent(e,i);if(!0===o)r.nodes=[],r.edges=[];else{var s=this.getSelection();r.nodes=s.nodes,r.edges=s.edges}void 0!==n&&(r.previousSelection=n),"click"==t&&(r.items=this.getClickedItems(i)),void 0!==e.controlEdge&&(r.controlEdge=e.controlEdge),this.body.emitter.emit(t,r)}},{key:"selectObject",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.options.selectConnectedEdges;if(void 0!==t){if(t instanceof U1){var i;if(!0===e)(i=this._selectionAccumulator).addEdges.apply(i,JW(t.edges));this._selectionAccumulator.addNodes(t)}else this._selectionAccumulator.addEdges(t);return!0}return!1}},{key:"deselectObject",value:function(t){!0===t.isSelected()&&(t.selected=!1,this._removeFromSelection(t))}},{key:"_getAllNodesOverlappingWith",value:function(t){for(var e=[],i=this.body.nodes,n=0;n1&&void 0!==arguments[1])||arguments[1],i=this._pointerToPositionObject(t),n=this._getAllNodesOverlappingWith(i);return n.length>0?!0===e?this.body.nodes[n[n.length-1]]:n[n.length-1]:void 0}},{key:"_getEdgesOverlappingWith",value:function(t,e){for(var i=this.body.edges,n=0;n1&&void 0!==arguments[1])||arguments[1],i=this.canvas.DOMtoCanvas(t),n=10,o=null,r=this.body.edges,s=0;s0&&(this.generateClickEvent("deselectEdge",e,t,o),i=!0),n.nodes.deleted.length>0&&(this.generateClickEvent("deselectNode",e,t,o),i=!0),n.nodes.added.length>0&&(this.generateClickEvent("selectNode",e,t),i=!0),n.edges.added.length>0&&(this.generateClickEvent("selectEdge",e,t),i=!0),!0===i&&this.generateClickEvent("select",e,t)}},{key:"getSelection",value:function(){return{nodes:this.getSelectedNodeIds(),edges:this.getSelectedEdgeIds()}}},{key:"getSelectedNodes",value:function(){return this._selectionAccumulator.getNodes()}},{key:"getSelectedEdges",value:function(){return this._selectionAccumulator.getEdges()}},{key:"getSelectedNodeIds",value:function(){var t;return _V(t=this._selectionAccumulator.getNodes()).call(t,(function(t){return t.id}))}},{key:"getSelectedEdgeIds",value:function(){var t;return _V(t=this._selectionAccumulator.getEdges()).call(t,(function(t){return t.id}))}},{key:"setSelection",value:function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};if(!t||!t.nodes&&!t.edges)throw new TypeError("Selection must be an object with nodes and/or edges properties");if((e.unselectAll||void 0===e.unselectAll)&&this.unselectAll(),t.nodes){var i,n=T3(t.nodes);try{for(n.s();!(i=n.n()).done;){var o=i.value,r=this.body.nodes[o];if(!r)throw new RangeError('Node with id "'+o+'" not found');this.selectObject(r,e.highlightEdges)}}catch(t){n.e(t)}finally{n.f()}}if(t.edges){var s,a=T3(t.edges);try{for(a.s();!(s=a.n()).done;){var d=s.value,l=this.body.edges[d];if(!l)throw new RangeError('Edge with id "'+d+'" not found');this.selectObject(l)}}catch(t){a.e(t)}finally{a.f()}}this.body.emitter.emit("_requestRedraw"),this._selectionAccumulator.commit()}},{key:"selectNodes",value:function(t){var e=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];if(!t||void 0===t.length)throw"Selection must be an array with ids";this.setSelection({nodes:t},{highlightEdges:e})}},{key:"selectEdges",value:function(t){if(!t||void 0===t.length)throw"Selection must be an array with ids";this.setSelection({edges:t})}},{key:"updateSelection",value:function(){for(var t in this._selectionAccumulator.getNodes())Object.prototype.hasOwnProperty.call(this.body.nodes,t.id)||this._selectionAccumulator.deleteNodes(t);for(var e in this._selectionAccumulator.getEdges())Object.prototype.hasOwnProperty.call(this.body.edges,e.id)||this._selectionAccumulator.deleteEdges(e)}},{key:"getClickedItems",value:function(t){for(var e=this.canvas.DOMtoCanvas(t),i=[],n=this.body.nodeIndices,o=this.body.nodes,r=n.length-1;r>=0;r--){var s=o[n[r]].getItemsOnPoint(e);i.push.apply(i,s)}for(var a=this.body.edgeIndices,d=this.body.edges,l=a.length-1;l>=0;l--){var c=d[a[l]].getItemsOnPoint(e);i.push.apply(i,c)}return i}}]),t}(),R3=Qj,P3=Math.floor,D3=function(t,e){var i=t.length,n=P3(i/2);return i<8?M3(t,e):F3(t,D3(R3(t,0,n),e),D3(R3(t,n),e),e)},M3=function(t,e){for(var i,n,o=t.length,r=1;r0;)t[n]=t[--n];n!==r++&&(t[n]=i)}return t},F3=function(t,e,i,n){for(var o=e.length,r=i.length,s=0,a=0;s3)){if(J3)return!0;if(e4)return e4<603;var t,e,i,n,o="";for(t=65;t<76;t++){switch(e=String.fromCharCode(t),t){case 66:case 69:case 70:case 72:i=3;break;case 68:case 71:i=4;break;default:i=2}for(n=0;n<47;n++)i4.push({k:e+n,v:i})}for(i4.sort((function(t,e){return e.v-t.v})),n=0;nY3(i)?1:-1}}(t)),i=q3(o),n=0;n=0:a>d;d+=l)d in s&&(o=i(o,s[d],d,r));return o}},_4={left:x4(!1),right:x4(!0)},w4="process"===fR(QA.process),E4=_4.left;wM({target:"Array",proto:!0,forced:!w4&&lP>79&&lP<83||!AV("reduce")},{reduce:function(t){var e=arguments.length;return E4(this,t,e,e>1?arguments[1]:void 0)}});var k4=TF("Array").reduce,O4=eP,C4=k4,S4=Array.prototype,T4=function(t){var e=t.reduce;return t===S4||O4(S4,t)&&e===S4.reduce?C4:e},I4=KA(T4);function A4(t){var e=function(){if("undefined"==typeof Reflect||!X0)return!1;if(X0.sham)return!1;if("function"==typeof Proxy)return!0;try{return Boolean.prototype.valueOf.call(X0(Boolean,[],(function(){}))),!0}catch(t){return!1}}();return function(){var i,n=e1(t);if(e){var o=e1(this).constructor;i=X0(n,arguments,o)}else i=n.apply(this,arguments);return J0(this,i)}}var R4=function(){function t(){rj(this,t)}return cW(t,[{key:"abstract",value:function(){throw new Error("Can't instantiate abstract class!")}},{key:"fake_use",value:function(){}},{key:"curveType",value:function(){return this.abstract()}},{key:"getPosition",value:function(t){return this.fake_use(t),this.abstract()}},{key:"setPosition",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;this.fake_use(t,e,i),this.abstract()}},{key:"getTreeSize",value:function(t){return this.fake_use(t),this.abstract()}},{key:"sort",value:function(t){this.fake_use(t),this.abstract()}},{key:"fix",value:function(t,e){this.fake_use(t,e),this.abstract()}},{key:"shift",value:function(t,e){this.fake_use(t,e),this.abstract()}}]),t}(),P4=function(t){Q0(i,t);var e=A4(i);function i(t){var n;return rj(this,i),(n=e.call(this)).layout=t,n}return cW(i,[{key:"curveType",value:function(){return"horizontal"}},{key:"getPosition",value:function(t){return t.x}},{key:"setPosition",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;void 0!==i&&this.layout.hierarchical.addToOrdering(t,i),t.x=e}},{key:"getTreeSize",value:function(t){var e=this.layout.hierarchical.getTreeSize(this.layout.body.nodes,t);return{min:e.min_x,max:e.max_x}}},{key:"sort",value:function(t){f4(t).call(t,(function(t,e){return t.x-e.x}))}},{key:"fix",value:function(t,e){t.y=this.layout.options.hierarchical.levelSeparation*e,t.options.fixed.y=!0}},{key:"shift",value:function(t,e){this.layout.body.nodes[t].x+=e}}]),i}(R4),D4=function(t){Q0(i,t);var e=A4(i);function i(t){var n;return rj(this,i),(n=e.call(this)).layout=t,n}return cW(i,[{key:"curveType",value:function(){return"vertical"}},{key:"getPosition",value:function(t){return t.y}},{key:"setPosition",value:function(t,e){var i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:void 0;void 0!==i&&this.layout.hierarchical.addToOrdering(t,i),t.y=e}},{key:"getTreeSize",value:function(t){var e=this.layout.hierarchical.getTreeSize(this.layout.body.nodes,t);return{min:e.min_y,max:e.max_y}}},{key:"sort",value:function(t){f4(t).call(t,(function(t,e){return t.y-e.y}))}},{key:"fix",value:function(t,e){t.x=this.layout.options.hierarchical.levelSeparation*e,t.options.fixed.x=!0}},{key:"shift",value:function(t,e){this.layout.body.nodes[t].y+=e}}]),i}(R4),M4=OH.every;wM({target:"Array",proto:!0,forced:!AV("every")},{every:function(t){return M4(this,t,arguments.length>1?arguments[1]:void 0)}});var F4=TF("Array").every,N4=eP,B4=F4,L4=Array.prototype,z4=function(t){var e=t.every;return t===L4||N4(L4,t)&&e===L4.every?B4:e},j4=KA(z4);function H4(t,e){var i=void 0!==tV&&oj(t)||t["@@iterator"];if(!i){if(fV(t)||(i=function(t,e){var i;if(!t)return;if("string"==typeof t)return $4(t,e);var n=aV(i=Object.prototype.toString.call(t)).call(i,8,-1);"Object"===n&&t.constructor&&(n=t.constructor.name);if("Map"===n||"Set"===n)return Bz(t);if("Arguments"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n))return $4(t,e)}(t))||e){i&&(t=i);var n=0,o=function(){};return{s:o,n:function(){return n>=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function $4(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i=e[n])&&(e[n]=e[i]+1)})),e}function W4(t,e,i,n){var o,r,s=NX(null),a=I4(o=JW(E0(n).call(n))).call(o,(function(t,e){return t+1+e.edges.length}),0),d=i+"Id",l="to"===i?1:-1,c=H4(n);try{var h,u=function(){var o=QW(r.value,2),c=o[0],h=o[1];if(!n.has(c)||!t(h))return 0;s[c]=0;for(var u,p,f=[h],m=0,v=function(){var t,o;if(!n.has(c))return 0;var r=s[u.id]+l;if(HV(t=Jq(o=u.edges).call(o,(function(t){return t.connected&&t.to!==t.from&&t[i]!==u&&n.has(t.toId)&&n.has(t.fromId)}))).call(t,(function(t){var n=t[d],o=s[n];(null==o||e(r,o))&&(s[n]=r,f.push(t[i]))})),m>a)return{v:{v:U4(n,s)}};++m};u=f.pop();)if(0!==(p=v())&&p)return p.v};for(c.s();!(r=c.n()).done;)if(0!==(h=u())&&h)return h.v}catch(t){c.e(t)}finally{c.f()}return s}var V4=function(){function t(){rj(this,t),this.childrenReference={},this.parentReference={},this.trees={},this.distributionOrdering={},this.levels={},this.distributionIndex={},this.isTree=!1,this.treeIndex=-1}return cW(t,[{key:"addRelation",value:function(t,e){void 0===this.childrenReference[t]&&(this.childrenReference[t]=[]),this.childrenReference[t].push(e),void 0===this.parentReference[e]&&(this.parentReference[e]=[]),this.parentReference[e].push(t)}},{key:"checkIfTree",value:function(){for(var t in this.parentReference)if(this.parentReference[t].length>1)return void(this.isTree=!1);this.isTree=!0}},{key:"numTrees",value:function(){return this.treeIndex+1}},{key:"setTreeIndex",value:function(t,e){void 0!==e&&void 0===this.trees[t.id]&&(this.trees[t.id]=e,this.treeIndex=Math.max(e,this.treeIndex))}},{key:"ensureLevel",value:function(t){void 0===this.levels[t]&&(this.levels[t]=0)}},{key:"getMaxLevel",value:function(t){var e=this,i={};return function t(n){if(void 0!==i[n])return i[n];var o=e.levels[n];if(e.childrenReference[n]){var r=e.childrenReference[n];if(r.length>0)for(var s=0;s0&&(i.levelSeparation*=-1):i.levelSeparation<0&&(i.levelSeparation*=-1),this.setDirectionStrategy(),this.body.emitter.emit("_resetHierarchicalLayout"),this.adaptAllOptionsForHierarchicalLayout(e);if(!0===n)return this.body.emitter.emit("refresh"),TK(e,this.optionsBackup)}return e}},{key:"_resetRNG",value:function(t){this.initialRandomSeed=t,this._rng=fK(this.initialRandomSeed)}},{key:"adaptAllOptionsForHierarchicalLayout",value:function(t){if(!0===this.options.hierarchical.enabled){var e=this.optionsBackup.physics;void 0===t.physics||!0===t.physics?(t.physics={enabled:void 0===e.enabled||e.enabled,solver:"hierarchicalRepulsion"},e.enabled=void 0===e.enabled||e.enabled,e.solver=e.solver||"barnesHut"):"object"===sW(t.physics)?(e.enabled=void 0===t.physics.enabled||t.physics.enabled,e.solver=t.physics.solver||"barnesHut",t.physics.solver="hierarchicalRepulsion"):!1!==t.physics&&(e.solver="barnesHut",t.physics={solver:"hierarchicalRepulsion"});var i=this.direction.curveType();if(void 0===t.edges)this.optionsBackup.edges={smooth:{enabled:!0,type:"dynamic"}},t.edges={smooth:!1};else if(void 0===t.edges.smooth)this.optionsBackup.edges={smooth:{enabled:!0,type:"dynamic"}},t.edges.smooth=!1;else if("boolean"==typeof t.edges.smooth)this.optionsBackup.edges={smooth:t.edges.smooth},t.edges.smooth={enabled:t.edges.smooth,type:i};else{var n=t.edges.smooth;void 0!==n.type&&"dynamic"!==n.type&&(i=n.type),this.optionsBackup.edges={smooth:{enabled:void 0===n.enabled||n.enabled,type:void 0===n.type?"dynamic":n.type,roundness:void 0===n.roundness?.5:n.roundness,forceDirection:void 0!==n.forceDirection&&n.forceDirection}},t.edges.smooth={enabled:void 0===n.enabled||n.enabled,type:i,roundness:void 0===n.roundness?.5:n.roundness,forceDirection:void 0!==n.forceDirection&&n.forceDirection}}this.body.emitter.emit("_forceDisableDynamicCurves",i)}return t}},{key:"positionInitially",value:function(t){if(!0!==this.options.hierarchical.enabled){this._resetRNG(this.initialRandomSeed);for(var e=t.length+50,i=0;io){for(var s=t.length;t.length>o&&n<=10;){n+=1;var a=t.length;if(n%3==0?this.body.modules.clustering.clusterBridges(r):this.body.modules.clustering.clusterOutliers(r),a==t.length&&n%3!=0)return this._declusterAll(),this.body.emitter.emit("_layoutFailed"),void console.info("This network could not be positioned by this version of the improved layout algorithm. Please disable improvedLayout for better performance.")}this.body.modules.kamadaKawai.setOptions({springLength:Math.max(150,2*s)})}n>10&&console.info("The clustering didn't succeed within the amount of interations allowed, progressing with partial result."),this.body.modules.kamadaKawai.solve(t,this.body.edgeIndices,!0),this._shiftToCenter();for(var d=0;d0){var t,e,i=!1,n=!1;for(e in this.lastNodeOnLevel={},this.hierarchical=new V4,this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,e)&&(void 0!==(t=this.body.nodes[e]).options.level?(i=!0,this.hierarchical.levels[e]=t.options.level):n=!0);if(!0===n&&!0===i)throw new Error("To use the hierarchical layout, nodes require either no predefined levels or levels have to be defined for all nodes.");if(!0===n){var o=this.options.hierarchical.sortMethod;"hubsize"===o?this._determineLevelsByHubsize():"directed"===o?this._determineLevelsDirected():"custom"===o&&this._determineLevelsCustomCallback()}for(var r in this.body.nodes)Object.prototype.hasOwnProperty.call(this.body.nodes,r)&&this.hierarchical.ensureLevel(r);var s=this._getDistribution();this._generateMap(),this._placeNodesByHierarchy(s),this._condenseHierarchy(),this._shiftToCenter()}}},{key:"_condenseHierarchy",value:function(){var t=this,e=!1,i={},n=function(e,i){var n=t.hierarchical.trees;for(var o in n)Object.prototype.hasOwnProperty.call(n,o)&&n[o]===e&&t.direction.shift(o,i)},o=function(){for(var e=[],i=0;i0)for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:1e9,n=1e9,o=1e9,r=1e9,s=-1e9;for(var a in e)if(Object.prototype.hasOwnProperty.call(e,a)){var d=t.body.nodes[a],l=t.hierarchical.levels[d.id],c=t.direction.getPosition(d),h=QW(t._getSpaceAroundNode(d,e),2),u=h[0],p=h[1];n=Math.min(u,n),o=Math.min(p,o),l<=i&&(r=Math.min(c,r),s=Math.max(c,s))}return[r,s,n,o]},a=function(e,i,n){for(var o=t.hierarchical,r=0;r1)for(var d=0;d2&&void 0!==arguments[2]&&arguments[2],a=t.direction.getPosition(i),d=t.direction.getPosition(n),l=Math.abs(d-a),c=t.options.hierarchical.nodeSpacing;if(l>c){var h={},u={};r(i,h),r(n,u);var p=function(e,i){var n=t.hierarchical.getMaxLevel(e.id),o=t.hierarchical.getMaxLevel(i.id);return Math.min(n,o)}(i,n),f=s(h,p),m=s(u,p),v=f[1],g=m[0],y=m[2];if(Math.abs(v-g)>c){var b=v-g+c;b<-y+c&&(b=-y+c),b<0&&(t._shiftBlock(n.id,b),e=!0,!0===o&&t._centerParent(n))}}},l=function(n,o){for(var a=o.id,d=o.edges,l=t.hierarchical.levels[o.id],c=t.options.hierarchical.levelSeparation*t.options.hierarchical.levelSeparation,h={},u=[],p=0;p0?p=Math.min(u,h-t.options.hierarchical.nodeSpacing):u<0&&(p=-Math.min(-u,c-t.options.hierarchical.nodeSpacing)),0!=p&&(t._shiftBlock(o.id,p),e=!0)}(b),function(i){var n=t.direction.getPosition(o),r=QW(t._getSpaceAroundNode(o),2),s=r[0],a=r[1],d=i-n,l=n;d>0?l=Math.min(n+(a-t.options.hierarchical.nodeSpacing),i):d<0&&(l=Math.max(n-(s-t.options.hierarchical.nodeSpacing),i)),l!==n&&(t.direction.setPosition(o,l),e=!0)}(b=y(n,d))};!0===this.options.hierarchical.blockShifting&&(function(i){var n=t.hierarchical.getLevels();n=ZV(n).call(n);for(var o=0;o0&&Math.abs(h)0&&(d=this.direction.getPosition(n[r-1])+a),this.direction.setPosition(s,d,e),this._validatePositionAndContinue(s,e,d),o++}}}}},{key:"_placeBranchNodes",value:function(t,e){var i,n=this.hierarchical.childrenReference[t];if(void 0!==n){for(var o=[],r=0;re&&void 0===this.positionedNodes[a.id]))return;var l=this.options.hierarchical.nodeSpacing,c=void 0;c=0===s?this.direction.getPosition(this.body.nodes[t]):this.direction.getPosition(o[s-1])+l,this.direction.setPosition(a,c,d),this._validatePositionAndContinue(a,d,c)}var h=this._getCenterPosition(o);this.direction.setPosition(this.body.nodes[t],h,e)}}},{key:"_validatePositionAndContinue",value:function(t,e,i){if(this.hierarchical.isTree){if(void 0!==this.lastNodeOnLevel[e]){var n=this.direction.getPosition(this.body.nodes[this.lastNodeOnLevel[e]]);if(i-nt}),"from",t)}(i),this.hierarchical.setMinLevelToZero(this.body.nodes)}},{key:"_generateMap",value:function(){var t=this;this._crawlNetwork((function(e,i){t.hierarchical.levels[i.id]>t.hierarchical.levels[e.id]&&t.hierarchical.addRelation(e.id,i.id)})),this.hierarchical.checkIfTree()}},{key:"_crawlNetwork",value:function(){var t=this,e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){},i=arguments.length>1?arguments[1]:void 0,n={},o=function i(o,r){if(void 0===n[o.id]){var s;t.hierarchical.setTreeIndex(o,r),n[o.id]=!0;for(var a=t._getActiveEdges(o),d=0;d=t.length?{done:!0}:{done:!1,value:t[n++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var r,s=!0,a=!1;return{s:function(){i=i.call(t)},n:function(){var t=i.next();return s=t.done,t},e:function(t){a=!0,r=t},f:function(){try{s||null==i.return||i.return()}finally{if(a)throw r}}}}function Y4(t,e){(null==e||e>t.length)&&(e=t.length);for(var i=0,n=new Array(e);i0&&!1!==this.options.deleteNode||0===i&&!1!==this.options.deleteEdge)&&(!0===s&&this._createSeperator(4),this._createDeleteButton(r)),this._bindElementEvents(this.closeDiv,FF(t=this.toggleEditMode).call(t,this)),this._temporaryBindEvent("select",FF(e=this.showManipulatorToolbar).call(e,this))}this.body.emitter.emit("_redraw")}},{key:"addNodeMode",value:function(){var t;if(!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="addNode",!0===this.guiEnabled){var e,i=this.options.locales[this.options.locale];this.manipulationDOM={},this._createBackButton(i),this._createSeperator(),this._createDescription(i.addDescription||this.options.locales.en.addDescription),this._bindElementEvents(this.closeDiv,FF(e=this.toggleEditMode).call(e,this))}this._temporaryBindEvent("click",FF(t=this._performAddNode).call(t,this))}},{key:"editNode",value:function(){var t=this;!0!==this.editMode&&this.enableEditMode(),this._clean();var e=this.selectionHandler.getSelectedNodes()[0];if(void 0!==e){if(this.inMode="editNode","function"!=typeof this.options.editNode)throw new Error("No function has been configured to handle the editing of nodes.");if(!0!==e.isCluster){var i=TK({},e.options,!1);if(i.x=e.x,i.y=e.y,2!==this.options.editNode.length)throw new Error("The function for edit does not support two arguments (data, callback)");this.options.editNode(i,(function(e){null!=e&&"editNode"===t.inMode&&t.body.data.nodes.getDataSet().update(e),t.showManipulatorToolbar()}))}else alert(this.options.locales[this.options.locale].editClusterError||this.options.locales.en.editClusterError)}else this.showManipulatorToolbar()}},{key:"addEdgeMode",value:function(){var t,e,i,n,o;if(!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="addEdge",!0===this.guiEnabled){var r,s=this.options.locales[this.options.locale];this.manipulationDOM={},this._createBackButton(s),this._createSeperator(),this._createDescription(s.edgeDescription||this.options.locales.en.edgeDescription),this._bindElementEvents(this.closeDiv,FF(r=this.toggleEditMode).call(r,this))}this._temporaryBindUI("onTouch",FF(t=this._handleConnect).call(t,this)),this._temporaryBindUI("onDragEnd",FF(e=this._finishConnect).call(e,this)),this._temporaryBindUI("onDrag",FF(i=this._dragControlNode).call(i,this)),this._temporaryBindUI("onRelease",FF(n=this._finishConnect).call(n,this)),this._temporaryBindUI("onDragStart",FF(o=this._dragStartEdge).call(o,this)),this._temporaryBindUI("onHold",(function(){}))}},{key:"editEdgeMode",value:function(){if(!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="editEdge","object"!==sW(this.options.editEdge)||"function"!=typeof this.options.editEdge.editWithoutDrag||(this.edgeBeingEditedId=this.selectionHandler.getSelectedEdgeIds()[0],void 0===this.edgeBeingEditedId)){if(!0===this.guiEnabled){var t,e=this.options.locales[this.options.locale];this.manipulationDOM={},this._createBackButton(e),this._createSeperator(),this._createDescription(e.editEdgeDescription||this.options.locales.en.editEdgeDescription),this._bindElementEvents(this.closeDiv,FF(t=this.toggleEditMode).call(t,this))}if(this.edgeBeingEditedId=this.selectionHandler.getSelectedEdgeIds()[0],void 0!==this.edgeBeingEditedId){var i,n,o,r,s=this.body.edges[this.edgeBeingEditedId],a=this._getNewTargetNode(s.from.x,s.from.y),d=this._getNewTargetNode(s.to.x,s.to.y);this.temporaryIds.nodes.push(a.id),this.temporaryIds.nodes.push(d.id),this.body.nodes[a.id]=a,this.body.nodeIndices.push(a.id),this.body.nodes[d.id]=d,this.body.nodeIndices.push(d.id),this._temporaryBindUI("onTouch",FF(i=this._controlNodeTouch).call(i,this)),this._temporaryBindUI("onTap",(function(){})),this._temporaryBindUI("onHold",(function(){})),this._temporaryBindUI("onDragStart",FF(n=this._controlNodeDragStart).call(n,this)),this._temporaryBindUI("onDrag",FF(o=this._controlNodeDrag).call(o,this)),this._temporaryBindUI("onDragEnd",FF(r=this._controlNodeDragEnd).call(r,this)),this._temporaryBindUI("onMouseMove",(function(){})),this._temporaryBindEvent("beforeDrawing",(function(t){var e=s.edgeType.findBorderPositions(t);!1===a.selected&&(a.x=e.from.x,a.y=e.from.y),!1===d.selected&&(d.x=e.to.x,d.y=e.to.y)})),this.body.emitter.emit("_redraw")}else this.showManipulatorToolbar()}else{var l=this.body.edges[this.edgeBeingEditedId];this._performEditEdge(l.from.id,l.to.id)}}},{key:"deleteSelected",value:function(){var t=this;!0!==this.editMode&&this.enableEditMode(),this._clean(),this.inMode="delete";var e=this.selectionHandler.getSelectedNodeIds(),i=this.selectionHandler.getSelectedEdgeIds(),n=void 0;if(e.length>0){for(var o=0;o0&&"function"==typeof this.options.deleteEdge&&(n=this.options.deleteEdge);if("function"==typeof n){var r={nodes:e,edges:i};if(2!==n.length)throw new Error("The function for delete does not support two arguments (data, callback)");n(r,(function(e){null!=e&&"delete"===t.inMode?(t.body.data.edges.getDataSet().remove(e.edges),t.body.data.nodes.getDataSet().remove(e.nodes),t.body.emitter.emit("startSimulation"),t.showManipulatorToolbar()):(t.body.emitter.emit("startSimulation"),t.showManipulatorToolbar())}))}else this.body.data.edges.getDataSet().remove(i),this.body.data.nodes.getDataSet().remove(e),this.body.emitter.emit("startSimulation"),this.showManipulatorToolbar()}},{key:"_setup",value:function(){!0===this.options.enabled?(this.guiEnabled=!0,this._createWrappers(),!1===this.editMode?this._createEditButton():this.showManipulatorToolbar()):(this._removeManipulationDOM(),this.guiEnabled=!1)}},{key:"_createWrappers",value:function(){var t,e;(void 0===this.manipulationDiv&&(this.manipulationDiv=document.createElement("div"),this.manipulationDiv.className="vis-manipulation",!0===this.editMode?this.manipulationDiv.style.display="block":this.manipulationDiv.style.display="none",this.canvas.frame.appendChild(this.manipulationDiv)),void 0===this.editModeDiv&&(this.editModeDiv=document.createElement("div"),this.editModeDiv.className="vis-edit-mode",!0===this.editMode?this.editModeDiv.style.display="none":this.editModeDiv.style.display="block",this.canvas.frame.appendChild(this.editModeDiv)),void 0===this.closeDiv)&&(this.closeDiv=document.createElement("button"),this.closeDiv.className="vis-close",this.closeDiv.setAttribute("aria-label",null!==(t=null===(e=this.options.locales[this.options.locale])||void 0===e?void 0:e.close)&&void 0!==t?t:this.options.locales.en.close),this.closeDiv.style.display=this.manipulationDiv.style.display,this.canvas.frame.appendChild(this.closeDiv))}},{key:"_getNewTargetNode",value:function(t,e){var i=TK({},this.options.controlNodeStyle);i.id="targetNode"+o5(),i.hidden=!1,i.physics=!1,i.x=t,i.y=e;var n=this.body.functions.createNode(i);return n.shape.boundingBox={left:t,right:t,top:e,bottom:e},n}},{key:"_createEditButton",value:function(){var t;this._clean(),this.manipulationDOM={},_K(this.editModeDiv);var e=this.options.locales[this.options.locale],i=this._createButton("editMode","vis-edit vis-edit-mode",e.edit||this.options.locales.en.edit);this.editModeDiv.appendChild(i),this._bindElementEvents(i,FF(t=this.toggleEditMode).call(t,this))}},{key:"_clean",value:function(){this.inMode=!1,!0===this.guiEnabled&&(_K(this.editModeDiv),_K(this.manipulationDiv),this._cleanupDOMEventListeners()),this._cleanupTemporaryNodesAndEdges(),this._unbindTemporaryUIs(),this._unbindTemporaryEvents(),this.body.emitter.emit("restorePhysics")}},{key:"_cleanupDOMEventListeners",value:function(){var t,e,i=X4(bq(t=this._domEventListenerCleanupQueue).call(t,0));try{for(i.s();!(e=i.n()).done;){(0,e.value)()}}catch(t){i.e(t)}finally{i.f()}}},{key:"_removeManipulationDOM",value:function(){this._clean(),_K(this.manipulationDiv),_K(this.editModeDiv),_K(this.closeDiv),this.manipulationDiv&&this.canvas.frame.removeChild(this.manipulationDiv),this.editModeDiv&&this.canvas.frame.removeChild(this.editModeDiv),this.closeDiv&&this.canvas.frame.removeChild(this.closeDiv),this.manipulationDiv=void 0,this.editModeDiv=void 0,this.closeDiv=void 0}},{key:"_createSeperator",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;this.manipulationDOM["seperatorLineDiv"+t]=document.createElement("div"),this.manipulationDOM["seperatorLineDiv"+t].className="vis-separator-line",this.manipulationDiv.appendChild(this.manipulationDOM["seperatorLineDiv"+t])}},{key:"_createAddNodeButton",value:function(t){var e,i=this._createButton("addNode","vis-add",t.addNode||this.options.locales.en.addNode);this.manipulationDiv.appendChild(i),this._bindElementEvents(i,FF(e=this.addNodeMode).call(e,this))}},{key:"_createAddEdgeButton",value:function(t){var e,i=this._createButton("addEdge","vis-connect",t.addEdge||this.options.locales.en.addEdge);this.manipulationDiv.appendChild(i),this._bindElementEvents(i,FF(e=this.addEdgeMode).call(e,this))}},{key:"_createEditNodeButton",value:function(t){var e,i=this._createButton("editNode","vis-edit",t.editNode||this.options.locales.en.editNode);this.manipulationDiv.appendChild(i),this._bindElementEvents(i,FF(e=this.editNode).call(e,this))}},{key:"_createEditEdgeButton",value:function(t){var e,i=this._createButton("editEdge","vis-edit",t.editEdge||this.options.locales.en.editEdge);this.manipulationDiv.appendChild(i),this._bindElementEvents(i,FF(e=this.editEdgeMode).call(e,this))}},{key:"_createDeleteButton",value:function(t){var e,i;i=this.options.rtl?"vis-delete-rtl":"vis-delete";var n=this._createButton("delete",i,t.del||this.options.locales.en.del);this.manipulationDiv.appendChild(n),this._bindElementEvents(n,FF(e=this.deleteSelected).call(e,this))}},{key:"_createBackButton",value:function(t){var e,i=this._createButton("back","vis-back",t.back||this.options.locales.en.back);this.manipulationDiv.appendChild(i),this._bindElementEvents(i,FF(e=this.showManipulatorToolbar).call(e,this))}},{key:"_createButton",value:function(t,e,i){var n=arguments.length>3&&void 0!==arguments[3]?arguments[3]:"vis-label";return this.manipulationDOM[t+"Div"]=document.createElement("button"),this.manipulationDOM[t+"Div"].className="vis-button "+e,this.manipulationDOM[t+"Label"]=document.createElement("div"),this.manipulationDOM[t+"Label"].className=n,this.manipulationDOM[t+"Label"].innerText=i,this.manipulationDOM[t+"Div"].appendChild(this.manipulationDOM[t+"Label"]),this.manipulationDOM[t+"Div"]}},{key:"_createDescription",value:function(t){this.manipulationDOM.descriptionLabel=document.createElement("div"),this.manipulationDOM.descriptionLabel.className="vis-none",this.manipulationDOM.descriptionLabel.innerText=t,this.manipulationDiv.appendChild(this.manipulationDOM.descriptionLabel)}},{key:"_temporaryBindEvent",value:function(t,e){this.temporaryEventFunctions.push({event:t,boundFunction:e}),this.body.emitter.on(t,e)}},{key:"_temporaryBindUI",value:function(t,e){if(void 0===this.body.eventListeners[t])throw new Error("This UI function does not exist. Typo? You tried: "+t+" possible are: "+jX(kV(this.body.eventListeners)));this.temporaryUIFunctions[t]=this.body.eventListeners[t],this.body.eventListeners[t]=e}},{key:"_unbindTemporaryUIs",value:function(){for(var t in this.temporaryUIFunctions)Object.prototype.hasOwnProperty.call(this.temporaryUIFunctions,t)&&(this.body.eventListeners[t]=this.temporaryUIFunctions[t],delete this.temporaryUIFunctions[t]);this.temporaryUIFunctions={}}},{key:"_unbindTemporaryEvents",value:function(){for(var t=0;t=0;s--)if(o[s]!==this.selectedControlNode.id){r=this.body.nodes[o[s]];break}if(void 0!==r&&void 0!==this.selectedControlNode)if(!0===r.isCluster)alert(this.options.locales[this.options.locale].createEdgeError||this.options.locales.en.createEdgeError);else{var a=this.body.nodes[this.temporaryIds.nodes[0]];this.selectedControlNode.id===a.id?this._performEditEdge(r.id,n.to.id):this._performEditEdge(n.from.id,r.id)}else n.updateEdgeType(),this.body.emitter.emit("restorePhysics");this.body.emitter.emit("_redraw")}}},{key:"_handleConnect",value:function(t){if((new Date).valueOf()-this.touchTime>100){this.lastTouch=this.body.functions.getPointer(t.center),this.lastTouch.translation=pF({},this.body.view.translation),this.interactionHandler.drag.pointer=this.lastTouch,this.interactionHandler.drag.translation=this.lastTouch.translation;var e=this.lastTouch,i=this.selectionHandler.getNodeAt(e);if(void 0!==i)if(!0===i.isCluster)alert(this.options.locales[this.options.locale].createEdgeError||this.options.locales.en.createEdgeError);else{var n=this._getNewTargetNode(i.x,i.y);this.body.nodes[n.id]=n,this.body.nodeIndices.push(n.id);var o=this.body.functions.createEdge({id:"connectionEdge"+o5(),from:i.id,to:n.id,physics:!1,smooth:{enabled:!0,type:"continuous",roundness:.5}});this.body.edges[o.id]=o,this.body.edgeIndices.push(o.id),this.temporaryIds.nodes.push(n.id),this.temporaryIds.edges.push(o.id)}this.touchTime=(new Date).valueOf()}}},{key:"_dragControlNode",value:function(t){var e=this.body.functions.getPointer(t.center),i=this.selectionHandler._pointerToPositionObject(e),n=void 0;void 0!==this.temporaryIds.edges[0]&&(n=this.body.edges[this.temporaryIds.edges[0]].fromId);for(var o=this.selectionHandler._getAllNodesOverlappingWith(i),r=void 0,s=o.length-1;s>=0;s--){var a;if(-1===PX(a=this.temporaryIds.nodes).call(a,o[s])){r=this.body.nodes[o[s]];break}}if(t.controlEdge={from:n,to:r?r.id:void 0},this.selectionHandler.generateClickEvent("controlNodeDragging",t,e),void 0!==this.temporaryIds.nodes[0]){var d=this.body.nodes[this.temporaryIds.nodes[0]];d.x=this.canvas._XconvertDOMtoCanvas(e.x),d.y=this.canvas._YconvertDOMtoCanvas(e.y),this.body.emitter.emit("_redraw")}else this.interactionHandler.onDrag(t)}},{key:"_finishConnect",value:function(t){var e=this.body.functions.getPointer(t.center),i=this.selectionHandler._pointerToPositionObject(e),n=void 0;void 0!==this.temporaryIds.edges[0]&&(n=this.body.edges[this.temporaryIds.edges[0]].fromId);for(var o=this.selectionHandler._getAllNodesOverlappingWith(i),r=void 0,s=o.length-1;s>=0;s--){var a;if(-1===PX(a=this.temporaryIds.nodes).call(a,o[s])){r=this.body.nodes[o[s]];break}}this._cleanupTemporaryNodesAndEdges(),void 0!==r&&(!0===r.isCluster?alert(this.options.locales[this.options.locale].createEdgeError||this.options.locales.en.createEdgeError):void 0!==this.body.nodes[n]&&void 0!==this.body.nodes[r.id]&&this._performAddEdge(n,r.id)),t.controlEdge={from:n,to:r?r.id:void 0},this.selectionHandler.generateClickEvent("controlNodeDragEnd",t,e),this.body.emitter.emit("_redraw")}},{key:"_dragStartEdge",value:function(t){var e=this.lastTouch;this.selectionHandler.generateClickEvent("dragStart",t,e,void 0,!0)}},{key:"_performAddNode",value:function(t){var e=this,i={id:o5(),x:t.pointer.canvas.x,y:t.pointer.canvas.y,label:"new"};if("function"==typeof this.options.addNode){if(2!==this.options.addNode.length)throw this.showManipulatorToolbar(),new Error("The function for add does not support two arguments (data,callback)");this.options.addNode(i,(function(t){null!=t&&"addNode"===e.inMode&&e.body.data.nodes.getDataSet().add(t),e.showManipulatorToolbar()}))}else this.body.data.nodes.getDataSet().add(i),this.showManipulatorToolbar()}},{key:"_performAddEdge",value:function(t,e){var i=this,n={from:t,to:e};if("function"==typeof this.options.addEdge){if(2!==this.options.addEdge.length)throw new Error("The function for connect does not support two arguments (data,callback)");this.options.addEdge(n,(function(t){null!=t&&"addEdge"===i.inMode&&(i.body.data.edges.getDataSet().add(t),i.selectionHandler.unselectAll(),i.showManipulatorToolbar())}))}else this.body.data.edges.getDataSet().add(n),this.selectionHandler.unselectAll(),this.showManipulatorToolbar()}},{key:"_performEditEdge",value:function(t,e){var i=this,n={id:this.edgeBeingEditedId,from:t,to:e,label:this.body.data.edges.get(this.edgeBeingEditedId).label},o=this.options.editEdge;if("object"===sW(o)&&(o=o.editWithoutDrag),"function"==typeof o){if(2!==o.length)throw new Error("The function for edit does not support two arguments (data, callback)");o(n,(function(t){null==t||"editEdge"!==i.inMode?(i.body.edges[n.id].updateEdgeType(),i.body.emitter.emit("_redraw"),i.showManipulatorToolbar()):(i.body.data.edges.getDataSet().update(t),i.selectionHandler.unselectAll(),i.showManipulatorToolbar())}))}else this.body.data.edges.getDataSet().update(n),this.selectionHandler.unselectAll(),this.showManipulatorToolbar()}}]),t}(),K4="string",Z4="boolean",Q4="number",J4="array",t6="object",e6=["arrow","bar","box","circle","crow","curve","diamond","image","inv_curve","inv_triangle","triangle","vee"],i6={borderWidth:{number:Q4},borderWidthSelected:{number:Q4,undefined:"undefined"},brokenImage:{string:K4,undefined:"undefined"},chosen:{label:{boolean:Z4,function:"function"},node:{boolean:Z4,function:"function"},__type__:{object:t6,boolean:Z4}},color:{border:{string:K4},background:{string:K4},highlight:{border:{string:K4},background:{string:K4},__type__:{object:t6,string:K4}},hover:{border:{string:K4},background:{string:K4},__type__:{object:t6,string:K4}},__type__:{object:t6,string:K4}},opacity:{number:Q4,undefined:"undefined"},fixed:{x:{boolean:Z4},y:{boolean:Z4},__type__:{object:t6,boolean:Z4}},font:{align:{string:K4},color:{string:K4},size:{number:Q4},face:{string:K4},background:{string:K4},strokeWidth:{number:Q4},strokeColor:{string:K4},vadjust:{number:Q4},multi:{boolean:Z4,string:K4},bold:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},boldital:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},ital:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},mono:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},__type__:{object:t6,string:K4}},group:{string:K4,number:Q4,undefined:"undefined"},heightConstraint:{minimum:{number:Q4},valign:{string:K4},__type__:{object:t6,boolean:Z4,number:Q4}},hidden:{boolean:Z4},icon:{face:{string:K4},code:{string:K4},size:{number:Q4},color:{string:K4},weight:{string:K4,number:Q4},__type__:{object:t6}},id:{string:K4,number:Q4},image:{selected:{string:K4,undefined:"undefined"},unselected:{string:K4,undefined:"undefined"},__type__:{object:t6,string:K4}},imagePadding:{top:{number:Q4},right:{number:Q4},bottom:{number:Q4},left:{number:Q4},__type__:{object:t6,number:Q4}},label:{string:K4,undefined:"undefined"},labelHighlightBold:{boolean:Z4},level:{number:Q4,undefined:"undefined"},margin:{top:{number:Q4},right:{number:Q4},bottom:{number:Q4},left:{number:Q4},__type__:{object:t6,number:Q4}},mass:{number:Q4},physics:{boolean:Z4},scaling:{min:{number:Q4},max:{number:Q4},label:{enabled:{boolean:Z4},min:{number:Q4},max:{number:Q4},maxVisible:{number:Q4},drawThreshold:{number:Q4},__type__:{object:t6,boolean:Z4}},customScalingFunction:{function:"function"},__type__:{object:t6}},shadow:{enabled:{boolean:Z4},color:{string:K4},size:{number:Q4},x:{number:Q4},y:{number:Q4},__type__:{object:t6,boolean:Z4}},shape:{string:["custom","ellipse","circle","database","box","text","image","circularImage","diamond","dot","star","triangle","triangleDown","square","icon","hexagon"]},ctxRenderer:{function:"function"},shapeProperties:{borderDashes:{boolean:Z4,array:J4},borderRadius:{number:Q4},interpolation:{boolean:Z4},useImageSize:{boolean:Z4},useBorderWithImage:{boolean:Z4},coordinateOrigin:{string:["center","top-left"]},__type__:{object:t6}},size:{number:Q4},title:{string:K4,dom:"dom",undefined:"undefined"},value:{number:Q4,undefined:"undefined"},widthConstraint:{minimum:{number:Q4},maximum:{number:Q4},__type__:{object:t6,boolean:Z4,number:Q4}},x:{number:Q4},y:{number:Q4},__type__:{object:t6}},n6={configure:{enabled:{boolean:Z4},filter:{boolean:Z4,string:K4,array:J4,function:"function"},container:{dom:"dom"},showButton:{boolean:Z4},__type__:{object:t6,boolean:Z4,string:K4,array:J4,function:"function"}},edges:{arrows:{to:{enabled:{boolean:Z4},scaleFactor:{number:Q4},type:{string:e6},imageHeight:{number:Q4},imageWidth:{number:Q4},src:{string:K4},__type__:{object:t6,boolean:Z4}},middle:{enabled:{boolean:Z4},scaleFactor:{number:Q4},type:{string:e6},imageWidth:{number:Q4},imageHeight:{number:Q4},src:{string:K4},__type__:{object:t6,boolean:Z4}},from:{enabled:{boolean:Z4},scaleFactor:{number:Q4},type:{string:e6},imageWidth:{number:Q4},imageHeight:{number:Q4},src:{string:K4},__type__:{object:t6,boolean:Z4}},__type__:{string:["from","to","middle"],object:t6}},endPointOffset:{from:{number:Q4},to:{number:Q4},__type__:{object:t6,number:Q4}},arrowStrikethrough:{boolean:Z4},background:{enabled:{boolean:Z4},color:{string:K4},size:{number:Q4},dashes:{boolean:Z4,array:J4},__type__:{object:t6,boolean:Z4}},chosen:{label:{boolean:Z4,function:"function"},edge:{boolean:Z4,function:"function"},__type__:{object:t6,boolean:Z4}},color:{color:{string:K4},highlight:{string:K4},hover:{string:K4},inherit:{string:["from","to","both"],boolean:Z4},opacity:{number:Q4},__type__:{object:t6,string:K4}},dashes:{boolean:Z4,array:J4},font:{color:{string:K4},size:{number:Q4},face:{string:K4},background:{string:K4},strokeWidth:{number:Q4},strokeColor:{string:K4},align:{string:["horizontal","top","middle","bottom"]},vadjust:{number:Q4},multi:{boolean:Z4,string:K4},bold:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},boldital:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},ital:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},mono:{color:{string:K4},size:{number:Q4},face:{string:K4},mod:{string:K4},vadjust:{number:Q4},__type__:{object:t6,string:K4}},__type__:{object:t6,string:K4}},hidden:{boolean:Z4},hoverWidth:{function:"function",number:Q4},label:{string:K4,undefined:"undefined"},labelHighlightBold:{boolean:Z4},length:{number:Q4,undefined:"undefined"},physics:{boolean:Z4},scaling:{min:{number:Q4},max:{number:Q4},label:{enabled:{boolean:Z4},min:{number:Q4},max:{number:Q4},maxVisible:{number:Q4},drawThreshold:{number:Q4},__type__:{object:t6,boolean:Z4}},customScalingFunction:{function:"function"},__type__:{object:t6}},selectionWidth:{function:"function",number:Q4},selfReferenceSize:{number:Q4},selfReference:{size:{number:Q4},angle:{number:Q4},renderBehindTheNode:{boolean:Z4},__type__:{object:t6}},shadow:{enabled:{boolean:Z4},color:{string:K4},size:{number:Q4},x:{number:Q4},y:{number:Q4},__type__:{object:t6,boolean:Z4}},smooth:{enabled:{boolean:Z4},type:{string:["dynamic","continuous","discrete","diagonalCross","straightCross","horizontal","vertical","curvedCW","curvedCCW","cubicBezier"]},roundness:{number:Q4},forceDirection:{string:["horizontal","vertical","none"],boolean:Z4},__type__:{object:t6,boolean:Z4}},title:{string:K4,undefined:"undefined"},width:{number:Q4},widthConstraint:{maximum:{number:Q4},__type__:{object:t6,boolean:Z4,number:Q4}},value:{number:Q4,undefined:"undefined"},__type__:{object:t6}},groups:{useDefaultGroups:{boolean:Z4},__any__:i6,__type__:{object:t6}},interaction:{dragNodes:{boolean:Z4},dragView:{boolean:Z4},hideEdgesOnDrag:{boolean:Z4},hideEdgesOnZoom:{boolean:Z4},hideNodesOnDrag:{boolean:Z4},hover:{boolean:Z4},keyboard:{enabled:{boolean:Z4},speed:{x:{number:Q4},y:{number:Q4},zoom:{number:Q4},__type__:{object:t6}},bindToWindow:{boolean:Z4},autoFocus:{boolean:Z4},__type__:{object:t6,boolean:Z4}},multiselect:{boolean:Z4},navigationButtons:{boolean:Z4},selectable:{boolean:Z4},selectConnectedEdges:{boolean:Z4},hoverConnectedEdges:{boolean:Z4},tooltipDelay:{number:Q4},zoomView:{boolean:Z4},zoomSpeed:{number:Q4},__type__:{object:t6}},layout:{randomSeed:{undefined:"undefined",number:Q4,string:K4},improvedLayout:{boolean:Z4},clusterThreshold:{number:Q4},hierarchical:{enabled:{boolean:Z4},levelSeparation:{number:Q4},nodeSpacing:{number:Q4},treeSpacing:{number:Q4},blockShifting:{boolean:Z4},edgeMinimization:{boolean:Z4},parentCentralization:{boolean:Z4},direction:{string:["UD","DU","LR","RL"]},sortMethod:{string:["hubsize","directed"]},shakeTowards:{string:["leaves","roots"]},__type__:{object:t6,boolean:Z4}},__type__:{object:t6}},manipulation:{enabled:{boolean:Z4},initiallyActive:{boolean:Z4},addNode:{boolean:Z4,function:"function"},addEdge:{boolean:Z4,function:"function"},editNode:{function:"function"},editEdge:{editWithoutDrag:{function:"function"},__type__:{object:t6,boolean:Z4,function:"function"}},deleteNode:{boolean:Z4,function:"function"},deleteEdge:{boolean:Z4,function:"function"},controlNodeStyle:i6,__type__:{object:t6,boolean:Z4}},nodes:i6,physics:{enabled:{boolean:Z4},barnesHut:{theta:{number:Q4},gravitationalConstant:{number:Q4},centralGravity:{number:Q4},springLength:{number:Q4},springConstant:{number:Q4},damping:{number:Q4},avoidOverlap:{number:Q4},__type__:{object:t6}},forceAtlas2Based:{theta:{number:Q4},gravitationalConstant:{number:Q4},centralGravity:{number:Q4},springLength:{number:Q4},springConstant:{number:Q4},damping:{number:Q4},avoidOverlap:{number:Q4},__type__:{object:t6}},repulsion:{centralGravity:{number:Q4},springLength:{number:Q4},springConstant:{number:Q4},nodeDistance:{number:Q4},damping:{number:Q4},__type__:{object:t6}},hierarchicalRepulsion:{centralGravity:{number:Q4},springLength:{number:Q4},springConstant:{number:Q4},nodeDistance:{number:Q4},damping:{number:Q4},avoidOverlap:{number:Q4},__type__:{object:t6}},maxVelocity:{number:Q4},minVelocity:{number:Q4},solver:{string:["barnesHut","repulsion","hierarchicalRepulsion","forceAtlas2Based"]},stabilization:{enabled:{boolean:Z4},iterations:{number:Q4},updateInterval:{number:Q4},onlyDynamicEdges:{boolean:Z4},fit:{boolean:Z4},__type__:{object:t6,boolean:Z4}},timestep:{number:Q4},adaptiveTimestep:{boolean:Z4},wind:{x:{number:Q4},y:{number:Q4},__type__:{object:t6}},__type__:{object:t6,boolean:Z4}},autoResize:{boolean:Z4},clickToUse:{boolean:Z4},locale:{string:K4},locales:{__any__:{any:"any"},__type__:{object:t6}},height:{string:K4},width:{string:K4},__type__:{object:t6}},o6={nodes:{borderWidth:[1,0,10,1],borderWidthSelected:[2,0,10,1],color:{border:["color","#2B7CE9"],background:["color","#97C2FC"],highlight:{border:["color","#2B7CE9"],background:["color","#D2E5FF"]},hover:{border:["color","#2B7CE9"],background:["color","#D2E5FF"]}},opacity:[0,0,1,.1],fixed:{x:!1,y:!1},font:{color:["color","#343434"],size:[14,0,100,1],face:["arial","verdana","tahoma"],background:["color","none"],strokeWidth:[0,0,50,1],strokeColor:["color","#ffffff"]},hidden:!1,labelHighlightBold:!0,physics:!0,scaling:{min:[10,0,200,1],max:[30,0,200,1],label:{enabled:!1,min:[14,0,200,1],max:[30,0,200,1],maxVisible:[30,0,200,1],drawThreshold:[5,0,20,1]}},shadow:{enabled:!1,color:"rgba(0,0,0,0.5)",size:[10,0,20,1],x:[5,-30,30,1],y:[5,-30,30,1]},shape:["ellipse","box","circle","database","diamond","dot","square","star","text","triangle","triangleDown","hexagon"],shapeProperties:{borderDashes:!1,borderRadius:[6,0,20,1],interpolation:!0,useImageSize:!1},size:[25,0,200,1]},edges:{arrows:{to:{enabled:!1,scaleFactor:[1,0,3,.05],type:"arrow"},middle:{enabled:!1,scaleFactor:[1,0,3,.05],type:"arrow"},from:{enabled:!1,scaleFactor:[1,0,3,.05],type:"arrow"}},endPointOffset:{from:[0,-10,10,1],to:[0,-10,10,1]},arrowStrikethrough:!0,color:{color:["color","#848484"],highlight:["color","#848484"],hover:["color","#848484"],inherit:["from","to","both",!0,!1],opacity:[1,0,1,.05]},dashes:!1,font:{color:["color","#343434"],size:[14,0,100,1],face:["arial","verdana","tahoma"],background:["color","none"],strokeWidth:[2,0,50,1],strokeColor:["color","#ffffff"],align:["horizontal","top","middle","bottom"]},hidden:!1,hoverWidth:[1.5,0,5,.1],labelHighlightBold:!0,physics:!0,scaling:{min:[1,0,100,1],max:[15,0,100,1],label:{enabled:!0,min:[14,0,200,1],max:[30,0,200,1],maxVisible:[30,0,200,1],drawThreshold:[5,0,20,1]}},selectionWidth:[1.5,0,5,.1],selfReferenceSize:[20,0,200,1],selfReference:{size:[20,0,200,1],angle:[Math.PI/2,-6*Math.PI,6*Math.PI,Math.PI/8],renderBehindTheNode:!0},shadow:{enabled:!1,color:"rgba(0,0,0,0.5)",size:[10,0,20,1],x:[5,-30,30,1],y:[5,-30,30,1]},smooth:{enabled:!0,type:["dynamic","continuous","discrete","diagonalCross","straightCross","horizontal","vertical","curvedCW","curvedCCW","cubicBezier"],forceDirection:["horizontal","vertical","none"],roundness:[.5,0,1,.05]},width:[1,0,30,1]},layout:{hierarchical:{enabled:!1,levelSeparation:[150,20,500,5],nodeSpacing:[100,20,500,5],treeSpacing:[200,20,500,5],blockShifting:!0,edgeMinimization:!0,parentCentralization:!0,direction:["UD","DU","LR","RL"],sortMethod:["hubsize","directed"],shakeTowards:["leaves","roots"]}},interaction:{dragNodes:!0,dragView:!0,hideEdgesOnDrag:!1,hideEdgesOnZoom:!1,hideNodesOnDrag:!1,hover:!1,keyboard:{enabled:!1,speed:{x:[10,0,40,1],y:[10,0,40,1],zoom:[.02,0,.1,.005]},bindToWindow:!0,autoFocus:!0},multiselect:!1,navigationButtons:!1,selectable:!0,selectConnectedEdges:!0,hoverConnectedEdges:!0,tooltipDelay:[300,0,1e3,25],zoomView:!0,zoomSpeed:[1,.1,2,.1]},manipulation:{enabled:!1,initiallyActive:!1},physics:{enabled:!0,barnesHut:{theta:[.5,.1,1,.05],gravitationalConstant:[-2e3,-3e4,0,50],centralGravity:[.3,0,10,.05],springLength:[95,0,500,5],springConstant:[.04,0,1.2,.005],damping:[.09,0,1,.01],avoidOverlap:[0,0,1,.01]},forceAtlas2Based:{theta:[.5,.1,1,.05],gravitationalConstant:[-50,-500,0,1],centralGravity:[.01,0,1,.005],springLength:[95,0,500,5],springConstant:[.08,0,1.2,.005],damping:[.4,0,1,.01],avoidOverlap:[0,0,1,.01]},repulsion:{centralGravity:[.2,0,10,.05],springLength:[200,0,500,5],springConstant:[.05,0,1.2,.005],nodeDistance:[100,0,500,5],damping:[.09,0,1,.01]},hierarchicalRepulsion:{centralGravity:[.2,0,10,.05],springLength:[100,0,500,5],springConstant:[.01,0,1.2,.005],nodeDistance:[120,0,500,5],damping:[.09,0,1,.01],avoidOverlap:[0,0,1,.01]},maxVelocity:[50,0,150,1],minVelocity:[.1,.01,.5,.01],solver:["barnesHut","forceAtlas2Based","repulsion","hierarchicalRepulsion"],timestep:[.5,.01,1,.01],wind:{x:[0,-10,10,.1],y:[0,-10,10,.1]}}},r6=function(t,e,i){var n;return!(!Hq(t).call(t,"physics")||!Hq(n=o6.physics.solver).call(n,e)||i.physics.solver===e||"wind"===e)},s6=function(){function t(){rj(this,t)}return cW(t,[{key:"getDistances",value:function(t,e,i){for(var n={},o=t.edges,r=0;r2&&void 0!==arguments[2]&&arguments[2],n=this.distanceSolver.getDistances(this.body,t,e);this._createL_matrix(n),this._createK_matrix(n),this._createE_matrix();for(var o=0,r=Math.max(1e3,Math.min(10*this.body.nodeIndices.length,6e3)),s=1e9,a=0,d=0,l=0,c=0,h=0;s>.01&&o1&&h<5;){h+=1,this._moveNode(a,d,l);var p=QW(this._getEnergy(a),3);c=p[0],d=p[1],l=p[2]}}}},{key:"_getHighestEnergyNode",value:function(t){for(var e=this.body.nodeIndices,i=this.body.nodes,n=0,o=e[0],r=0,s=0,a=0;a{class e extends t{connectedCallback(){super.connectedCallback(),this.__checkSubscribed()}disconnectedCallback(){if(super.disconnectedCallback(),this.__unsubs){for(;this.__unsubs.length;){const t=this.__unsubs.pop();t instanceof Promise?t.then((t=>t())):null!=t&&t()}this.__unsubs=void 0}}updated(t){super.updated(t),t.has("hass")&&this.__checkSubscribed()}hassSubscribe(){return[]}__checkSubscribed(){void 0===this.__unsubs&&this.isConnected&&void 0!==this.hass&&(this.__unsubs=this.hassSubscribe())}}return o([At({attribute:!1})],e.prototype,"hass",void 0),e};return console.info(`%c WISER-ZIGBEE-NETWORK-CARD \n%c ${Zt("common.version")} ${$t} `,"color: orange; font-weight: bold; background: black","color: white; font-weight: bold; background: dimgray"),window.customCards=window.customCards||[],window.customCards.push({type:"wiser-zigbee-card",name:"Wiser Zigbee Card",description:"A card to display Wiser Zigbee network between devices"}),t.WiserZigbeeCard=class extends(l6(Ot)){constructor(){super(...arguments),this.options=Ut,this.current_theme="",this.need_render=!1,this.is_initialised=!1,this.is_preview=!0}static async getConfigElement(){return document.createElement("wiser-zigbee-card-editor")}static getStubConfig(){return{}}setConfig(t){if(!t)throw new Error(Zt("common.invalid_configuration"));this.config=Object.assign({name:"Wiser Zigbee Network"},t),this.loadCardHelpers(),this.initialise()}async loadCardHelpers(){this._helpers=await window.loadCardHelpers()}async isComponentLoaded(){for(;!this.hass||!this.hass.config.components.includes("wiser");)await new Promise((t=>setTimeout(t,100)));return!0}async initialise(){var t;await this.isComponentLoaded()&&(null===(t=this.config)||void 0===t?void 0:t.layout_data)&&(this.layoutData=this.config.layout_data);const e=this._dialog_close.bind(this);return window.addEventListener("dialog-closed",e),this.loadData(),!0}hassSubscribe(){return[this.hass.connection.subscribeMessage((t=>this.handleUpdate(t)),{type:"wiser_updated"})]}_dialog_close(t){this.is_preview&&"hui-dialog-edit-card"==t.detail.dialog&&(this.is_preview=!1,this.need_render=!0)}async handleUpdate(t){"wiser_updated"==t.event&&this.config.auto_update&&!this.is_preview&&this.is_initialised&&await this.loadData()}async loadData(){const t=await(e=this.hass,i=this.config.hub,e.callWS({type:"wiser/zigbee",hub:i}));var e,i;this.layoutData?this.zigbeeData=await this.addLayout(t):this.zigbeeData=t,this.need_render=!0}async addLayout(t){for(let e=0;e{let t=document.querySelector("home-assistant");return t=t&&t.shadowRoot,t=t&&t.querySelector("hui-dialog-edit-card"),t=t&&t.shadowRoot,t=t&&t.querySelector("ha-dialog"),!!t})(),!this.hass||!this.config)return!1;if(1==this.need_render)return!0;if(this.is_preview&&this.is_initialised)return!1;if((null===(e=this.hass)||void 0===e?void 0:e.selectedTheme)&&this.current_theme!=(null===(i=this.hass)||void 0===i?void 0:i.selectedTheme)){this.current_theme=this.hass.selectedTheme;const t=getComputedStyle(document.documentElement).getPropertyValue("--primary-text-color");return this.options.edges.font.color=t,null===(n=this.network)||void 0===n||n.setOptions(this.options),!0}return function(t,e,i){if(e.has("config")||i)return!0;if(t.config.entity){var n=e.get("hass");return!n||n.states[t.config.entity]!==t.hass.states[t.config.entity]}return!1}(this,t,!1)}async _initialise_network(){var t;await this.loadData(),this.container=null===(t=this.shadowRoot)||void 0===t?void 0:t.getElementById("zigbee-network"),this.network=new d6(this.container,this.zigbeeData,this.options)}firstUpdated(){this._initialise_network(),this.is_initialised=!0}render(){var t;return this.need_render=!1,this.zigbeeData&&this.network&&(null===(t=this.network)||void 0===t||t.setData(this.zigbeeData)),$` - -
-
${this.config.name}
-
-
-
-
- ${!this.config.auto_update||this.is_preview?this.render_card_actions():""} -
- `}render_card_actions(){return $` -
- ${this.is_preview?this.render_save_layout_button():""} - ${!this.config.auto_update||this.is_preview?this.render_refresh_button():""} -
- `}render_refresh_button(){return $` - - ${this.hass.localize("ui.common.refresh")} - - `}render_save_layout_button(){return $` - Save Layout - `}async refreshClick(){await this.loadData()}async saveLayoutClick(){var t;this.layoutData=null===(t=this.network)||void 0===t?void 0:t.getPositions(),this.config.layout_data=this.layoutData,Ht(this,"wiser-zigbee-save-layout",{layout_data:this.layoutData}),this.need_render=!0,await this.loadData()}},t.WiserZigbeeCard.style=ht` - .zigbee-network { - width: 600px; - height: 400px; - border: 1px solid lightgray; - } - `,o([Rt()],t.WiserZigbeeCard.prototype,"config",void 0),o([Rt()],t.WiserZigbeeCard.prototype,"zigbeeData",void 0),o([Rt()],t.WiserZigbeeCard.prototype,"_helpers",void 0),t.WiserZigbeeCard=o([St("wiser-zigbee-card")],t.WiserZigbeeCard),t}({}); diff --git a/custom_components/wiser/frontend/wiser_sidebar.py b/custom_components/wiser/frontend/wiser_sidebar.py new file mode 100644 index 00000000..35a803e9 --- /dev/null +++ b/custom_components/wiser/frontend/wiser_sidebar.py @@ -0,0 +1,281 @@ +"""Manage the single shared Wiser sidebar panel.""" + +from __future__ import annotations + +import math +from pathlib import Path + +from homeassistant.components import frontend, panel_custom + +from . import async_card_resource, card_version +from .registry import get_manifest +from ..const import ( + CONF_SHOW_WISER_SIDEBAR, + CONF_WISER_PANEL_CONFIG, + DATA, + DOMAIN, + URL_BASE, +) + +# Keep the UI route outside URL_BASE: a direct request to the static-file +# directory returns 403 instead of loading the Home Assistant frontend. +PANEL_PATH = "wiser-panel" +PANEL_STATE = "wiser_sidebar_panel" +PANEL_COMPONENT = "wiser-panel" +PANEL_FILENAME = "wiser-panel.js" +_PANEL_TABS_CONFIG = "_panel_tabs" + + +def _panel_enabled(options): + """Return the unified preference, enabled by default.""" + return options.get(CONF_SHOW_WISER_SIDEBAR, True) + + +def _enabled_entries(hass): + """Yield loaded hubs enabled for the shared Wiser panel.""" + loaded = hass.data.get(DOMAIN, {}) + for entry in hass.config_entries.async_entries(DOMAIN): + if ( + entry.entry_id in loaded + and not entry.disabled_by + and _panel_enabled(entry.options) + ): + yield loaded[entry.entry_id][DATA].wiserhub.system.name, entry + + +def _panel_title(component): + """Derive a readable tab title from the panel custom element.""" + name = component.removeprefix("wiser-").removesuffix("-panel") + return name.replace("-", " ").title() + + +def _legacy_panel_titles(module): + """Return former default titles derived from renamed card bundles.""" + titles = set() + for filename in module.get("legacy_filenames", []): + name = filename.removeprefix("wiser-").removesuffix("-card.js") + titles.add(name.replace("-", " ").title()) + return titles + + +def _stored_config(entry, panel_id): + """Return settings from the shared container for any panel.""" + generic = entry.options.get(CONF_WISER_PANEL_CONFIG, {}) + if isinstance(generic, dict) and isinstance(generic.get(panel_id), dict): + return dict(generic[panel_id]) + return {} + + +async def _discover_panels(hass, entries): + """Find installed panel components from the active frontend registry.""" + panels = [] + hubs = [hub for hub, _ in entries] + manifest = get_manifest(hass) + definitions = {module["id"]: module for module in manifest} + for module in manifest: + component = module.get("panel") + if not component: + continue + filename = module["filename"] + path, module_url, _ = await async_card_resource(hass, filename) + try: + source = await hass.async_add_executor_job(path.read_text, "utf-8") + except OSError: + continue + if component not in source: + continue + panel_id = module["id"] + default_title = _panel_title(component) + panels.append( + { + "id": panel_id, + "title": default_title, + "default_title": default_title, + "component": component, + "module_url": module_url, + "config": { + "panel_id": panel_id, + "hubs": hubs, + "hub_ids": { + hub: entry.entry_id for hub, entry in entries + }, + "card_configs": { + hub: _stored_config(entry, panel_id) + for hub, entry in entries + }, + "card_url": module_url, + }, + } + ) + stored_tabs = None + for _, entry in entries: + generic = entry.options.get(CONF_WISER_PANEL_CONFIG, {}) + if isinstance(generic, dict) and isinstance( + generic.get(_PANEL_TABS_CONFIG), list + ): + stored_tabs = generic[_PANEL_TABS_CONFIG] + break + + if not stored_tabs: + return panels + + preferences = { + item["id"]: item + for item in stored_tabs + if isinstance(item, dict) and isinstance(item.get("id"), str) + } + for panel in panels: + title = preferences.get(panel["id"], {}).get("title") + if isinstance(title, str) and title.strip(): + stored_title = title.strip() + if stored_title not in _legacy_panel_titles(definitions[panel["id"]]): + panel["title"] = stored_title + + order = {item["id"]: index for index, item in enumerate(stored_tabs)} + discovered_order = {panel["id"]: index for index, panel in enumerate(panels)} + panels.sort( + key=lambda panel: ( + order.get(panel["id"], len(order) + discovered_order[panel["id"]]) + ) + ) + return panels + + +async def async_update_wiser_panel(hass): + """Register, refresh, or remove the single Wiser sidebar panel.""" + entries = list(_enabled_entries(hass)) + if not entries: + if PANEL_STATE in hass.data: + frontend.async_remove_panel(hass, PANEL_PATH) + hass.data.pop(PANEL_STATE) + return + + panels = await _discover_panels(hass, entries) + panel_path = Path(__file__).parent / PANEL_FILENAME + panel_version = await hass.async_add_executor_job(card_version, panel_path) + panel_url = f"{URL_BASE}/{PANEL_FILENAME}?v={panel_version}" + # Include the shell URL so an integration reload refreshes a changed shell bundle. + state = {"panels": panels, "module_url": panel_url} + if state == hass.data.get(PANEL_STATE): + return + + config = { + **state, + "_panel_custom": { + "name": PANEL_COMPONENT, + "module_url": panel_url, + "embed_iframe": False, + "trust_external": False, + }, + } + if PANEL_STATE in hass.data: + frontend.async_register_built_in_panel( + hass, + component_name="custom", + frontend_url_path=PANEL_PATH, + sidebar_title="Wiser", + sidebar_icon="wiser:wiser", + config=config, + update=True, + ) + else: + await panel_custom.async_register_panel( + hass, + frontend_url_path=PANEL_PATH, + webcomponent_name=PANEL_COMPONENT, + sidebar_title="Wiser", + sidebar_icon="wiser:wiser", + module_url=panel_url, + config=state, + ) + hass.data[PANEL_STATE] = state + + +def _is_json_value(value, depth=0): + """Accept finite JSON data, including future card options.""" + if depth > 64: + return False + if type(value) in (str, bool, int, type(None)): + return True + if type(value) is float: + return math.isfinite(value) + if type(value) is list: + return all(_is_json_value(item, depth + 1) for item in value) + if type(value) is dict: + return all( + type(key) is str and _is_json_value(item, depth + 1) + for key, item in value.items() + ) + return False + + +def save_panel_config(hass, panel_id, configs): + """Save settings for any registry-defined panel using the shared option.""" + if panel_id not in {card["id"] for card in get_manifest(hass) if card.get("panel")}: + raise ValueError("Unknown Wiser panel") + if type(configs) is not dict: + raise ValueError("Invalid panel setting") + entries = dict(_enabled_entries(hass)) + updates = [] + for hub, config in configs.items(): + if hub not in entries: + raise ValueError("Hub is not available in the Wiser panel") + if type(config) is not dict or not _is_json_value(config): + raise ValueError("Invalid panel setting") + settings = { + key: value + for key, value in config.items() + if key not in {"type", "hub", "hubs", "_panel_hide_title"} + } + entry = entries[hub] + generic = entry.options.get(CONF_WISER_PANEL_CONFIG, {}) + if not isinstance(generic, dict): + generic = {} + updates.append((entry, {**generic, panel_id: settings})) + for entry, generic in updates: + hass.config_entries.async_update_entry( + entry, + options={**entry.options, CONF_WISER_PANEL_CONFIG: generic}, + ) + + +def save_wiser_panel_tabs(hass, tabs): + """Save the shared panel tab order and custom titles for every hub.""" + if type(tabs) is not list: + raise ValueError("Invalid Wiser panel tabs") + + known_ids = {module["id"] for module in get_manifest(hass) if module.get("panel")} + saved_tabs = [] + seen = set() + for tab in tabs: + if type(tab) is not dict: + raise ValueError("Invalid Wiser panel tab") + panel_id = tab.get("id") + title = tab.get("title") + if ( + type(panel_id) is not str + or panel_id not in known_ids + or panel_id in seen + or type(title) is not str + or not title.strip() + or len(title.strip()) > 64 + ): + raise ValueError("Invalid Wiser panel tab") + seen.add(panel_id) + saved_tabs.append({"id": panel_id, "title": title.strip()}) + + entries = list(_enabled_entries(hass)) + for _, entry in entries: + generic = entry.options.get(CONF_WISER_PANEL_CONFIG, {}) + if not isinstance(generic, dict): + generic = {} + hass.config_entries.async_update_entry( + entry, + options={ + **entry.options, + CONF_WISER_PANEL_CONFIG: { + **generic, + _PANEL_TABS_CONFIG: saved_tabs, + }, + }, + ) diff --git a/custom_components/wiser/helpers.py b/custom_components/wiser/helpers.py index 1275f3f2..8707e171 100644 --- a/custom_components/wiser/helpers.py +++ b/custom_components/wiser/helpers.py @@ -5,6 +5,7 @@ WiserHubAuthenticationError, WiserHubRESTError, ) +from homeassistant.const import MAJOR_VERSION, MINOR_VERSION from homeassistant.core import HomeAssistant from .const import DOMAIN, ENTITY_PREFIX, MANUFACTURER import logging @@ -28,6 +29,21 @@ ENTITY_UNIQUE_ID_NAMESPACE = UUID("8d42a4e1-b77b-4dda-a964-137b67c6257f") +def active_wiser_rooms(data): + """Return rooms that still have a device or controller assigned.""" + return [ + room + for room in data.wiserhub.rooms.all + if str(getattr(room, "_data", {}).get("Invalid", "")).casefold() + != "nothingassigned" + ] + + +def room_has_climate(room): + """Return whether the integration creates a climate entity for a room.""" + return bool(room.devices) + + def get_hub_mac_suffix(data): """Return the last three octets of the physical hub MAC address.""" mac_address = data.wiserhub.system.network.mac_address @@ -42,13 +58,20 @@ def get_hub_device_name(data): return f"{ENTITY_PREFIX} HeatHub" -def get_hub_entity_object_id(data, entity_name): +def get_hub_entity_object_id(data, entity_name, device_name=None): """Return the MAC-derived entity portion of a new hub object ID.""" entity_slug = re.sub(r"[^a-z0-9]+", "_", str(entity_name).lower()).strip("_") - # Home Assistant adds the area and device portions. Supplying them here too - # would produce IDs such as ``wiser_heathub_wiser_heathub_...`` when users - # recreate entity IDs from the device page. - return f"{get_hub_mac_suffix(data).lower()}_{entity_slug}" + # Home Assistant adds the area and effective device name around this value. + # The integration's multi-hub device name already contains the MAC suffix, + # so omit it here to avoid ``..._04f8a0_04f8a0_away_mode``. + device_name = device_name or get_hub_device_name(data) + compact_device_name = re.sub(r"[^0-9A-Fa-f]", "", str(device_name)) + mac_suffix = get_hub_mac_suffix(data) + if mac_suffix.casefold() in compact_device_name.casefold(): + return entity_slug + # A user may rename the device and remove the visible MAC. Add it to the + # entity portion so resetting the entity ID still retains the hub identity. + return f"{mac_suffix.lower()}_{entity_slug}" def hub_error_handler(func): @@ -68,6 +91,7 @@ async def wrapper(*args, **kwargs): def get_device_name(data, device_id, device_type="device"): + legacy_naming = getattr(data, "legacy_naming", False) if device_type == "device": device = data.wiserhub.devices.get_by_id(device_id) @@ -75,6 +99,8 @@ def get_device_name(data, device_id, device_type="device"): return get_hub_device_name(data) if device.product_type == "iTRV": + if not legacy_naming: + return f"{ENTITY_PREFIX} {device.product_type}" device_room = data.wiserhub.rooms.get_by_device_id(device_id) # If device not allocated to a room return type and id only if device_room: @@ -90,6 +116,8 @@ def get_device_name(data, device_id, device_type="device"): if device.product_type == "RoomStat": device_room = data.wiserhub.rooms.get_by_device_id(device_id) if device_room: + if legacy_naming: + return f"{ENTITY_PREFIX} Thermostat {device_room.name}" return f"{ENTITY_PREFIX} Thermostat" return f"{ENTITY_PREFIX} Thermostat {device.id}" @@ -97,6 +125,8 @@ def get_device_name(data, device_id, device_type="device"): return f"{ENTITY_PREFIX} {device.name}" if device.product_type in ["HeatingActuator", "CFMT"]: + if not legacy_naming: + return f"{ENTITY_PREFIX} {device.product_type}" device_room = data.wiserhub.rooms.get_by_device_id(device_id) # If device not allocated to a room return type and id only if device_room: @@ -113,12 +143,15 @@ def get_device_name(data, device_id, device_type="device"): if device.product_type == "SmartPlug": return f"{ENTITY_PREFIX} {device.name}" + if device.product_type == "EVSE": + return f"{ENTITY_PREFIX} {device.name}" + if device.product_type in ["PowerTagE", "LoadControl"]: return f"{ENTITY_PREFIX} {device.name}" if device.product_type in ["SmokeAlarmDevice", "ButtonPanel"]: device_room = data.wiserhub.rooms.get_by_id(device.room_id) - if device_room: + if legacy_naming and device_room: return f"{ENTITY_PREFIX} {device_room.name} {device.name}" return f"{ENTITY_PREFIX} {device.name} {device.id}" @@ -126,6 +159,10 @@ def get_device_name(data, device_id, device_type="device"): return f"{ENTITY_PREFIX} {device.product_type} {device.name}" if device.product_type == "TemperatureHumiditySensor": + if legacy_naming: + device_room = data.wiserhub.rooms.get_by_device_id(device_id) + suffix = device_room.name if device_room else device.name + return f"{ENTITY_PREFIX} Temperature/Humidity Sensor {suffix}" return f"{ENTITY_PREFIX} Temperature/Humidity Sensor" if device.product_type in [ @@ -139,32 +176,193 @@ def get_device_name(data, device_id, device_type="device"): ]: device_room = data.wiserhub.rooms.get_by_device_id(device_id) # If device not allocated to a room return type and id only - if device_room: + if legacy_naming and device_room: return f"{ENTITY_PREFIX} {device.product_type} {device_room.name} {device.name}" return f"{ENTITY_PREFIX} {device.product_type} {device.name}" return f"{ENTITY_PREFIX} {device.serial_number}" elif device_type == "room": + if legacy_naming: + room = data.wiserhub.rooms.get_by_id(device_id) + return f"{ENTITY_PREFIX} {room.name}" return f"{ENTITY_PREFIX} Room" else: return f"{ENTITY_PREFIX} {device_type}" +def get_light_entity_name(data, light, entity_name): + """Return a device-relative name for an entity belonging to a light. + + The hub repeats the physical device name in each light channel name. With + Home Assistant's native entity naming enabled, including that portion here + causes the device name to appear twice. Keep only a real channel suffix so + multi-gang devices remain distinguishable. + """ + light_name = str(getattr(light, "name", "")).strip() + if getattr(data, "legacy_naming", False): + return " ".join( + part for part in (ENTITY_PREFIX, light_name, entity_name) if part + ) + + devices = data.wiserhub.devices + light_node_id = getattr(light, "node_id", None) + lights = getattr(getattr(devices, "lights", None), "all", None) + related_lights = ( + [ + candidate + for candidate in lights + if getattr(candidate, "id", None) == light.id + or ( + light_node_id not in (None, 0) + and getattr(candidate, "node_id", None) == light_node_id + ) + ] + if lights is not None + else None + ) + has_multiple_channels = ( + len(related_lights) > 1 if related_lights is not None else None + ) + + # aioWiserHeatAPI composes light.name from the physical device Name and + # the per-channel Name. The released API does not expose the former as a + # public property, so read its source value directly when available. A + # future API can expose device_name without changing this integration. + source_data = getattr(light, "_data", {}) + device_name = str( + getattr(light, "device_name", "") + or ( + source_data.get("Name", "") + if isinstance(source_data, dict) + else "" + ) + ).strip() + physical_device = None + + # Combined dual-relay/dimmer units expose a ButtonPanel and multiple Light + # views with the same physical ID. The generic lookup may return one of + # the light channels, whose name cannot be used to remove the shared device + # prefix from another channel. Some two-gang channels also use synthetic + # IDs, so prefer the physical ButtonPanel view by its shared Zigbee node. + if not device_name: + physical_device = next( + ( + device + for device in getattr(devices, "all", ()) + if hasattr(device, "number_of_gangs") + and hasattr(device, "events") + and ( + getattr(device, "id", None) == light.id + or ( + light_node_id not in (None, 0) + and getattr(device, "node_id", None) == light_node_id + ) + ) + ), + None, + ) + if physical_device is None: + physical_device = devices.get_by_id(light.id) + device_name = str(getattr(physical_device, "name", "")).strip() + product_type = str( + getattr(physical_device or light, "product_type", "") + ).strip() + + # Some hub models include Wiser and/or the product type in the physical + # device name, while their channel names omit those prefixes. Compare all + # equivalent device-name forms so only the physical-device portion is + # removed from the channel name. + device_names = {device_name} + metadata_prefixes = tuple( + prefix for prefix in (ENTITY_PREFIX, product_type) if prefix + ) + pending_names = [device_name] + while pending_names: + candidate = pending_names.pop() + for prefix in metadata_prefixes: + marker = f"{prefix} " + if candidate.casefold().startswith(marker.casefold()): + stripped = candidate[len(marker) :].strip() + if stripped and stripped not in device_names: + device_names.add(stripped) + pending_names.append(stripped) + + channel_name = "" if has_multiple_channels is False else light_name + if channel_name: + for candidate in sorted(device_names, key=len, reverse=True): + if candidate and light_name.casefold() == candidate.casefold(): + channel_name = "" + break + if candidate and light_name.casefold().startswith( + f"{candidate} ".casefold() + ): + channel_name = light_name[len(candidate) :].strip() + break + + return " ".join(part for part in (channel_name, entity_name) if part) + + def get_legacy_device_name(data, device_id): """Return a physical device name used by historical entity unique IDs.""" device = data.wiserhub.devices.get_by_id(device_id) - if device.product_type != "TemperatureHumiditySensor": - return get_device_name(data, device_id) - device_room = data.wiserhub.rooms.get_by_device_id(device_id) - if device_room: + + if device.product_type == "iTRV": + if device_room: + if device_room.number_of_smartvalves > 1: + index = device_room.smartvalve_ids.index(device.id) + 1 + return ( + f"{ENTITY_PREFIX} {device.product_type} " + f"{device_room.name}-{index}" + ) + return f"{ENTITY_PREFIX} {device.product_type} {device_room.name}" + return f"{ENTITY_PREFIX} {device.product_type} {device.id}" + + if device.product_type == "RoomStat": + if device_room: + return f"{ENTITY_PREFIX} Thermostat {device_room.name}" + return f"{ENTITY_PREFIX} Thermostat {device.id}" + + if device.product_type in {"HeatingActuator", "CFMT"}: + if device_room: + if device_room.number_of_heating_actuators > 1: + index = device_room.heating_actuator_ids.index(device.id) + 1 + return ( + f"{ENTITY_PREFIX} {device.product_type} " + f"{device_room.name}-{index}" + ) + return f"{ENTITY_PREFIX} {device.product_type} {device_room.name}" + return f"{ENTITY_PREFIX} {device.product_type} {device.id}" + + if device.product_type in {"SmokeAlarmDevice", "ButtonPanel"}: + if device_room: + return f"{ENTITY_PREFIX} {device_room.name} {device.name}" + return f"{ENTITY_PREFIX} {device.name} {device.id}" + + if device.product_type == "TemperatureHumiditySensor" and device_room: return ( f"{ENTITY_PREFIX} {device.product_type} " f"{device_room.name} {device.name}" ) - return f"{ENTITY_PREFIX} {device.product_type} {device.name}" + if device.product_type in { + "Shutter", + "OnOffLight", + "DimmableLight", + "WindowDoorSensor", + "WaterLeakageSensor", + "MotionLightSensor", + "TemperatureHumiditySensor", + }: + if device_room: + return ( + f"{ENTITY_PREFIX} {device.product_type} " + f"{device_room.name} {device.name}" + ) + return f"{ENTITY_PREFIX} {device.product_type} {device.name}" + + return get_device_name(data, device_id) def get_identifier(data, device_id, device_type="device"): @@ -173,25 +371,43 @@ def get_identifier(data, device_id, device_type="device"): if device_id == 0 and device_type == "device": # Preserve the historical Controller identifier for registry migration. device_name = f"{ENTITY_PREFIX} HeatHub ({data.wiserhub.system.name})" - elif device_type == "device" and ( - device := data.wiserhub.devices.get_by_id(device_id) - ).product_type == "RoomStat": - # Preserve the existing name-derived identifier while modernising the - # RoomStat's display name. + elif device_type == "device": + # A physical device can move between rooms or be renamed without + # becoming a new Home Assistant device. + return f"{data.wiserhub.system.name} device {device_id}" + else: + device_name = get_device_name(data, device_id, device_type) + return f"{data.wiserhub.system.name} {device_name}" + + +def get_legacy_device_identifier(data, device_id): + """Return the former name-derived identifier for a physical device.""" + device = data.wiserhub.devices.get_by_id(device_id) + if device.product_type == "RoomStat": device_room = data.wiserhub.rooms.get_by_device_id(device_id) if device_room: device_name = f"{ENTITY_PREFIX} RoomStat {device_room.name}" else: device_name = f"{ENTITY_PREFIX} RoomStat {device.id}" - elif ( - device_type == "device" - and device.product_type == "TemperatureHumiditySensor" - ): - # Preserve the former name-derived identifier while modernising the - # physical sensor's display name. + elif device.product_type == "TemperatureHumiditySensor": device_name = get_legacy_device_name(data, device_id) + elif device.product_type == "iTRV": + device_room = data.wiserhub.rooms.get_by_device_id(device_id) + if device_room: + if device_room.number_of_smartvalves > 1: + index = device_room.smartvalve_ids.index(device.id) + 1 + device_name = ( + f"{ENTITY_PREFIX} {device.product_type} " + f"{device_room.name}-{index}" + ) + else: + device_name = ( + f"{ENTITY_PREFIX} {device.product_type} {device_room.name}" + ) + else: + device_name = f"{ENTITY_PREFIX} {device.product_type} {device.id}" else: - device_name = get_device_name(data, device_id, device_type) + device_name = get_legacy_device_name(data, device_id) return f"{data.wiserhub.system.name} {device_name}" @@ -221,11 +437,12 @@ def get_hub_device_info(data): def get_hub_via_device_info(data): - """Return the registered physical HeatHub as a parent device.""" - hub_device_id = getattr(data, "hub_device_id", None) - if hub_device_id is None: - return {} - return {"via_device_id": hub_device_id} + """Return the HeatHub parent supported by this Home Assistant version.""" + if (MAJOR_VERSION, MINOR_VERSION) >= (2026, 8): + hub_device_id = getattr(data, "hub_device_id", None) + if hub_device_id is not None: + return {"via_device_id": hub_device_id} + return {"via_device": (DOMAIN, data.wiserhub.system.name)} def get_legacy_unique_id(data, device_type, entity_type, device_id): @@ -245,6 +462,219 @@ def get_unique_id(data, device_type, entity_type, device_id): ) +def get_physical_entity_unique_id(data, domain, device_id, entity_type): + """Return a name-independent unique ID for a physical-device entity.""" + return get_unique_id(data, domain, entity_type, device_id) + + +def get_room_entity_unique_id(data, room_id, entity_type): + """Return a room-name-independent unique ID for a room entity.""" + return get_unique_id(data, "room", entity_type, room_id) + + +def get_light_binary_sensor_unique_id(data, light, sensor_type): + """Return the stable per-channel ID for a light capability sensor.""" + return get_unique_id( + data, + "binary_sensor", + sensor_type, + light.light_id, + ) + + +def build_physical_entity_unique_id_migration( + data, + previous_room_names=None, + legacy_device_names_by_id=None, + device_ids=None, +) -> dict: + """Map mutable-name physical entity IDs to immutable device-based IDs.""" + mapping = {} + previous_room_names = previous_room_names or {} + legacy_device_names_by_id = legacy_device_names_by_id or {} + binary_sensor_types = ( + "Smoke Alarm", + "Heat Alarm", + "Tamper Alarm", + "Fault Warning", + "Remote Alarm", + "Battery Defect", + "Controllable", + "PCM Mode", + "Is Tilt Supported", + "Is Open", + "Is Closed", + "Active", + ) + + for device in data.wiserhub.devices.all: + if device_ids is not None and device.id not in device_ids: + continue + device_names = set(legacy_device_names_by_id.get(device.id, ())) + device_names.add(get_legacy_device_name(data, device.id)) + product_type = device.product_type + room = data.wiserhub.rooms.get_by_device_id(device.id) + possible_room_names = {room.name} if room is not None else set() + if room is not None and ( + previous_name := previous_room_names.get(str(room.id)) + ): + possible_room_names.add(previous_name) + + if product_type == "RoomStat": + for room_name in possible_room_names: + mapping[ + get_unique_id( + data, + "sensor", + f"LTS Humidity {room_name}", + device.id, + ) + ] = get_physical_entity_unique_id( + data, "sensor", device.id, "humidity" + ) + + power_device_names = possible_room_names or { + f"{product_type} {device.id}" + } + historical_power_types = { + "Power": "power", + "Total Power": "energy", + "Equipment Power ": "power", + "Equipment Total Energy ": "energy", + "Power ": "power", + "Energy Delivered ": "energy", + "Energy Received ": "energy_received", + } + for power_device_name in power_device_names: + historical_power_types.update( + { + f"LTS Power {power_device_name}": "power", + f"LTS Energy {power_device_name}": "energy", + } + ) + for old_entity_type, new_entity_type in historical_power_types.items(): + mapping[ + get_unique_id(data, "sensor", old_entity_type, device.id) + ] = get_physical_entity_unique_id( + data, "sensor", device.id, new_entity_type + ) + + # Light capability sensors are keyed by channel ``light_id`` and have + # their own migration below. A multi-gang dimmer shares one physical + # device ID, so treating these as ordinary physical-device sensors + # would merge distinct channels. + if product_type not in {"DimmableLight", "OnOffLight"}: + for device_name in device_names: + for sensor_type in binary_sensor_types: + old_name = f"{device_name} {sensor_type}" + mapping[ + get_unique_id( + data, "binary_sensor", sensor_type, old_name + ) + ] = get_physical_entity_unique_id( + data, "binary_sensor", device.id, sensor_type + ) + + if product_type == "Shutter": + for device_name in device_names: + old_cover_id = get_uuid_unique_id( + f"{data.wiserhub.system.name}-Wisershutter-" + f"{device.id}-{device_name} Control" + ) + mapping[old_cover_id] = get_physical_entity_unique_id( + data, "cover", device.id, "control" + ) + for label, entity_type in ( + ("Away Mode Closes", "away_mode_closes"), + ("Respect Summer Comfort", "respect_summer_comfort"), + ): + mapping[ + get_unique_id( + data, + product_type, + f"{device_name} {label}", + device.id, + ) + ] = get_physical_entity_unique_id( + data, "switch", device.id, entity_type + ) + + if product_type in {"HeatingActuator", "CFMT"}: + for device_name in device_names: + mapping[ + get_unique_id( + data, + "system", + "number", + f"{device_name} Floor Temp Offset", + ) + ] = get_physical_entity_unique_id( + data, "number", device.id, "floor_temperature_offset" + ) + + if product_type in {"SmartPlug", "PowerTagC"}: + for device_name in device_names: + mapping[ + get_unique_id( + data, + product_type, + f"{device_name} Switch", + device.id, + ) + ] = get_physical_entity_unique_id( + data, "switch", device.id, "outlet" + ) + mapping[ + get_unique_id( + data, + product_type, + f"{device_name} Away Mode Turns Off", + device.id, + ) + ] = get_physical_entity_unique_id( + data, "switch", device.id, "away_mode_turns_off" + ) + + if hasattr(device, "interacts_with_room_climate"): + suffix = "Interacts With Room Climate" + for device_name in device_names: + old_id = get_legacy_unique_id( + data, + product_type, + f"{device_name} {suffix}", + device.id, + ) + mapping[get_uuid_unique_id(old_id)] = ( + get_physical_entity_unique_id( + data, + "switch", + device.id, + "interacts_with_room_climate", + ) + ) + + for ancillary in getattr(device, "threshold_sensors", ()): + suffix = f"{ancillary.quantity} Interacts With Room Climate" + for device_name in device_names: + old_id = get_legacy_unique_id( + data, + product_type, + f"{device_name} {suffix}", + device.id, + ) + mapping[get_uuid_unique_id(f"{old_id}_{ancillary.id}")] = ( + get_physical_entity_unique_id( + data, + "switch", + device.id, + f"{ancillary.quantity.lower()}_" + f"interacts_with_room_climate_{ancillary.id}", + ) + ) + + return mapping + + def build_light_unique_id_migration(data) -> dict: """Map pre-#683 light unique_ids to the new per-channel light_id scheme. @@ -275,7 +705,7 @@ def build_light_unique_id_migration(data) -> dict: old_id = light.id new_id = light.light_id ptype = light.product_type - old_name = get_device_name(data, old_id) + old_name = get_legacy_device_name(data, old_id) new_name = f"{ENTITY_PREFIX} {light.name}" # light (name-based -> light_id-based) @@ -292,22 +722,25 @@ def build_light_unique_id_migration(data) -> dict: # away-mode switch (both name and id embedded in the unique_id) mapping[ get_unique_id(data, ptype, f"{old_name} Away Mode Turns Off", old_id) - ] = get_unique_id(data, ptype, f"{new_name} Away Mode Turns Off", new_id) + ] = get_physical_entity_unique_id( + data, "switch", new_id, "light_away_mode_turns_off" + ) + mapping[ + get_unique_id(data, ptype, f"{new_name} Away Mode Turns Off", new_id) + ] = get_physical_entity_unique_id( + data, "switch", new_id, "light_away_mode_turns_off" + ) # capability binary_sensors (name-based) for stype in LIGHT_BINARY_SENSOR_TYPES: mapping[ get_unique_id(data, "binary_sensor", stype, f"{old_name} {stype}") - ] = get_unique_id(data, "binary_sensor", stype, f"{new_name} {stype}") + ] = get_light_binary_sensor_unique_id(data, light, stype) # Drop no-ops where the two id spaces happen to coincide for a light. return {old: new for old, new in mapping.items() if old != new} -def get_room_name(data, room_id): - return f"{ENTITY_PREFIX} {data.wiserhub.rooms.get_by_id(room_id).name}" - - def get_instance_count(hass: HomeAssistant) -> int: entries = [ entry diff --git a/custom_components/wiser/light.py b/custom_components/wiser/light.py index 19e203e0..04fde449 100644 --- a/custom_components/wiser/light.py +++ b/custom_components/wiser/light.py @@ -11,12 +11,13 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DATA, DOMAIN, ENTITY_PREFIX, MANUFACTURER_SCHNEIDER +from .const import DATA, DOMAIN, MANUFACTURER_SCHNEIDER from .entity import WiserEntityMixin from .helpers import ( get_device_name, get_hub_via_device_info, get_identifier, + get_light_entity_name, get_unique_id, hub_error_handler, ) @@ -72,6 +73,7 @@ def __init__(self, coordinator, light_id) -> None: # optimistic state if it is still the latest, so rapid toggles don't let # an earlier command wipe a newer command's value. self._optimistic_gen = 0 + self._confirmation_task = None _LOGGER.debug(f"{self._data.wiserhub.system.name} {self.name} initialise") async def async_force_update(self, delay: int = 0): @@ -80,6 +82,52 @@ async def async_force_update(self, delay: int = 0): await asyncio.sleep(delay) await self._data.async_refresh() + @hub_error_handler + async def _async_confirm_command(self, gen: int) -> None: + """Refresh the hub after it has had time to apply a light command.""" + try: + # Some hubs acknowledge the REST command before its Zigbee result is + # visible in hub data. Keep the optimistic state through one stale + # read and try once more instead of reverting until the next scan. + for _attempt in range(2): + if gen != self._optimistic_gen: + return + await self.async_force_update(2) + if gen != self._optimistic_gen or ( + self._optimistic_is_on is None + and self._optimistic_percentage is None + ): + return + finally: + # A newer command owns the optimistic state. Its confirmation task + # will clear that state after the hub has processed the newer value. + # If both confirmation reads remained stale, return to the real hub + # state rather than leaving an optimistic value indefinitely. + if gen == self._optimistic_gen: + self._clear_optimistic_state() + + @callback + def _schedule_command_confirmation(self, gen: int) -> None: + """Confirm a command without delaying the Home Assistant service call.""" + # A newer command makes the previous confirmation obsolete. Cancelling + # it also avoids an unnecessary full hub refresh after a rapid toggle. + if self._confirmation_task is not None: + self._confirmation_task.cancel() + self._confirmation_task = self.hass.async_create_task( + self._async_confirm_command(gen), + f"Wiser light command confirmation: {self.entity_id}", + ) + + async def async_will_remove_from_hass(self) -> None: + """Cancel delayed confirmation when Home Assistant removes the entity.""" + # Invalidate the task before cancellation so its finally block cannot + # write state for an entity that is being unloaded. + self._optimistic_gen += 1 + if self._confirmation_task is not None: + self._confirmation_task.cancel() + self._confirmation_task = None + await super().async_will_remove_from_hass() + @callback def _handle_coordinator_update(self) -> None: _LOGGER.debug(f"{self.name} updating") @@ -96,6 +144,11 @@ def _handle_coordinator_update(self) -> None: and self._device.is_on == self._optimistic_is_on ): self._optimistic_is_on = None + if ( + self._optimistic_percentage is not None + and self._device.current_percentage == self._optimistic_percentage + ): + self._optimistic_percentage = None self.async_write_ha_state() @property @@ -118,10 +171,7 @@ def is_on(self): @property def name(self): """Return the name of the Device.""" - # Use the per-channel light name directly. It already carries room and - # channel (e.g. "Badezimmer Badewanne") and is distinct per channel, so - # the two channels of a multi-gang dimmer no longer collide. - return f"{ENTITY_PREFIX} {self._device.name} Light" + return get_light_entity_name(self._data, self._device, "Light") @property def icon(self): @@ -211,43 +261,51 @@ def extra_state_attributes(self): @hub_error_handler async def async_turn_on(self, **kwargs): """Turn light on.""" - if ATTR_BRIGHTNESS in kwargs: - percentage = round((int(kwargs[ATTR_BRIGHTNESS]) / 255) * 100) - _LOGGER.debug(f"Setting brightness of {self.name} to {percentage}%") - await self._device.set_current_percentage(percentage) - self._optimistic_percentage = percentage - else: - _LOGGER.debug(f"Turning on {self.name}") - await self._device.turn_on() - # Reflect the command in the UI immediately (the hub only reports the new - # state a few seconds later); the refresh below then confirms it. self._optimistic_gen += 1 gen = self._optimistic_gen self._optimistic_is_on = True + percentage = None + if ATTR_BRIGHTNESS in kwargs: + percentage = round((int(kwargs[ATTR_BRIGHTNESS]) / 255) * 100) + self._optimistic_percentage = percentage self.async_write_ha_state() + try: - await self.async_force_update(2) - finally: - # Only the latest command clears the optimistic state; a superseded - # command must leave the newer command's value untouched. + if percentage is not None: + _LOGGER.debug(f"Setting brightness of {self.name} to {percentage}%") + await self._device.set_current_percentage(percentage) + else: + _LOGGER.debug(f"Turning on {self.name}") + await self._device.turn_on() + except Exception: if gen == self._optimistic_gen: self._clear_optimistic_state() + raise + + # The hub applies its Zigbee command asynchronously. Refresh after the + # existing two-second settling period, but do not hold open the HA + # service call while waiting for state confirmation. + self._schedule_command_confirmation(gen) return True @hub_error_handler async def async_turn_off(self, **kwargs): """Turn light off.""" - _LOGGER.debug(f"Turning off {self.name}") - await self._device.turn_off() self._optimistic_gen += 1 gen = self._optimistic_gen self._optimistic_is_on = False + self._optimistic_percentage = None self.async_write_ha_state() + try: - await self.async_force_update(2) - finally: + _LOGGER.debug(f"Turning off {self.name}") + await self._device.turn_off() + except Exception: if gen == self._optimistic_gen: self._clear_optimistic_state() + raise + + self._schedule_command_confirmation(gen) return True @callback diff --git a/custom_components/wiser/local_schedule.py b/custom_components/wiser/local_schedule.py new file mode 100644 index 00000000..92cbdba0 --- /dev/null +++ b/custom_components/wiser/local_schedule.py @@ -0,0 +1,343 @@ +"""Home Assistant-managed schedules for Wiser features not scheduled by the hub.""" + +from __future__ import annotations + +from copy import deepcopy +from datetime import timedelta +from time import strptime + +from homeassistant.helpers.storage import Store +from homeassistant.util import dt as dt_util + +from .const import DOMAIN + +LOCAL_HOT_WATER_SCHEDULE_ID = -1000 +LOCAL_HOT_WATER_SCHEDULE_NAME = "OpenTherm Hot Water" +LOCAL_HOT_WATER_SCHEDULE_TYPE = "HotWater" +LOCAL_SCHEDULE_STORAGE_VERSION = 1 +WEEKDAYS = ( + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", +) + + +def _schedule_preset_options(presets, minimum, maximum): + """Return schedule-safe presets, excluding temporary boosts.""" + options = [] + seen_names = set() + for preset in presets or []: + if not isinstance(preset, dict) or preset.get("duration_minutes", 0): + continue + name = str(preset.get("name", "")).strip() + try: + temperature = float(preset.get("temperature")) + except (TypeError, ValueError): + continue + folded_name = name.casefold() + if ( + not name + or folded_name in seen_names + or not minimum <= temperature <= maximum + ): + continue + seen_names.add(folded_name) + options.append( + { + "name": name, + "icon": str(preset.get("icon") or "mdi:water"), + "temperature": temperature, + } + ) + return options + + +def _default_schedule( + minimum, maximum=55.0, default_temperature=45.0, presets=None +): + """Return a useful split-day schedule with the minimum between periods.""" + scheduled_temperature = min( + maximum, + max(minimum, default_temperature), + ) + return { + "Id": LOCAL_HOT_WATER_SCHEDULE_ID, + "Name": LOCAL_HOT_WATER_SCHEDULE_NAME, + "Type": LOCAL_HOT_WATER_SCHEDULE_TYPE, + "SubType": LOCAL_HOT_WATER_SCHEDULE_TYPE, + "MinSetpoint": minimum, + "MaxSetpoint": maximum, + "PresetOptions": _schedule_preset_options( + presets, minimum, maximum + ), + "Assignments": [ + { + "id": LOCAL_HOT_WATER_SCHEDULE_ID, + "name": LOCAL_HOT_WATER_SCHEDULE_NAME, + } + ], + "ScheduleData": [ + { + "day": day, + "slots": [ + { + "Time": "00:00", + "Setpoint": f"{minimum:g}", + }, + { + "Time": "06:00", + "Setpoint": f"{scheduled_temperature:g}", + }, + { + "Time": "09:00", + "Setpoint": f"{minimum:g}", + }, + { + "Time": "17:00", + "Setpoint": f"{scheduled_temperature:g}", + }, + { + "Time": "22:00", + "Setpoint": f"{minimum:g}", + }, + ], + } + for day in WEEKDAYS + ], + } + + +def _validated_schedule( + schedule, + minimum, + maximum, + presets=None, + *, + replace_unknown_presets=False, +): + """Return a normalized schedule or raise ValueError.""" + if not isinstance(schedule, dict): + raise ValueError("Schedule must be an object") + days = schedule.get("ScheduleData") + if not isinstance(days, list): + raise ValueError("ScheduleData must be a list") + + preset_options = _schedule_preset_options(presets, minimum, maximum) + preset_names = { + option["name"].casefold(): option["name"] + for option in preset_options + } + normalized_days = [] + seen_days = set() + for day_data in days: + if not isinstance(day_data, dict) or day_data.get("day") not in WEEKDAYS: + raise ValueError("Schedule contains an invalid day") + day = day_data["day"] + if day in seen_days: + raise ValueError(f"Schedule contains duplicate {day} data") + seen_days.add(day) + slots = day_data.get("slots") + if not isinstance(slots, list): + raise ValueError(f"Schedule slots for {day} must be a list") + normalized_slots = [] + seen_times = set() + for slot in slots: + if not isinstance(slot, dict): + raise ValueError(f"Schedule slot for {day} must be an object") + value = str(slot.get("Time", "")) + try: + parsed_time = strptime(value, "%H:%M") + except ValueError as err: + raise ValueError(f"Invalid schedule time {value!r}") from err + raw_setpoint = slot.get("Setpoint") + raw_text = str(raw_setpoint).strip() + preset_name = preset_names.get(raw_text.casefold()) + if preset_name is not None: + normalized_setpoint = preset_name + else: + try: + setpoint = ( + minimum if raw_text.casefold() == "off" else float(raw_text) + ) + except ValueError as err: + if replace_unknown_presets: + setpoint = minimum + else: + raise ValueError( + f"Unknown hot-water preset {raw_text!r}" + ) from err + if not minimum <= setpoint <= maximum: + raise ValueError( + f"Schedule temperature must be between {minimum:g} and " + f"{maximum:g} °C" + ) + normalized_setpoint = f"{setpoint:g}" + canonical_time = f"{parsed_time.tm_hour:02}:{parsed_time.tm_min:02}" + if canonical_time in seen_times: + raise ValueError(f"Schedule contains duplicate {day} time") + seen_times.add(canonical_time) + normalized_slots.append( + { + "Time": canonical_time, + "Setpoint": normalized_setpoint, + } + ) + normalized_days.append( + { + "day": day, + "slots": sorted(normalized_slots, key=lambda slot: slot["Time"]), + } + ) + + if not any(day["slots"] for day in normalized_days): + raise ValueError("Schedule must contain at least one time slot") + normalized_by_day = {day["day"]: day for day in normalized_days} + name = str(schedule.get("Name") or LOCAL_HOT_WATER_SCHEDULE_NAME) + return { + "Id": LOCAL_HOT_WATER_SCHEDULE_ID, + "Name": name, + "Type": LOCAL_HOT_WATER_SCHEDULE_TYPE, + "SubType": LOCAL_HOT_WATER_SCHEDULE_TYPE, + "MinSetpoint": minimum, + "MaxSetpoint": maximum, + "PresetOptions": preset_options, + "Assignments": [ + { + "id": LOCAL_HOT_WATER_SCHEDULE_ID, + "name": name, + } + ], + "ScheduleData": [ + normalized_by_day.get(day, {"day": day, "slots": []}) + for day in WEEKDAYS + ], + } + + +def _schedule_position(schedule, now): + """Return current setpoint and next transition for a local datetime.""" + preset_temperatures = { + option["name"].casefold(): float(option["temperature"]) + for option in schedule.get("PresetOptions", []) + } + transitions = [] + days = {day["day"]: day["slots"] for day in schedule["ScheduleData"]} + for day_index, day in enumerate(WEEKDAYS): + for slot in days.get(day, []): + hour, minute = (int(part) for part in slot["Time"].split(":")) + raw_setpoint = str(slot["Setpoint"]).strip() + temperature = preset_temperatures.get(raw_setpoint.casefold()) + if temperature is None: + temperature = float(raw_setpoint) + transitions.append( + (day_index * 1440 + hour * 60 + minute, temperature) + ) + transitions.sort() + if not transitions: + return None, None + + current_minute = now.weekday() * 1440 + now.hour * 60 + now.minute + previous = [ + transition for transition in transitions if transition[0] <= current_minute + ] + current = previous[-1] if previous else transitions[-1] + future = [ + transition + for transition in transitions + if transition[0] > current_minute + ] + next_transition = future[0] if future else transitions[0] + days_ahead = (next_transition[0] - current_minute) % (7 * 1440) + if days_ahead == 0: + days_ahead = 7 * 1440 + next_datetime = (now + timedelta(minutes=days_ahead)).replace( + second=0, microsecond=0 + ) + return current[1], next_datetime + + +class OpenThermHotWaterSchedule: + """Persist and evaluate the integration-owned OpenTherm DHW schedule.""" + + def __init__( + self, + hass, + entry_id, + minimum=35.0, + maximum=55.0, + default_temperature=45.0, + presets=None, + ): + self._hass = hass + self._store = Store( + hass, + LOCAL_SCHEDULE_STORAGE_VERSION, + f"{DOMAIN}.{entry_id}.opentherm_hot_water_schedule", + ) + self._default = _default_schedule( + minimum, + maximum, + default_temperature, + presets, + ) + self._minimum = minimum + self._maximum = maximum + self._presets = presets or [] + self._schedule = deepcopy(self._default) + self._listeners = set() + + async def async_load(self): + """Load and validate saved data, retaining a safe default if corrupt.""" + saved = await self._store.async_load() + if saved is None: + return + try: + self._schedule = _validated_schedule( + saved, + self._minimum, + self._maximum, + self._presets, + replace_unknown_presets=True, + ) + except (TypeError, ValueError): + self._schedule = deepcopy(self._default) + + @property + def ws_schedule_data(self): + """Return data in the existing Wiser schedule-card format.""" + return deepcopy(self._schedule) + + @property + def name(self): + return self._schedule["Name"] + + def current(self, now=None): + """Return current setpoint and next transition.""" + return _schedule_position(self._schedule, now or dt_util.now()) + + def async_add_listener(self, listener): + """Subscribe an entity to saved schedule changes.""" + self._listeners.add(listener) + return lambda: self._listeners.discard(listener) + + async def async_save(self, schedule): + """Validate, persist and publish schedule changes.""" + self._schedule = _validated_schedule( + schedule, + self._minimum, + self._maximum, + self._presets, + ) + await self._store.async_save(self._schedule) + for listener in tuple(self._listeners): + listener() + + async def async_set_name(self, name): + """Rename the local schedule.""" + schedule = self.ws_schedule_data + schedule["Name"] = name + await self.async_save(schedule) diff --git a/custom_components/wiser/manifest.json b/custom_components/wiser/manifest.json index 10ef6ff3..99646a4d 100755 --- a/custom_components/wiser/manifest.json +++ b/custom_components/wiser/manifest.json @@ -19,7 +19,7 @@ "requirements": [ "aioWiserHeatAPI==1.7.3" ], - "version": "3.4.21b0", + "version": "4.0.0", "zeroconf": [ { "type": "_http._tcp.local.", diff --git a/custom_components/wiser/number.py b/custom_components/wiser/number.py index 0aaa8f30..0e9e336c 100644 --- a/custom_components/wiser/number.py +++ b/custom_components/wiser/number.py @@ -8,6 +8,7 @@ get_hub_device_info, get_hub_via_device_info, get_identifier, + get_physical_entity_unique_id, get_unique_id, hub_error_handler, ) @@ -86,7 +87,7 @@ def __init__(self, coordinator, name) -> None: async def async_force_update(self, delay: int = 0): _LOGGER.debug(f"Hub update initiated by {self._name}") if delay: - asyncio.sleep(delay) + await asyncio.sleep(delay) await self._data.async_refresh() @callback @@ -171,7 +172,7 @@ def __init__(self, coordinator, actuator, device_type) -> None: async def async_force_update(self, delay: int = 0): _LOGGER.debug(f"Hub update initiated by {self._name}") if delay: - asyncio.sleep(delay) + await asyncio.sleep(delay) await self._data.async_refresh() @callback @@ -213,10 +214,12 @@ def icon(self): @property def unique_id(self): - legacy_name = ( - f"{get_device_name(self._data, self._actuator.id)} Floor Temp Offset" + return get_physical_entity_unique_id( + self._data, + "number", + self._actuator.id, + "floor_temperature_offset", ) - return get_unique_id(self._data, "system", "number", legacy_name) @property def device_info(self): @@ -265,7 +268,7 @@ def __init__(self, coordinator, name) -> None: async def async_force_update(self, delay: int = 0): _LOGGER.debug(f"Hub update initiated by {self._name}") if delay: - asyncio.sleep(delay) + await asyncio.sleep(delay) await self._data.async_refresh() @callback @@ -349,7 +352,7 @@ def __init__(self, coordinator, name) -> None: async def async_force_update(self, delay: int = 0): _LOGGER.debug(f"Hub update initiated by {self._name}") if delay: - asyncio.sleep(delay) + await asyncio.sleep(delay) await self._data.async_refresh() @callback diff --git a/custom_components/wiser/opentherm_detection.py b/custom_components/wiser/opentherm_detection.py new file mode 100644 index 00000000..4158dee8 --- /dev/null +++ b/custom_components/wiser/opentherm_detection.py @@ -0,0 +1,101 @@ +"""Track whether a Wiser hub has a confirmed OpenTherm connection.""" + +from math import isfinite + +from .const import ( + CONF_OPENTHERM_EVER_CONNECTED, + CONF_OPENTHERM_HOT_WATER_CAPABLE, + CONF_OPENTHERM_HOT_WATER_CONTROL, +) +from .helpers import get_unique_id +from .opentherm import ( + OPENTHERM_BINARY_SENSOR_KEYS, + OPENTHERM_SENSOR_NAMES, +) + + +def opentherm_is_detected(config_entry, opentherm) -> bool: + """Return whether OpenTherm is connected now or has connected before. + + This sticky predicate controls entity creation and registry visibility so a + temporary outage does not discard entities or history. Entity availability + deliberately continues to require live OpenTherm data. + """ + return bool( + opentherm + and ( + config_entry.data.get(CONF_OPENTHERM_EVER_CONNECTED, False) + or getattr(opentherm, "connection_status", None) == "Connected" + ) + ) + + +def opentherm_hot_water_control_is_supported(config_entry, opentherm) -> bool: + """Return whether this entry has confirmed writable DHW support. + + An already-enabled option also counts as prior confirmation. This preserves + the entity across an upgrade or restart where the first OpenTherm response + is incomplete, before the new sticky capability flag has been stored. + """ + boiler_parameters = getattr(opentherm, "boiler_parameters", None) + return bool( + opentherm + and ( + getattr(boiler_parameters, "hw_setpoint_read_write", None) is True + or config_entry.data.get(CONF_OPENTHERM_HOT_WATER_CAPABLE, False) + or config_entry.options.get(CONF_OPENTHERM_HOT_WATER_CONTROL, False) + ) + ) + + +def opentherm_hot_water_limits(opentherm) -> tuple[float, float]: + """Return a valid boiler DHW setpoint range with safe fallbacks.""" + boiler_parameters = getattr(opentherm, "boiler_parameters", None) + try: + minimum = float( + getattr(boiler_parameters, "hw_setpoint_lower_bound", None) + ) + maximum = float( + getattr(boiler_parameters, "hw_setpoint_upper_bound", None) + ) + except (TypeError, ValueError): + return 35.0, 55.0 + if not isfinite(minimum) or not isfinite(maximum) or minimum > maximum: + return 35.0, 55.0 + return minimum, maximum + + +def opentherm_entity_unique_ids(data): + """Return the entity-registry IDs reserved for OpenTherm entities.""" + sensor_types = { + "LTS Boiler Flow Temperature", + "LTS Boiler Return Temperature", + } + binary_sensor_types = { + f"opentherm_{key}" for key in OPENTHERM_BINARY_SENSOR_KEYS + } + + for key in OPENTHERM_SENSOR_NAMES: + if key in OPENTHERM_BINARY_SENSOR_KEYS or key in { + "ch_flow_temperature", + "ch_return_temperature", + }: + continue + sensor_types.add( + "relative_modulation_level" + if key == "relative_modulation_level" + else f"opentherm_{key}" + ) + + unique_ids = { + ("sensor", get_unique_id(data, "sensor", sensor_type, 0)) + for sensor_type in sensor_types + } + unique_ids.update( + ( + "binary_sensor", + get_unique_id(data, "binary_sensor", sensor_type, 0), + ) + for sensor_type in binary_sensor_types + ) + return unique_ids diff --git a/custom_components/wiser/opentherm_hot_water.py b/custom_components/wiser/opentherm_hot_water.py new file mode 100644 index 00000000..06d133d3 --- /dev/null +++ b/custom_components/wiser/opentherm_hot_water.py @@ -0,0 +1,674 @@ +"""OpenTherm domestic-hot-water control for Wiser.""" + +from __future__ import annotations + +import asyncio +from datetime import timedelta +import logging +from math import isfinite +from uuid import uuid4 + +from homeassistant.components.water_heater import ( + WaterHeaterEntity, + WaterHeaterEntityFeature, +) +from homeassistant.const import ( + ATTR_TEMPERATURE, + ATTR_UNIT_OF_MEASUREMENT, + STATE_OFF, + STATE_UNAVAILABLE, + STATE_UNKNOWN, + UnitOfTemperature, +) +from homeassistant.core import HomeAssistant, callback +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers.event import ( + async_call_later, + async_track_point_in_time, + async_track_state_change_event, +) +from homeassistant.helpers.restore_state import RestoreEntity +from homeassistant.helpers.update_coordinator import CoordinatorEntity +from homeassistant.util.unit_conversion import TemperatureConverter + +from .const import ( + ATTR_HUB, + ATTR_OPENTHERM_ENDPOINT, + ATTR_OPENTHERM_PARAM, + ATTR_OPENTHERM_REQUEST_ID, + ATTR_OPENTHERM_TEMPERATURE, + CONF_OPENTHERM_HOT_WATER_CONTROL, + CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP, + CONF_OPENTHERM_HOT_WATER_PRESETS, + CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID, + DATA, + DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP, + DEFAULT_OPENTHERM_HOT_WATER_PRESET_TEMP, + DOMAIN, + EVENT_OPENTHERM_COMMAND_FAILED, + OPENTHERM_HOT_WATER_SCHEDULE, + OPENTHERM_HOT_WATER_DEFAULT_BOOSTS, + WISER_SERVICES, +) +from .entity import WiserEntityMixin +from .helpers import get_hub_device_info, get_unique_id +from .local_schedule import LOCAL_HOT_WATER_SCHEDULE_ID +from .opentherm_detection import ( + opentherm_hot_water_control_is_supported, + opentherm_hot_water_limits, + opentherm_is_detected, +) + +_LOGGER = logging.getLogger(__name__) + +MODE_MANUAL = "Manual" +MODE_AUTO = "Auto" + + +def _normalize_presets(value, minimum=35.0, maximum=55.0): + """Return safe preset dictionaries from stored options.""" + if not isinstance(value, list): + return [] + presets = [] + for item in value: + if not isinstance(item, dict): + continue + try: + temperature = float(item["temperature"]) + duration = int(item.get("duration_minutes", 0)) + except (KeyError, TypeError, ValueError): + continue + name = str(item.get("name", "")).strip() + icon = str(item.get("icon", "")).strip() + if ( + not name + or name.casefold() in {"off", "manual", "auto", "custom"} + or not 0 <= duration <= 999 + ): + continue + temperature = min(max(temperature, minimum), maximum) + presets.append( + { + "name": name, + "icon": icon, + "temperature": temperature, + "duration_minutes": duration, + } + ) + return presets + + +def _default_presets(minimum=35.0, maximum=55.0): + """Return the original control's default timed boost presets.""" + return [ + { + "name": f"Boost {duration} Mins", + "icon": f"mdi:numeric-{index}", + "temperature": min( + max(DEFAULT_OPENTHERM_HOT_WATER_PRESET_TEMP, minimum), maximum + ), + "duration_minutes": duration, + } + for index, duration in enumerate( + OPENTHERM_HOT_WATER_DEFAULT_BOOSTS, start=1 + ) + ] + + +def _finite_float(value): + """Return a finite float, or None for unavailable API data.""" + if isinstance(value, bool): + return None + try: + parsed = float(value) + except (TypeError, ValueError): + return None + return parsed if isfinite(parsed) else None + + +async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): + """Set up optional OpenTherm hot-water control.""" + if not config_entry.options.get(CONF_OPENTHERM_HOT_WATER_CONTROL, False): + return + + data = hass.data[DOMAIN][config_entry.entry_id][DATA] + opentherm = data.wiserhub.system.opentherm + if not opentherm_is_detected(config_entry, opentherm): + return + if not opentherm_hot_water_control_is_supported(config_entry, opentherm): + _LOGGER.warning( + "OpenTherm hot-water control is enabled but the boiler does not " + "report a writable DHW setpoint" + ) + return + + schedule = hass.data[DOMAIN][config_entry.entry_id].get( + OPENTHERM_HOT_WATER_SCHEDULE + ) + if schedule is None: + return + + async_add_entities([WiserOpenThermHotWater(data, config_entry, schedule)]) + + +class WiserOpenThermHotWater( + WiserEntityMixin, CoordinatorEntity, RestoreEntity, WaterHeaterEntity +): + """Control the boiler DHW setpoint through the Wiser OpenTherm endpoint.""" + + _attr_has_entity_name = True + _attr_translation_key = "opentherm_hot_water" + _attr_temperature_unit = UnitOfTemperature.CELSIUS + _attr_target_temperature_step = 0.5 + _attr_supported_features = ( + WaterHeaterEntityFeature.TARGET_TEMPERATURE + | WaterHeaterEntityFeature.OPERATION_MODE + | WaterHeaterEntityFeature.ON_OFF + ) + + def __init__(self, data, config_entry, schedule): + """Initialize the entity.""" + super().__init__(data) + self._data = data + self._entry_id = config_entry.entry_id + self._schedule = schedule + self._minimum, self._maximum = opentherm_hot_water_limits( + data.wiserhub.system.opentherm + ) + self._manual_temperature = float( + config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP, + DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP, + ) + ) + self._manual_temperature = min( + max(self._manual_temperature, self.min_temp), self.max_temp + ) + self._presets = _normalize_presets( + config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_PRESETS, + _default_presets(self._minimum, self._maximum), + ), + self._minimum, + self._maximum, + ) + self._temperature_sensor_entity_id = config_entry.options.get( + CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID + ) + self._operation = MODE_AUTO + self._last_schedule_request = None + self._target_write_in_progress = False + self._cancel_schedule_timer = None + self._cancel_boost_timer = None + self._boost_deadline = None + self._pending_request_id = None + self._pending_previous_operation = None + self._pending_previous_boost_seconds = None + self._attr_unique_id = get_unique_id( + data, + "water_heater", + "opentherm_hot_water", + data.wiserhub.system.name, + ) + + @property + def device_info(self): + """Attach the entity to the physical HeatHub.""" + return get_hub_device_info(self._data) + + @property + def _opentherm(self): + return self._data.wiserhub.system.opentherm + + @property + def available(self): + """Require current coordinator and OpenTherm connectivity.""" + return bool( + super().available + and self._opentherm + and self._opentherm.connection_status == "Connected" + and getattr( + getattr(self._opentherm, "boiler_parameters", None), + "hw_setpoint_read_write", + None, + ) + is True + ) + + @property + def current_temperature(self): + """Return the measured domestic-hot-water temperature.""" + if self._temperature_sensor_entity_id: + sensor_state = self.hass.states.get( + self._temperature_sensor_entity_id + ) + if sensor_state and sensor_state.state not in ( + STATE_UNAVAILABLE, + STATE_UNKNOWN, + ): + sensor_temperature = _finite_float(sensor_state.state) + if sensor_temperature is not None: + unit = sensor_state.attributes.get( + ATTR_UNIT_OF_MEASUREMENT, + UnitOfTemperature.CELSIUS, + ) + try: + return TemperatureConverter.convert( + sensor_temperature, + unit, + UnitOfTemperature.CELSIUS, + ) + except (TypeError, ValueError): + pass + operational_data = getattr(self._opentherm, "operational_data", None) + return _finite_float(getattr(operational_data, "hw_temperature", None)) + + @property + def target_temperature(self): + """Return the setpoint reported by Wiser.""" + boiler_parameters = getattr(self._opentherm, "boiler_parameters", None) + value = getattr(boiler_parameters, "hw_setpoint", None) + if value is None: + value = getattr(self._opentherm, "hw_flow_setpoint", None) + return _finite_float(value) + + @property + def min_temp(self): + """Return the boiler-reported lower bound when available.""" + return self._minimum + + @property + def max_temp(self): + """Return the boiler-reported upper bound when available.""" + return self._maximum + + @property + def operation_list(self): + """Return the operations rendered by the hot-water mode control.""" + return [ + STATE_OFF, + MODE_MANUAL, + MODE_AUTO, + *(preset["name"] for preset in self._presets), + ] + + @property + def current_operation(self): + """Return the selected operating mode.""" + return self._operation + + @property + def extra_state_attributes(self): + """Expose the selected schedule and its next transition.""" + _, next_event = self._schedule.current() + return { + # Used by Wiser's card feature to scope its icon mode control to + # this water heater rather than every water_heater entity in HA. + "wiser_opentherm_hot_water": True, + "custom_ui_more_info": "opentherm-hot-water-control-more-info", + "opentherm_presets": self._presets, + "schedule_id": LOCAL_HOT_WATER_SCHEDULE_ID, + "schedule_name": self._schedule.name, + "next_schedule_event": next_event, + } + + async def async_added_to_hass(self): + """Restore mode, subscribe to changes, and start the local schedule.""" + await super().async_added_to_hass() + if self._temperature_sensor_entity_id: + self.async_on_remove( + async_track_state_change_event( + self.hass, + [self._temperature_sensor_entity_id], + self._async_temperature_sensor_changed, + ) + ) + previous = await self.async_get_last_state() + reset_expired_boost = False + if previous: + restored_mode = { + "manual": MODE_MANUAL, + "heat": MODE_MANUAL, + "auto": MODE_AUTO, + "off": STATE_OFF, + }.get(previous.state.lower()) + preset = self._preset_for_mode(previous.state) + if preset is not None: + # Timed boosts cannot be resumed without a saved deadline. + restored_mode = ( + MODE_MANUAL + if preset["duration_minutes"] + else preset["name"] + ) + reset_expired_boost = bool(preset["duration_minutes"]) + if restored_mode in self.operation_list: + self._operation = restored_mode + + self.async_on_remove( + self._schedule.async_add_listener(self._async_schedule_changed) + ) + self.async_on_remove( + self.hass.bus.async_listen( + EVENT_OPENTHERM_COMMAND_FAILED, + self._async_command_failed, + ) + ) + if self._operation == MODE_AUTO: + self.hass.async_create_task(self._async_apply_schedule_safely()) + elif reset_expired_boost: + # A timed boost has no persisted deadline. Restore Manual and write + # its configured target so the boiler cannot remain at boost heat. + self.hass.async_create_task(self._async_finish_boost_safely()) + self._schedule_next_transition() + + @callback + def _async_temperature_sensor_changed(self, _event): + """Publish a changed external hot-water temperature reading.""" + self.async_write_ha_state() + + @callback + def _handle_coordinator_update(self) -> None: + """Reconcile a scheduled target if its transition timer was missed.""" + if self._operation == MODE_AUTO and not self._target_write_in_progress: + temperature = self._scheduled_temperature() + if ( + temperature is not None + and (MODE_AUTO, temperature) != self._last_schedule_request + ): + self.hass.async_create_task( + self._async_apply_schedule_safely() + ) + self.async_write_ha_state() + + async def async_will_remove_from_hass(self): + """Cancel the pending schedule transition.""" + if self._cancel_schedule_timer is not None: + self._cancel_schedule_timer() + self._cancel_schedule_timer = None + self._cancel_active_boost() + await super().async_will_remove_from_hass() + + def _preset_for_mode(self, mode): + """Return the configured preset matching an operation name.""" + return next( + (preset for preset in self._presets if preset["name"] == mode), + None, + ) + + def _cancel_active_boost(self): + """Cancel a pending timed-preset return.""" + if self._cancel_boost_timer is not None: + self._cancel_boost_timer() + self._cancel_boost_timer = None + self._boost_deadline = None + + def _remaining_boost_seconds(self): + """Return the remaining timed-preset duration, if any.""" + if self._boost_deadline is None: + return None + return max( + 0, + self._boost_deadline - asyncio.get_running_loop().time(), + ) + + @callback + def _async_command_failed(self, event): + """Restore the prior UI operation after confirmed boiler rejection.""" + if event.data.get("request_id") != self._pending_request_id: + return + failed_temperature = _finite_float(event.data.get("requested")) + previous_operation = self._pending_previous_operation + previous_boost_seconds = self._pending_previous_boost_seconds + self._pending_request_id = None + self._pending_previous_operation = None + self._pending_previous_boost_seconds = None + if previous_operation not in self.operation_list: + return + self._cancel_active_boost() + self._operation = previous_operation + # A scheduled command has already had its one service-level retry. + # Remember that failed request so coordinator reconciliation does not + # send it repeatedly; a different schedule block will still be sent. + self._last_schedule_request = ( + (MODE_AUTO, failed_temperature) + if previous_operation == MODE_AUTO + and failed_temperature is not None + else None + ) + previous_preset = self._preset_for_mode(previous_operation) + if ( + previous_preset is not None + and previous_preset["duration_minutes"] + and previous_boost_seconds + ): + self._start_boost(previous_boost_seconds / 60) + self.async_write_ha_state() + + @callback + def _async_boost_finished(self, _now): + """Return a completed timed preset to Manual mode.""" + self._cancel_boost_timer = None + self._boost_deadline = None + self.hass.async_create_task(self._async_finish_boost_safely()) + + async def _async_finish_boost_safely(self): + """Apply the Manual setpoint after a timed preset expires.""" + try: + await self.async_set_operation_mode(MODE_MANUAL) + except HomeAssistantError as err: + _LOGGER.warning("Unable to end OpenTherm hot-water boost: %s", err) + + def _start_boost(self, minutes): + """Start the return timer for a timed preset.""" + self._cancel_active_boost() + if minutes: + self._boost_deadline = ( + asyncio.get_running_loop().time() + minutes * 60 + ) + self._cancel_boost_timer = async_call_later( + self.hass, + minutes * 60, + self._async_boost_finished, + ) + + @callback + def _async_schedule_changed(self) -> None: + """Apply a saved integration-owned schedule change.""" + if self._operation == MODE_AUTO: + self._last_schedule_request = None + self.hass.async_create_task(self._async_apply_schedule_safely()) + self._schedule_next_transition() + self.async_write_ha_state() + + @callback + def _schedule_next_transition(self) -> None: + """Wake the entity when the next local schedule slot starts.""" + if self._cancel_schedule_timer is not None: + self._cancel_schedule_timer() + self._cancel_schedule_timer = None + _, next_event = self._schedule.current() + if next_event is None: + return + # Run just beyond the minute boundary so schedule evaluation cannot + # observe the previous slot if wall-clock and event-loop time differ by + # a few milliseconds. Coordinator updates provide a second recovery + # path if Home Assistant sleeps through this absolute-time callback. + self._cancel_schedule_timer = async_track_point_in_time( + self.hass, + self._async_schedule_transition, + next_event + timedelta(seconds=1), + ) + + @callback + def _async_schedule_transition(self, _now) -> None: + """Apply a due slot and arm the following transition.""" + self._cancel_schedule_timer = None + self._last_schedule_request = None + if self._operation == MODE_AUTO: + self.hass.async_create_task(self._async_apply_schedule_safely()) + self._schedule_next_transition() + self.async_write_ha_state() + + def _scheduled_temperature(self): + """Resolve the current integration-owned schedule setpoint.""" + temperature, _ = self._schedule.current() + return temperature + + async def _async_apply_schedule_safely(self): + """Apply a background schedule transition without leaking task errors.""" + try: + await self._async_apply_schedule() + except HomeAssistantError as err: + _LOGGER.warning("Unable to apply OpenTherm hot-water schedule: %s", err) + + async def _async_apply_schedule( + self, previous_operation=None, previous_boost_seconds=None + ): + """Apply the active block once, including attribute-only transitions.""" + temperature = self._scheduled_temperature() + request = (MODE_AUTO, temperature) + if temperature is None or request == self._last_schedule_request: + return + self._last_schedule_request = request + try: + await self._async_set_target( + temperature, + previous_operation=previous_operation, + previous_boost_seconds=previous_boost_seconds, + ) + except HomeAssistantError: + self._last_schedule_request = None + raise + + async def _async_set_target( + self, + temperature, + previous_operation=None, + previous_boost_seconds=None, + ): + """Use Wiser's existing serialized write and confirmation path.""" + if not self.min_temp <= temperature <= self.max_temp: + raise HomeAssistantError( + f"Hot-water target must be between {self.min_temp:g} and " + f"{self.max_temp:g} °C" + ) + request_id = uuid4().hex + self._pending_request_id = request_id + self._pending_previous_operation = ( + self._operation + if previous_operation is None + else previous_operation + ) + self._pending_previous_boost_seconds = ( + self._remaining_boost_seconds() + if previous_boost_seconds is None + else previous_boost_seconds + ) + # The service refreshes the coordinator before returning. Suppress + # Auto reconciliation during that refresh so it cannot race this + # foreground write and send the schedule target prematurely. + self._target_write_in_progress = True + try: + await self.hass.services.async_call( + DOMAIN, + WISER_SERVICES["SERVICE_SEND_OPENTHERM_COMMAND"], + { + ATTR_HUB: self._entry_id, + ATTR_OPENTHERM_ENDPOINT: "preDefinedRemoteBoilerParameters", + ATTR_OPENTHERM_PARAM: "dhwSetpoint", + ATTR_OPENTHERM_TEMPERATURE: temperature, + ATTR_OPENTHERM_REQUEST_ID: request_id, + }, + blocking=True, + context=self._context, + ) + except HomeAssistantError: + if self._pending_request_id == request_id: + self._pending_request_id = None + self._pending_previous_operation = None + self._pending_previous_boost_seconds = None + raise + finally: + self._target_write_in_progress = False + + async def async_set_temperature(self, **kwargs): + """Set a target, retaining Auto as an override until its next slot.""" + temperature = _finite_float(kwargs.get(ATTR_TEMPERATURE)) + if temperature is None: + return + auto_override = self._operation == MODE_AUTO + previous_schedule_request = self._last_schedule_request + if auto_override: + # Mark the current block before writing because the OpenTherm + # service refreshes the coordinator while the call is in flight. + self._last_schedule_request = ( + MODE_AUTO, + self._scheduled_temperature(), + ) + try: + await self._async_set_target(temperature) + except HomeAssistantError: + self._last_schedule_request = previous_schedule_request + raise + self._cancel_active_boost() + if auto_override: + # Treat a temperature adjustment in Auto like Wiser's native + # temporary override. Mark the current schedule block as handled + # so coordinator refreshes do not immediately replace it. The next + # scheduled callback clears this marker and applies its setpoint. + self._last_schedule_request = ( + MODE_AUTO, + self._scheduled_temperature(), + ) + else: + self._operation = MODE_MANUAL + self._last_schedule_request = None + self.async_write_ha_state() + + async def async_set_operation_mode(self, operation_mode): + """Select off, manual, or scheduled operation.""" + if operation_mode not in self.operation_list: + raise HomeAssistantError( + f"Unsupported hot-water operation mode: {operation_mode}" + ) + preset = self._preset_for_mode(operation_mode) + previous_operation = self._operation + previous_boost_seconds = self._remaining_boost_seconds() + if operation_mode == STATE_OFF: + await self._async_set_target(self.min_temp) + elif operation_mode == MODE_MANUAL: + await self._async_set_target(self._manual_temperature) + elif preset is not None: + await self._async_set_target(preset["temperature"]) + elif operation_mode != MODE_AUTO: + raise HomeAssistantError( + f"Unknown hot-water preset: {operation_mode}" + ) + self._cancel_active_boost() + self._operation = operation_mode + self._last_schedule_request = None + if operation_mode == MODE_AUTO: + try: + await self._async_apply_schedule( + previous_operation=previous_operation, + previous_boost_seconds=previous_boost_seconds, + ) + except HomeAssistantError: + # Auto changes the local operation before applying its first + # schedule target. Restore the prior UI mode if that initial + # foreground command could not be sent at all. + self._operation = previous_operation + self._last_schedule_request = None + self.async_write_ha_state() + raise + elif preset is not None: + self._start_boost(preset["duration_minutes"]) + self.async_write_ha_state() + + async def async_turn_on(self, **kwargs): + """Resume automatic scheduled operation.""" + await self.async_set_operation_mode(MODE_AUTO) + + async def async_turn_off(self, **kwargs): + """Apply the configured minimum/off setpoint.""" + await self.async_set_operation_mode(STATE_OFF) diff --git a/custom_components/wiser/select.py b/custom_components/wiser/select.py index b6966cf4..a11068ee 100644 --- a/custom_components/wiser/select.py +++ b/custom_components/wiser/select.py @@ -3,7 +3,6 @@ from .const import ( DATA, DOMAIN, - ENTITY_PREFIX, HOT_WATER, MANUFACTURER, ) @@ -12,6 +11,7 @@ from .helpers import ( get_device_name, get_hub_via_device_info, + get_light_entity_name, get_unique_id, get_identifier, hub_error_handler, @@ -89,7 +89,7 @@ def __init__(self, coordinator) -> None: async def async_force_update(self, delay: int = 0): _LOGGER.debug(f"Hub update initiated by {self._attr_translation_key}") if delay: - asyncio.sleep(delay) + await asyncio.sleep(delay) await self._data.async_refresh() @callback @@ -260,9 +260,7 @@ def _handle_coordinator_update(self) -> None: @property def name(self): """Return Name of device.""" - # Per-channel light name so the two channels of a multi-gang dimmer do - # not collide (WiserSelectEntity.name would use the shared device id). - return f"{ENTITY_PREFIX} {self._device.name} Mode" + return get_light_entity_name(self._data, self._device, "Mode") @property def unique_id(self): @@ -324,7 +322,9 @@ def unique_id(self): @property def name(self): """Return Name of device.""" - return f"{ENTITY_PREFIX} {self._device.name} Power On Behaviour" + return get_light_entity_name( + self._data, self._device, "Power On Behaviour" + ) @property def current_option(self) -> str: @@ -376,7 +376,7 @@ def unique_id(self): @property def name(self): """Return Name of device.""" - return f"{ENTITY_PREFIX} {self._device.name} Led Indicator" + return get_light_entity_name(self._data, self._device, "LED Indicator") @property def current_option(self) -> str: diff --git a/custom_components/wiser/sensor.py b/custom_components/wiser/sensor.py index 6d4bb46f..e66ef12d 100755 --- a/custom_components/wiser/sensor.py +++ b/custom_components/wiser/sensor.py @@ -11,7 +11,7 @@ from datetime import datetime, timedelta import logging -from aioWiserHeatAPI.const import TEXT_UNKNOWN +from aioWiserHeatAPI.const import TEXT_OFF, TEXT_ON, TEXT_UNKNOWN from homeassistant.components.sensor import ( SensorDeviceClass, @@ -22,9 +22,10 @@ ATTR_BATTERY_LEVEL, EntityCategory, LIGHT_LUX, + STATE_OFF, + STATE_ON, STATE_UNAVAILABLE, STATE_UNKNOWN, - STATE_ON, UnitOfTemperature, UnitOfTime, UnitOfElectricCurrent, @@ -41,6 +42,9 @@ from aioWiserHeatAPI.wiserhub import TEMP_MINIMUM, TEMP_OFF from .const import ( + CONF_ADDITIONAL_SENSORS, + CONF_SCHEDULE_SETPOINT_ROOMS, + CONF_EQUIPMENT_SENSORS, CONF_OPENTHERM_SENSORS, DATA, DOMAIN, @@ -56,8 +60,11 @@ get_device_name, get_hub_device_info, get_hub_via_device_info, + get_physical_entity_unique_id, get_identifier, + get_room_entity_unique_id, get_unique_id, + room_has_climate, ) from .opentherm import ( OPENTHERM_BINARY_SENSOR_KEYS, @@ -70,10 +77,16 @@ opentherm_sensor_is_enabled, relative_modulation_level as _relative_modulation_level, ) +from .opentherm_detection import opentherm_is_detected from .temperature import room_target_temperature _LOGGER = logging.getLogger(__name__) +ON_OFF_STATES = { + TEXT_ON: STATE_ON, + TEXT_OFF: STATE_OFF, +} + OPENTHERM_TEMPERATURE_SENSOR_KEYS = frozenset( { @@ -107,10 +120,31 @@ } +def _selected_room_scheduled_setpoint_sensors(data, selected_room_ids): + """Create scheduled setpoint sensors for selected rooms without climate.""" + selected_room_ids = {str(room_id) for room_id in selected_room_ids or ()} + return [ + WiserRoomScheduledSetpointSensor(data, room.id) + for room in data.wiserhub.rooms.all + if str(room.id) in selected_room_ids and not room_has_climate(room) + ] + + async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): """Initialize the entry.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA] # Get Handler wiser_sensors = [] + equipment_sensors_enabled = config_entry.options.get( + CONF_EQUIPMENT_SENSORS, False + ) + additional_sensor_options = config_entry.options.get( + CONF_ADDITIONAL_SENSORS, {} + ) + if not isinstance(additional_sensor_options, dict): + additional_sensor_options = {} + schedule_setpoint_rooms = additional_sensor_options.get( + CONF_SCHEDULE_SETPOINT_ROOMS, [] + ) # Add signal sensors for all devices _LOGGER.debug("Setting up Device sensors") @@ -192,10 +226,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie # Add enabled OpenTherm sensors. Flow and return temperatures retain their # historical default; every additional attribute is opt-in. - if ( - data.wiserhub.system.opentherm is not None - and data.wiserhub.system.opentherm.enabled - ): + if opentherm_is_detected(config_entry, data.wiserhub.system.opentherm): _LOGGER.debug("Setting up Opentherm sensors") opentherm = data.wiserhub.system.opentherm configured_sensors = config_entry.options.get(CONF_OPENTHERM_SENSORS) @@ -268,7 +299,9 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie _LOGGER.debug("Setting up Smart Plug power sensors") for smartplug in data.wiserhub.devices.smartplugs.all: # Hub V2 equipment telemetry - if smartplug.equipment_id > 0: + if smartplug.equipment is not None: + if equipment_sensors_enabled: + wiser_sensors.append(WiserEquipmentSensor(data, smartplug.id)) wiser_sensors.extend( [ WiserLTSPowerSensor( @@ -304,6 +337,34 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie ) + # Add EV charger electrical telemetry. EVSE devices are published by the + # Gen 2 hub as Equipment records rather than as SmartPlug records. + evse_collection = getattr(data.wiserhub.devices, "evse", None) + if evse_collection: + _LOGGER.debug("Setting up EV charger power sensors") + for evse in evse_collection.all: + if equipment_sensors_enabled: + wiser_sensors.append(WiserEquipmentSensor(data, evse.id)) + wiser_sensors.extend( + [ + WiserLTSPowerSensor( + data, evse.id, sensor_type="Power", name="Power" + ), + WiserLTSPowerSensor( + data, + evse.id, + sensor_type="Energy", + name="Total Energy", + ), + WiserCurrentVoltageSensor( + data, evse.id, sensor_type="Voltage" + ), + WiserCurrentVoltageSensor( + data, evse.id, sensor_type="Current" + ), + ] + ) + # Add power sensors for PTE (v2Hub) if data.wiserhub.devices.power_tags: for power_tag in data.wiserhub.devices.power_tags.all: @@ -332,6 +393,17 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie ), ] ) + # Add an equipment sensor for PowerTags + if equipment_sensors_enabled and power_tag.equipment is not None: + wiser_sensors.append(WiserEquipmentSensor(data, power_tag.id)) + + # Add equipment sensors for PowerTag Control devices + if equipment_sensors_enabled and data.wiserhub.devices.power_tags_c: + for power_tag_control in data.wiserhub.devices.power_tags_c.all: + if power_tag_control.equipment is not None: + wiser_sensors.append( + WiserEquipmentSensor(data, power_tag_control.id) + ) # Add LTS sensors - for room temp and target temp _LOGGER.debug("Setting up LTS sensors") @@ -350,16 +422,23 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie if room.roomstat_id: wiser_sensors.append(WiserLTSHumiditySensor(data, room.roomstat_id)) - # Add temp sensors for smoke alarms wiser_sensors.extend( - WiserLTSTempSensor( - data, - device.id, - sensor_type="smokealarm_temp", - ) - for device in data.wiserhub.devices.smokealarms.all + _selected_room_scheduled_setpoint_sensors(data, schedule_setpoint_rooms) ) + # Add smoke-alarm sensors + for device in data.wiserhub.devices.smokealarms.all: + wiser_sensors.extend( + [ + WiserLTSTempSensor( + data, + device.id, + sensor_type="smokealarm_temp", + ), + WiserSmokeAlarmReportCountSensor(data, device.id), + ] + ) + # Add temp sensors for ITRVs _LOGGER.debug("Setting up smartvalve remperature sensors") wiser_sensors.extend( @@ -377,7 +456,12 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie _LOGGER.debug("Setting up Heating Actuator LTS sensors") for heating_actuator in data.wiserhub.devices.heating_actuators.all: # Hub V2 equipment telemetry - if heating_actuator.equipment_id > 0: + if heating_actuator.equipment is not None: + if equipment_sensors_enabled: + wiser_sensors.append( + WiserEquipmentSensor(data, heating_actuator.id) + ) + wiser_sensors.extend( [ WiserLTSPowerSensor( @@ -574,6 +658,55 @@ def device_info(self): } +class WiserSmokeAlarmReportCountSensor(WiserSensor): + """Diagnostic count of reports received from a smoke alarm.""" + + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_icon = "mdi:counter" + + def __init__(self, data, device_id=0) -> None: + """Initialize the report-count sensor.""" + super().__init__( + data, + device_id, + "report_count", + translation_key="report_count", + ) + self._device = self._data.wiserhub.devices.get_by_id(self._device_id) + self._state = self._device.report_count + + @callback + def _handle_coordinator_update(self) -> None: + """Refresh the report count.""" + super()._handle_coordinator_update() + self._device = self._data.wiserhub.devices.get_by_id(self._device_id) + self._state = self._device.report_count + self.async_write_ha_state() + + @property + def unique_id(self): + """Return a name-independent unique ID.""" + return get_physical_entity_unique_id( + self._data, + "sensor", + self._device_id, + "report_count", + ) + + @property + def device_info(self): + """Attach the entity to its physical smoke-alarm device.""" + return { + "name": get_device_name(self._data, self._device_id), + "identifiers": {(DOMAIN, get_identifier(self._data, self._device_id))}, + "manufacturer": MANUFACTURER, + "model": self._device.product_type, + "sw_version": self._device.firmware_version, + **get_device_area_info(self._data, self._device_id), + **get_hub_via_device_info(self._data), + } + + class WiserDeviceSignalSensor(WiserSensor): """Definition of Wiser Device Sensor.""" @@ -855,26 +988,27 @@ def _handle_coordinator_update(self) -> None: self._device = self._data.wiserhub.heating_channels.get_by_id( self._device_id ) - self._state = ( + raw_state = ( self._device.heating_relay_status if self._device.heating_relay_status != TEXT_UNKNOWN else self._device.demand_on_off_output ) else: self._device = self._data.wiserhub.hotwater - self._state = self._device.current_state + raw_state = self._device.current_state + self._state = ON_OFF_STATES.get(raw_state) self.async_write_ha_state() @property def icon(self): """Return icon.""" if self._sensor_type == "Heating": - if self._state == "Off": + if self._state == STATE_OFF: return "mdi:radiator-disabled" return "mdi:radiator" # Hot water circuit - if self._state == "Off": + if self._state == STATE_OFF: return "mdi:fire-off" return "mdi:fire" @@ -893,15 +1027,22 @@ def extra_state_attributes(self): attrs[f"is_smartvalve_preventing_demand_{heating_channel.name}"] = ( heating_channel.is_smart_valve_preventing_demand ) - if self._data.wiserhub.system.opentherm.connection_status == "Connected": - opentherm = self._data.wiserhub.system.opentherm.operational_data - attrs["ch_flow_temperature"] = opentherm.ch_flow_temperature - attrs["ch_pressure_bar"] = opentherm.ch_pressure_bar - attrs["ch_return_temperature"] = opentherm.ch_return_temperature + system = getattr(self._data.wiserhub, "system", None) + opentherm = getattr(system, "opentherm", None) + operational_data = getattr(opentherm, "operational_data", None) + if ( + getattr(opentherm, "connection_status", None) == "Connected" + and operational_data is not None + ): + attrs["ch_flow_temperature"] = operational_data.ch_flow_temperature + attrs["ch_pressure_bar"] = operational_data.ch_pressure_bar + attrs["ch_return_temperature"] = ( + operational_data.ch_return_temperature + ) attrs["relative_modulation_level"] = _relative_modulation_level( - self._data.wiserhub.system.opentherm + opentherm ) - attrs["hw_temperature"] = opentherm.hw_temperature + attrs["hw_temperature"] = operational_data.hw_temperature else: hw = self._data.wiserhub.hotwater # If boosted show boost end time @@ -1061,6 +1202,14 @@ def __init__(self, data, device_id, sensor_type="") -> None: self._device = data.wiserhub.devices.smartplugs.get_by_id(device_id) self._last_delivered_power = 0 + @property + def unique_id(self): + """Return a stable ID across device and room renames.""" + entity_type = "power" if self._sensor_type == "Power" else "energy" + return get_physical_entity_unique_id( + self._data, "sensor", self._device_id, entity_type + ) + @callback def _handle_coordinator_update(self) -> None: """Fetch new state data for the sensor.""" @@ -1124,6 +1273,59 @@ def native_unit_of_measurement(self) -> str: return UnitOfEnergy.KILO_WATT_HOUR +class WiserRoomScheduledSetpointSensor(WiserSensor): + """Scheduled setpoint reported for a Wiser room without climate control.""" + + _attr_device_class = SensorDeviceClass.TEMPERATURE + _attr_state_class = SensorStateClass.MEASUREMENT + _attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS + _attr_translation_key = "current_schedule_temperature" + + def __init__(self, data, room_id: int) -> None: + """Initialize the scheduled setpoint sensor.""" + super().__init__( + data, + room_id, + "Current Schedule Temperature", + translation_key="current_schedule_temperature", + ) + self._update_state() + + def _update_state(self) -> None: + """Read the current ScheduledSetPoint value from the room.""" + room = self._data.wiserhub.rooms.get_by_id(self._device_id) + self._state = room.scheduled_target_temperature if room else None + + @property + def unique_id(self): + """Return a stable ID when the room is renamed.""" + return get_room_entity_unique_id( + self._data, self._device_id, "current_schedule_temperature" + ) + + @callback + def _handle_coordinator_update(self) -> None: + """Fetch the latest scheduled setpoint.""" + super()._handle_coordinator_update() + self._update_state() + self.async_write_ha_state() + + @property + def device_info(self): + """Attach the entity to its Wiser room device.""" + room = self._data.wiserhub.rooms.get_by_id(self._device_id) + return { + "name": get_device_name(self._data, self._device_id, "room"), + "identifiers": { + (DOMAIN, get_identifier(self._data, self._device_id, "room")) + }, + "manufacturer": MANUFACTURER, + "model": "Room", + "suggested_area": room.name, + **get_hub_via_device_info(self._data), + } + + class WiserLTSTempSensor(WiserSensor): """Sensor for long term stats for room temp and target temp""" @@ -1205,6 +1407,27 @@ def __init__( translation_key="target_temperature", ) + @property + def unique_id(self): + """Return a stable ID when a device or room is renamed.""" + if self._lts_sensor_type in { + "floor_current_temp", + "smokealarm_temp", + "smartvalve_temp", + "ufh_measured_temp", + }: + return get_physical_entity_unique_id( + self._data, + "sensor", + self._device_id, + self._lts_sensor_type, + ) + if self._lts_sensor_type in {"current_temp", "current_target_temp"}: + return get_room_entity_unique_id( + self._data, self._device_id, self._lts_sensor_type + ) + return super().unique_id + @callback def _handle_coordinator_update(self) -> None: """Fetch new state data for the sensor.""" @@ -1325,6 +1548,8 @@ def __init__(self, data, sensor_key) -> None: @property def available(self): + # Detection is sticky for registry continuity, but values are available + # only while the hub reports usable live OpenTherm data. opentherm = self._data.wiserhub.system.opentherm return ( super().available @@ -1393,8 +1618,10 @@ def icon(self): return "mdi:delta" if self._sensor_key == "boiler_exhaust_temperature": return "mdi:smoke" - if self._sensor_key in {"estimated_boiler_output", "maximum_capacity_kw"}: - return "mdi:flash" + if self._sensor_key == "estimated_boiler_output": + return "mdi:gas-burner" + if self._sensor_key == "maximum_capacity_kw": + return "mdi:gas-burner" if self._sensor_key == "coprocessor_version": return "mdi:chip" if self._sensor_key == "coprocessor_update_status": @@ -1415,7 +1642,7 @@ def icon(self): class WiserOpenThermFlameStatisticsSensor(WiserSensor): - """Rolling 24-hour runtime of the OpenTherm flame-active status.""" + """Today's runtime of the OpenTherm flame-active status, from local midnight.""" _attr_device_class = SensorDeviceClass.DURATION _attr_native_unit_of_measurement = UnitOfTime.HOURS @@ -1442,7 +1669,7 @@ def available(self): ) async def async_added_to_hass(self) -> None: - """Start the rolling history calculation after entities are registered.""" + """Start the daily history calculation after entities are registered.""" await super().async_added_to_hass() await self._async_setup_history() @@ -1482,9 +1709,9 @@ async def _async_setup_history(self, _now=None) -> None: self.hass, source_entity_id, [STATE_ON], - None, + Template("{{ today_at() }}", self.hass), Template("{{ now() }}", self.hass), - timedelta(hours=24), + None, timedelta(0), ) coordinator = HistoryStatsUpdateCoordinator( @@ -1536,6 +1763,7 @@ def __init__(self, data, device_id, sensor_type="") -> None: @property def available(self): + # Keep the entity during outages without exposing a stale temperature. opentherm = self._data.wiserhub.system.opentherm return ( super().available @@ -1620,6 +1848,13 @@ def __init__( use_device_class_name=True, ) + @property + def unique_id(self): + """Return a room-name-independent humidity sensor ID.""" + return get_physical_entity_unique_id( + self._data, "sensor", self._device_id, "humidity" + ) + @callback def _handle_coordinator_update(self) -> None: """Fetch new state data for the sensor.""" @@ -1714,6 +1949,15 @@ def __init__(self, data, device_id, sensor_type="") -> None: translation_key="heating_demand", ) + @property + def unique_id(self): + """Return a stable ID for room demand across room renames.""" + if self._lts_sensor_type == "room": + return get_room_entity_unique_id( + self._data, self._device_id, "heating_demand" + ) + return super().unique_id + @callback def _handle_coordinator_update(self) -> None: """Fetch new state data for the sensor.""" @@ -1847,6 +2091,18 @@ def __init__( ), ) + @property + def unique_id(self): + """Return a room-name-independent power or energy sensor ID.""" + entity_type = { + "Power": "power", + "Energy": "energy", + "EnergyReceived": "energy_received", + }[self._lts_sensor_type] + return get_physical_entity_unique_id( + self._data, "sensor", self._device_id, entity_type + ) + @callback def _handle_coordinator_update(self) -> None: """Fetch new state data for the sensor.""" @@ -2050,6 +2306,21 @@ def native_unit_of_measurement(self) -> str: class WiserEquipmentSensor(WiserSensor): """Definition of Wiser Equipment Sensor.""" + _attr_device_class = SensorDeviceClass.POWER + _attr_entity_category = EntityCategory.DIAGNOSTIC + _attr_state_class = SensorStateClass.MEASUREMENT + _unrecorded_attributes = frozenset( + { + "active_power", + "energy", + "energy_delivered", + "energy_received", + "rms_current", + "rms_voltage", + "total_active_power", + } + ) + def __init__(self, data, device_id=0, sensor_type="") -> None: """Initialise the device sensor.""" super().__init__( @@ -2059,6 +2330,7 @@ def __init__(self, data, device_id=0, sensor_type="") -> None: self._device = self._data.wiserhub.system else: self._device = self._data.wiserhub.devices.get_by_id(self._device_id) + self._state = self.native_value @callback def _handle_coordinator_update(self) -> None: @@ -2068,7 +2340,7 @@ def _handle_coordinator_update(self) -> None: self._device = self._data.wiserhub.system else: self._device = self._data.wiserhub.devices.get_by_id(self._device_id) - self._state = self._device.equipment.power.total_active_power + self._state = self.native_value self.async_write_ha_state() async def async_update(self) -> None: @@ -2081,9 +2353,31 @@ def icon(self): return "mdi:home-lightning-bolt" @property - def state(self) -> float: - """Return the state of the entity.""" - return self._device.equipment.power.total_active_power + def available(self) -> bool: + """Return whether equipment data is currently available.""" + return ( + super().available + and self._device is not None + and getattr(self._device, "equipment", None) is not None + ) + + @property + def native_value(self) -> float | None: + """Return the native power value.""" + equipment = getattr(self._device, "equipment", None) + if equipment is None: + return None + + power = equipment.power + for value in ( + getattr(power, "total_active_power", None), + getattr(power, "active_power", None), + getattr(self._device, "instantaneous_power", None), + ): + if value is not None: + return value + + return None @property def native_unit_of_measurement(self) -> str: @@ -2104,74 +2398,67 @@ def device_info(self): @property def extra_state_attributes(self): """Return device state attributes.""" - attrs = {} - - # common attributes - attrs["product_identifier"] = self._device.product_identifier - - attrs["name"] = self._device.equipment.equipment_name - attrs["device_type"] = self._device.equipment.device_type - attrs["family"] = self._device.equipment.equipment_family - attrs["installation_type"] = self._device.equipment.installation_type - - attrs["equipment_id"] = self._device.equipment.id - attrs["equipment_device_id"] = self._device.equipment.device_id - attrs["equipment_UUID"] = self._device.equipment.uuid - # more info 2024 06 - attrs["controllable"] = self._device.equipment.controllable - attrs["cloud_managed"] = self._device.equipment.cloud_managed - attrs["monitored"] = self._device.equipment.monitored - attrs["smart_compatible"] = self._device.equipment.smart_compatible - attrs["smart_supported"] = self._device.equipment.smart_supported - attrs["can_be_scheduled"] = self._device.equipment.can_be_scheduled - attrs["onoff_green_schedule_supported"] = self._device.equipment.onoff_green_schedule_supported - attrs["onoff_cost_schedule_supported"] = self._device.equipment.onoff_cost_schedule_supported - attrs["controllable"] = self._device.equipment.controllable - - attrs["functional_control_mode"] = self._device.equipment.functional_control_mode - attrs["current_control_mode"] = self._device.equipment.current_control_mode - #PCM - attrs["pcm_mode"] = self._device.equipment.pcm_mode - attrs["pcm_supported"] = self._device.equipment.pcm_supported - attrs["pcm_priority"] = self._device.equipment.pcm_priority - - attrs["number_of_phases"] = self._device.equipment.number_of_phases - attrs["direction"] = self._device.equipment.direction - attrs["operating_status"] = self._device.equipment.operating_status - attrs["fault_status"] = self._device.equipment.fault_status - #Load - attrs["load_state_status"] = self._device.equipment.load_state_status - attrs["load_state_command_optimized"] = self._device.equipment.load_state_command_optimized - attrs["load_shedding_status"] = self._device.equipment.load_shedding_status - attrs["load_state_command_prio"] = self._device.equipment.load_state_command_prio - attrs["load_setpoint_command_prio"] = self._device.equipment.load_setpoint_command_prio - - attrs["active_power"] = self._device.equipment.power.active_power - attrs["total_active_power"] = self._device.equipment.power.total_active_power - attrs["energy"] = round( - self._device.equipment.power.current_summation_delivered / 1000, - 2, - ) + equipment = getattr(self._device, "equipment", None) + if equipment is None: + return {} + + power = equipment.power + attrs = { + "product_identifier": self._device.product_identifier, + "equipment_name": equipment.equipment_name, + "device_type": equipment.device_type, + "family": equipment.equipment_family, + "installation_type": equipment.installation_type, + "equipment_id": equipment.id, + "equipment_device_id": equipment.device_id, + # Retain the existing key spelling for attribute compatibility. + "equipment_UUID": equipment.uuid, + "controllable": equipment.controllable, + "cloud_managed": equipment.cloud_managed, + "monitored": equipment.monitored, + "smart_compatible": equipment.smart_compatible, + "smart_supported": equipment.smart_supported, + "can_be_scheduled": equipment.can_be_scheduled, + "onoff_green_schedule_supported": ( + equipment.onoff_green_schedule_supported + ), + "onoff_cost_schedule_supported": equipment.onoff_cost_schedule_supported, + "functional_control_mode": equipment.functional_control_mode, + "current_control_mode": equipment.current_control_mode, + "pcm_mode": equipment.pcm_mode, + "pcm_supported": equipment.pcm_supported, + "pcm_priority": equipment.pcm_priority, + "number_of_phases": equipment.number_of_phases, + "direction": equipment.direction, + "operating_status": equipment.operating_status, + "fault_status": equipment.fault_status, + "load_state_status": equipment.load_state_status, + "load_state_command_optimized": equipment.load_state_command_optimized, + "load_shedding_status": equipment.load_shedding_status, + "load_state_command_prio": equipment.load_state_command_prio, + "load_setpoint_command_prio": equipment.load_setpoint_command_prio, + "active_power": power.active_power, + "total_active_power": power.total_active_power, + } + + delivered_energy = power.current_summation_delivered + if delivered_energy is not None: + attrs["energy"] = round(delivered_energy / 1000, 2) # PowerTagE attributes - if self._device.equipment.device_type in ["PTE","PowerTagE",]: + if equipment.device_type in ("PTE", "PowerTagE"): attrs["grid_limit"] = self._device.grid_limit + # Retain the existing key spelling for attribute compatibility. attrs["grid_limit_Uom"] = self._device.grid_limit_uom attrs["energy_export"] = self._device.energy_export attrs["self_consumption"] = self._device.self_consumption - - attrs["rms_current"] = self._device.equipment.power.rms_current - attrs["rms_voltage"] = self._device.equipment.power.rms_voltage - attrs["energy_received"] = self._device.equipment.power.current_summation_received - # SmartPlug attributes - if self._device.equipment.device_type in ["SmartPlug"]: - attrs["functional_control_mode"] = self._device.equipment.functional_control_mode + attrs["rms_current"] = power.rms_current + attrs["rms_voltage"] = power.rms_voltage + attrs["energy_received"] = power.current_summation_received # PowerTagE and SmartPlug attributes - if self._device.equipment.device_type in ["PTE","PowerTagE","SmartPlug"]: - attrs["energy_delivered"] = self._device.equipment.power.current_summation_delivered - attrs["pcm_mode"] = self._device.equipment.pcm_mode + if equipment.device_type in ("PTE", "PowerTagE", "SmartPlug"): + attrs["energy_delivered"] = power.current_summation_delivered - - return attrs + return attrs diff --git a/custom_components/wiser/services.py b/custom_components/wiser/services.py index 990aad9b..0ffb7e3e 100644 --- a/custom_components/wiser/services.py +++ b/custom_components/wiser/services.py @@ -449,10 +449,25 @@ async def verify_on_next_update(): remove_listener = instance.async_add_listener(update_received.set) retry_sent = False retry_error = None + notification_id = ( + f"wiser_opentherm_" + f"{getattr(instance.wiserhub.system, 'name', 'hub')}_{param}" + ) deadline = ( asyncio.get_running_loop().time() + OPENTHERM_CONFIRMATION_WINDOW ) + async def dismiss_previous_failure(): + """Remove a stale warning after a later confirmed command.""" + await hass.services.async_call( + "persistent_notification", + "dismiss", + { + "notification_id": notification_id + }, + blocking=False, + ) + async def report_failure_if_still_mismatched(): """Report only a mismatch that survives the full window.""" if ( @@ -531,9 +546,7 @@ async def report_failure_if_still_mismatched(): { "title": title, "message": message, - "notification_id": ( - f"wiser_opentherm_{instance.wiserhub.system.name}_{param}" - ), + "notification_id": notification_id, }, blocking=False, ) @@ -573,6 +586,7 @@ async def report_failure_if_still_mismatched(): return if matches: if retry_sent or timed_out: + await dismiss_previous_failure() return continue diff --git a/custom_components/wiser/strings.json b/custom_components/wiser/strings.json index 02e4f87c..0cce5abc 100755 --- a/custom_components/wiser/strings.json +++ b/custom_components/wiser/strings.json @@ -4,7 +4,7 @@ "step": { "user": { "title": "Wiser Heat Hub Setup", - "description": "Please enter the ip address and secret key obtained from the hub.", + "description": "Please enter the IP address and secret key obtained from the hub.", "data": { "host": "IP Address or Hostname", "port": "Port number", @@ -27,10 +27,11 @@ "not_wiser_hub": "The discovered device is not a Wiser Heat Hub." }, "error": { - "auth_failure": "Unable to authenticate with the Wiser Hub. Check the secret key and try again.", - "timeout_error": "Timed out trying to connect to the Wiser Hub. Check the IP address and try again.", - "timeout_error_discovery": "Timed out trying to connect to the Wiser Hub. Check your hub is still online and try again.", - "unknown": "Unknown error connecting to the Wiser hub. Please check the logs." + "invalid_hot_water_presets": "One or more hot-water presets are invalid.", + "auth_failure": "Unable to authenticate with the Wiser hub. Check the secret key and try again.", + "timeout_error": "Timed out trying to connect to the Wiser hub. Check the IP address and try again.", + "timeout_error_discovery": "Timed out trying to connect to the Wiser hub. Check that your hub is still online and try again.", + "unknown": "Unknown error connecting to the Wiser hub. Please check the logs." } }, "options": { @@ -41,7 +42,19 @@ "menu_options": { "main_params": "Main Parameters", "automation_params": "Automation Parameters", - "opentherm_sensors": "OpenTherm sensors" + "opentherm": "OpenTherm", + "equipment_sensors": "Equipment sensors", + "ui_options": "UI options" + } + }, + "ui_options": { + "title": "UI options", + "data": { + "legacy_naming": "Legacy naming", + "show_wiser_sidebar": "Show Wiser in sidebar" + }, + "data_description": { + "legacy_naming": "Include room names in Wiser device names. If enabled, using Home Assistant to regenerate entity IDs with the default format in Settings → System → Entity ID format (Home Assistant 2026.8 and later) will duplicate area names when the area and Wiser room names match. Changing this option does not rename existing entity IDs." } }, "main_params": { @@ -55,12 +68,12 @@ "heating_boost_time": "Default Heating Boost Duration (mins)", "hotwater_boost_time": "Default Hot Water Boost Duration (mins)", "setpoint_mode": "Setpoint Mode", - "restore_manual_temp_option": "Use This Temperature If No Stored Manual Temperature" + "restore_manual_temp_option": "Use This Temperature If No Manual Temperature Is Stored" } }, "automation_params": { "title": "Wiser Integration Options", - "description": "Enable in-built automations", + "description": "Enable built-in automations", "sections": { "automations_passive_mode": { "name": "Passive Mode", @@ -81,9 +94,37 @@ "hotwater_boost_mode": "Hot Water Boost Mode", "hotwater_sensor_entity_id": "Hot Water Temperature Sensor" } + }, + "additional_sensors": { + "name": "Additional sensors", + "description": "Expose optional sensors for data reported by Wiser.", + "data": { + "schedule_setpoint_rooms": "Rooms without climate control" + }, + "data_description": { + "schedule_setpoint_rooms": "Select the Wiser rooms where a current schedule temperature sensor should be created." + } } } }, + "equipment_sensors": { + "title": "Equipment sensors", + "description": "Configure detailed equipment data entities for compatible devices.", + "data": { + "equipment_sensors": "Enable equipment data sensors" + }, + "data_description": { + "equipment_sensors": "Expose an additional sensor containing detailed equipment attributes for each compatible smart plug, PowerTag, and heating actuator." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Configure OpenTherm features.", + "menu_options": { + "opentherm_hot_water_control": "Hot water control", + "opentherm_sensors": "Sensors" + } + }, "opentherm_sensors": { "title": "OpenTherm sensors", "description": "Choose a category of detected OpenTherm attributes.", @@ -94,6 +135,20 @@ "opentherm_status": "Status and diagnostics" } }, + "opentherm_hot_water_control": { + "title": "OpenTherm hot-water control", + "description": "Optionally control the boiler domestic-hot-water setpoint through Wiser. The integration creates an OpenTherm Hot Water schedule in the Wiser Schedules panel. Off uses the minimum hot-water temperature reported by OpenTherm.", + "data": { + "opentherm_hot_water_control": "Enable hot-water control", + "opentherm_hot_water_manual_temperature": "Manual temperature", + "opentherm_hot_water_sensor_entity_id": "Optional external temperature sensor.", + "opentherm_hot_water_presets": "Presets" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperature used by Manual mode.", + "opentherm_hot_water_sensor_entity_id": "Optional temperature sensor used for the current water temperature. OpenTherm is used when the sensor is unavailable." + } + }, "opentherm_readings": { "title": "OpenTherm readings", "description": "Choose which detected OpenTherm readings are exposed as sensor entities.", @@ -165,7 +220,8 @@ } }, "abort": { - "opentherm_not_available": "OpenTherm data is not available for this Wiser hub." + "opentherm_not_available": "OpenTherm data is not available for this Wiser hub.", + "equipment_not_available": "Equipment data is not available for this Wiser hub." } }, "services": { @@ -220,6 +276,11 @@ "name": "Floor temperature" } }, + "water_heater": { + "opentherm_hot_water": { + "name": "OpenTherm hot water" + } + }, "number": { "away_mode_target_temperature": { "name": "Away mode target temperature" }, "floor_temperature_offset": { "name": "Floor temperature offset" }, @@ -252,6 +313,7 @@ "device_lock": { "name": "Device lock" }, "identify": { "name": "Identify" }, "outlet": { "name": "Outlet" }, + "ev_charger": { "name": "EV charger" }, "away_mode_turns_off": { "name": "Away mode turns off" }, "away_mode_closes": { "name": "Away mode closes" }, "passive_mode": { "name": "Passive mode" }, @@ -274,6 +336,9 @@ "fault_warning": { "name": "Fault warning" }, "remote_alarm": { "name": "Remote alarm" }, "battery_defect": { "name": "Battery defect" }, + "ac_mains": { "name": "AC mains" }, + "test_mode": { "name": "Test mode" }, + "hush_mode": { "name": "Hush mode" }, "controllable": { "name": "Controllable" }, "pcm_mode": { "name": "PCM mode" }, "window_detection_active": { "name": "Window detection active" }, @@ -302,13 +367,33 @@ }, "sensor": { "signal": { "name": "Signal" }, + "report_count": { "name": "Report count" }, "cloud": { "name": "Cloud" }, "heating_operation_mode": { "name": "Heating operation mode" }, - "heating": { "name": "Heating" }, - "heating_channel": { "name": "Heating channel {channel}" }, - "hot_water": { "name": "Hot water" }, + "heating": { + "name": "Heating", + "state": { + "off": "[%key:common::state::off%]", + "on": "[%key:common::state::on%]" + } + }, + "heating_channel": { + "name": "Heating channel {channel}", + "state": { + "off": "[%key:common::state::off%]", + "on": "[%key:common::state::on%]" + } + }, + "hot_water": { + "name": "Hot water", + "state": { + "off": "[%key:common::state::off%]", + "on": "[%key:common::state::on%]" + } + }, "hot_water_operation_mode": { "name": "Hot water operation mode" }, "target_temperature": { "name": "Target temperature" }, + "current_schedule_temperature": { "name": "Current schedule temperature" }, "floor_temperature": { "name": "Floor temperature" }, "boiler_flow_temperature": { "name": "Boiler flow temperature" }, "boiler_return_temperature": { "name": "Boiler return temperature" }, @@ -347,7 +432,7 @@ "total_energy": { "name": "Total energy" }, "energy_delivered": { "name": "Energy delivered" }, "energy_received": { "name": "Energy received" }, - "equipment_power": { "name": "Equipment power" } + "equipment_power": { "name": "Equipment" } } }, "device_automation": { diff --git a/custom_components/wiser/switch.py b/custom_components/wiser/switch.py index d6d2e2f2..f5b1634e 100755 --- a/custom_components/wiser/switch.py +++ b/custom_components/wiser/switch.py @@ -16,18 +16,23 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.update_coordinator import CoordinatorEntity -from .const import DATA, DOMAIN, ENTITY_PREFIX, HOT_WATER, MANUFACTURER +from .const import ( + DATA, + DOMAIN, + HOT_WATER, + MANUFACTURER, + MANUFACTURER_SCHNEIDER, +) from .entity import WiserEntityMixin from .helpers import ( get_device_name, get_hub_device_info, get_hub_via_device_info, get_identifier, - get_legacy_device_name, - get_legacy_unique_id, - get_room_name, + get_light_entity_name, + get_physical_entity_unique_id, + get_room_entity_unique_id, get_unique_id, - get_uuid_unique_id, hub_error_handler, ) from custom_components.wiser.schedules import WiserScheduleEntity @@ -114,6 +119,17 @@ ] +def _room_supports_passive_mode(room) -> bool: + """Return whether a room has a heating device passive mode can control.""" + # Radiator rooms expose SmartValveIds, while UFH rooms expose an + # UnderFloorHeatingId instead. Both are controlled through the same room + # target-temperature API used by passive mode. + return ( + room.number_of_smartvalves > 0 + or room.underfloor_heating_id is not None + ) + + async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entities): """Add the Wiser System Switch entities.""" data = hass.data[DOMAIN][config_entry.entry_id][DATA] # Get Handler @@ -198,6 +214,13 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie ] ) + # EV chargers use the hub's Equipment control states rather than the + # SmartPlug RequestOutput command. + evse_collection = getattr(data.wiserhub.devices, "evse", None) + if evse_collection: + for evse in evse_collection.all: + wiser_switches.append(WiserEVSESwitch(data, evse.id)) + # Add threshold sensor interacts with room climate switch for device in data.wiserhub.devices.all: if hasattr(device, "interacts_with_room_climate"): @@ -229,7 +252,7 @@ async def async_setup_entry(hass: HomeAssistant, config_entry, async_add_entitie # Add Room passive mode switches if data.enable_automations_passive_mode: for room in data.wiserhub.rooms.all: - if room.number_of_smartvalves > 0: + if _room_supports_passive_mode(room): wiser_switches.append( WiserPassiveModeSwitch( hass, data, room.id, f"Wiser Passive Mode {room.name}" @@ -300,11 +323,13 @@ def icon(self): @property def unique_id(self): if self._type == "room": - legacy_name = ( - f"{get_room_name(self._data, self._room_id)} {self._name}" + entity_key = self._attr_translation_key or self._key + return get_room_entity_unique_id( + self._data, + self._room_id, + f"switch_{entity_key}", ) - else: - legacy_name = get_device_name(self._data, 0, self._name) + legacy_name = get_device_name(self._data, 0, self._name) return get_unique_id(self._data, self._type, "switch", legacy_name) @property @@ -508,6 +533,48 @@ def __init__(self, data, plugId, name) -> None: self._device = self._data.wiserhub.devices.get_by_id(self._device_id) self._schedule = self._device.schedule self._is_on = self._device.is_on + self._optimistic_is_on = None + self._optimistic_gen = 0 + self._confirmation_task = None + + @hub_error_handler + async def _async_confirm_command(self, gen: int) -> None: + """Refresh until the hub reports the accepted smart-plug command.""" + try: + for _attempt in range(2): + if gen != self._optimistic_gen: + return + await self.async_force_update(2) + if gen != self._optimistic_gen or self._optimistic_is_on is None: + return + finally: + if gen == self._optimistic_gen: + self._clear_optimistic_state() + + @callback + def _schedule_command_confirmation(self, gen: int) -> None: + """Confirm a command without delaying the Home Assistant service call.""" + if self._confirmation_task is not None: + self._confirmation_task.cancel() + self._confirmation_task = self.hass.async_create_task( + self._async_confirm_command(gen), + f"Wiser smart plug command confirmation: {self.entity_id}", + ) + + async def async_will_remove_from_hass(self) -> None: + """Cancel delayed confirmation when Home Assistant removes the entity.""" + self._optimistic_gen += 1 + if self._confirmation_task is not None: + self._confirmation_task.cancel() + self._confirmation_task = None + await super().async_will_remove_from_hass() + + @callback + def _clear_optimistic_state(self) -> None: + """Return the entity to the latest state reported by the hub.""" + self._optimistic_is_on = None + self._is_on = self._device.is_on + self.async_write_ha_state() @callback def _handle_coordinator_update(self) -> None: @@ -515,15 +582,19 @@ def _handle_coordinator_update(self) -> None: super()._handle_coordinator_update() self._device = self._data.wiserhub.devices.get_by_id(self._device_id) self._schedule = self._device.schedule - self._is_on = self._device.is_on + if ( + self._optimistic_is_on is None + or self._device.is_on == self._optimistic_is_on + ): + self._is_on = self._device.is_on + self._optimistic_is_on = None self.async_write_ha_state() @property def unique_id(self): - """Return unique Id.""" - legacy_name = f"{get_device_name(self._data, self._device_id)} Switch" - return get_unique_id( - self._data, self._device.product_type, legacy_name, self._device_id + """Return a name-independent unique ID.""" + return get_physical_entity_unique_id( + self._data, "switch", self._device_id, "outlet" ) @property @@ -546,7 +617,7 @@ def extra_state_attributes(self): attrs["manual_state"] = self._device.manual_state attrs["mode"] = self._device.mode attrs["name"] = self._device.name - attrs["output_state"] = "On" if self._device.is_on else "Off" + attrs["output_state"] = "On" if self.is_on else "Off" # Switches could be not allocated to room (issue:209) if self._data.wiserhub.rooms.get_by_id(self._device.room_id) is not None: attrs["room"] = self._data.wiserhub.rooms.get_by_id( @@ -568,15 +639,96 @@ def extra_state_attributes(self): @hub_error_handler async def async_turn_on(self, **kwargs): """Turn the device on.""" - await self._device.turn_on() - await self.async_force_update(2) + self._optimistic_gen += 1 + gen = self._optimistic_gen + self._optimistic_is_on = True + self._is_on = True + self.async_write_ha_state() + + try: + await self._device.turn_on() + except Exception: + if gen == self._optimistic_gen: + self._clear_optimistic_state() + raise + + self._schedule_command_confirmation(gen) return True @hub_error_handler async def async_turn_off(self, **kwargs): """Turn the device off.""" + self._optimistic_gen += 1 + gen = self._optimistic_gen + self._optimistic_is_on = False + self._is_on = False + self.async_write_ha_state() + + try: + await self._device.turn_off() + except Exception: + if gen == self._optimistic_gen: + self._clear_optimistic_state() + raise + + self._schedule_command_confirmation(gen) + return True + + +class WiserEVSESwitch(WiserSwitch): + """Switch that allows or force-disables EV charging.""" + + def __init__(self, data, device_id) -> None: + self._device_id = device_id + super().__init__(data, "EV Charger", "", "evse", "mdi:ev-station") + self._attr_translation_key = "ev_charger" + self._device = self._data.wiserhub.devices.evse.get_by_id(device_id) + self._is_on = self._device.is_on + + @callback + def _handle_coordinator_update(self) -> None: + """Refresh state from the latest equipment record.""" + super()._handle_coordinator_update() + self._device = self._data.wiserhub.devices.evse.get_by_id(self._device_id) + self._is_on = self._device.is_on + self.async_write_ha_state() + + @property + def unique_id(self): + """Return a stable physical-device entity ID.""" + return get_physical_entity_unique_id( + self._data, "switch", self._device_id, "ev_charger" + ) + + @property + def device_info(self): + """Return EV charger device registry information.""" + return { + "name": get_device_name(self._data, self._device_id), + "identifiers": {(DOMAIN, get_identifier(self._data, self._device_id))}, + "manufacturer": MANUFACTURER_SCHNEIDER, + "model": self._device.model, + "sw_version": self._device.firmware_version, + **get_hub_via_device_info(self._data), + } + + @property + def extra_state_attributes(self): + """Return the hub's current EVSE control state.""" + return {"control_state": self._device.control_state} + + @hub_error_handler + async def async_turn_on(self, **kwargs): + """Return charging control to the Wiser system.""" + await self._device.turn_on() + await self.async_force_update() + return True + + @hub_error_handler + async def async_turn_off(self, **kwargs): + """Force charging consumption off.""" await self._device.turn_off() - await self.async_force_update(2) + await self.async_force_update() return True @@ -602,15 +754,12 @@ def _handle_coordinator_update(self) -> None: @property def unique_id(self): - """Return unique Id.""" - legacy_name = ( - f"{get_device_name(self._data, self._smart_plug_id)} Away Mode Turns Off" - ) - return get_unique_id( + """Return a name-independent unique ID.""" + return get_physical_entity_unique_id( self._data, - self._smartplug.product_type, - legacy_name, + "switch", self._smart_plug_id, + "away_mode_turns_off", ) @property @@ -668,18 +817,18 @@ def _handle_coordinator_update(self) -> None: @property def name(self): """Return the name of the Device.""" - # Per-channel light name so the two channels of a multi-gang dimmer do - # not collide. - return f"{ENTITY_PREFIX} {self._light.name} Away Mode Turns Off" + return get_light_entity_name( + self._data, self._light, "Away Mode Turns Off" + ) @property def unique_id(self): - """Return unique Id.""" - legacy_name = ( - f"{get_device_name(self._data, self._light_id)} Away Mode Turns Off" - ) - return get_unique_id( - self._data, self._light.product_type, legacy_name, self._light_id + """Return a name-independent per-channel unique ID.""" + return get_physical_entity_unique_id( + self._data, + "switch", + self._light_id, + "light_away_mode_turns_off", ) @property @@ -731,12 +880,9 @@ def _handle_coordinator_update(self) -> None: @property def unique_id(self): - """Return unique Id.""" - legacy_name = ( - f"{get_device_name(self._data, self._shutter_id)} Away Mode Closes" - ) - return get_unique_id( - self._data, self._shutter.product_type, legacy_name, self._shutter_id + """Return a name-independent unique ID.""" + return get_physical_entity_unique_id( + self._data, "switch", self._shutter_id, "away_mode_closes" ) @property @@ -792,11 +938,10 @@ def _handle_coordinator_update(self) -> None: @property def unique_id(self): """Return unique Id.""" - legacy_name = ( - f"{get_room_name(self._data, self._room_id)} Passive Mode" - ) - return get_unique_id( - self._data, "passive-mode-switch", legacy_name, self._room_id + return get_room_entity_unique_id( + self._data, + self._room_id, + "switch_passive_mode", ) @property @@ -858,12 +1003,12 @@ def _handle_coordinator_update(self) -> None: @property def unique_id(self): - """Return unique Id.""" - legacy_name = ( - f"{get_device_name(self._data, self._shutter_id)} Respect Summer Comfort" - ) - return get_unique_id( - self._data, self._shutter.product_type, legacy_name, self._shutter_id + """Return a name-independent unique ID.""" + return get_physical_entity_unique_id( + self._data, + "switch", + self._shutter_id, + "respect_summer_comfort", ) @property @@ -944,19 +1089,16 @@ def _handle_coordinator_update(self) -> None: @property def unique_id(self): - """Return unique Id.""" - suffix = "Interacts With Room Climate" + """Return a name-independent unique ID.""" + entity_type = "interacts_with_room_climate" if self._ancillary_sensor_type: - suffix = f"{self._ancillary_sensor_type} {suffix}" - legacy_name = ( - f"{get_legacy_device_name(self._data, self._device_id)} {suffix}" - ) - legacy_unique_id = get_legacy_unique_id( - self._data, self._device.product_type, legacy_name, self._device_id + entity_type = ( + f"{self._ancillary_sensor_type.lower()}_" + f"interacts_with_room_climate_{self._ancillary_sensor_id}" + ) + return get_physical_entity_unique_id( + self._data, "switch", self._device_id, entity_type ) - if self._ancillary_sensor_id: - legacy_unique_id = f"{legacy_unique_id}_{self._ancillary_sensor_id}" - return get_uuid_unique_id(legacy_unique_id) @property def is_on(self) -> bool: @@ -1104,7 +1246,7 @@ def device_info(self): }, "manufacturer": MANUFACTURER, "model": HOT_WATER.title(), - "via_device": (DOMAIN, self._data.wiserhub.system.name), + **get_hub_via_device_info(self._data), } @hub_error_handler @@ -1118,4 +1260,3 @@ async def async_turn_off(self, **kwargs): """Turn hot water off.""" await self._data.wiserhub.hotwater.override_state("Off") await self.async_force_update() - diff --git a/custom_components/wiser/translations/da.json b/custom_components/wiser/translations/da.json new file mode 100644 index 00000000..99584bce --- /dev/null +++ b/custom_components/wiser/translations/da.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Opsætning af Wiser Heat Hub", + "description": "Indtast IP-adressen og den hemmelige nøgle, der er hentet fra hubben.", + "data": { + "host": "IP-adresse eller værtsnavn", + "port": "Portnummer", + "password": "Hemmelig nøgle" + } + }, + "zeroconf_confirm": { + "title": "Opsætning af Wiser Heat Hub ({name})", + "description": "Indtast den hemmelige nøgle, der er hentet fra hubben.", + "data": { + "host": "IP-adresse eller værtsnavn", + "port": "Portnummer", + "password": "Hemmelig nøgle" + } + } + }, + "abort": { + "already_in_progress": "Konfigurationen af Wiser Heat Hub er allerede i gang.", + "already_configured": "Denne enhed er allerede konfigureret.", + "not_wiser_hub": "Den fundne enhed er ikke en Wiser Heat Hub." + }, + "error": { + "invalid_hot_water_presets": "En eller flere varmtvandsforudindstillinger er ugyldige.", + "auth_failure": "Godkendelse med Wiser-hubben mislykkedes. Kontrollér den hemmelige nøgle, og prøv igen.", + "timeout_error": "Forbindelsen til Wiser-hubben fik timeout. Kontrollér IP-adressen, og prøv igen.", + "timeout_error_discovery": "Forbindelsen til Wiser-hubben fik timeout. Kontrollér, at hubben stadig er online, og prøv igen.", + "unknown": "Ukendt fejl ved forbindelse til Wiser-hubben. Kontrollér logfilerne." + } + }, + "options": { + "step": { + "init": { + "title": "Indstillinger for Wiser-integrationen", + "description": "Vælg parametre, der skal ændres", + "menu_options": { + "main_params": "Hovedparametre", + "automation_params": "Automatiseringsparametre", + "opentherm": "OpenTherm", + "equipment_sensors": "Udstyrssensorer", + "ui_options": "Brugerfladeindstillinger" + } + }, + "ui_options": { + "title": "Brugerfladeindstillinger", + "data": { + "legacy_naming": "Ældre navngivning", + "show_wiser_sidebar": "Vis Wiser i sidepanelet" + }, + "data_description": { + "legacy_naming": "Medtag rumnavne i Wiser-enhedsnavne. Hvis dette er aktiveret, vil regenerering af entitets-id'er i Home Assistant med standardformatet under Indstillinger → System → Entitets-id-format (Home Assistant 2026.8 og nyere) duplikere områdenavne, når området og Wiser-rummet har samme navn. Ændring af denne indstilling omdøber ikke eksisterende entitets-id'er." + } + }, + "main_params": { + "title": "Indstillinger for Wiser-integrationen", + "description": "Hovedparametre", + "data": { + "host": "IP-adresse eller værtsnavn", + "port": "Portnummer", + "scan_interval": "Scanningsinterval (sek.)", + "heating_boost_temp": "Standardtemperatur for varmeboost (°C)", + "heating_boost_time": "Standardvarighed for varmeboost (min.)", + "hotwater_boost_time": "Standardvarighed for varmtvandsboost (min.)", + "setpoint_mode": "Indstillingspunkttilstand", + "restore_manual_temp_option": "Brug denne temperatur, hvis der ikke er gemt en manuel temperatur" + } + }, + "automation_params": { + "title": "Indstillinger for Wiser-integrationen", + "description": "Aktivér indbyggede automatiseringer", + "sections": { + "automations_passive_mode": { + "name": "Passiv tilstand", + "description": "Aktivér passiv TRV-funktion. Hvert rum skal aktiveres, før funktionen virker.", + "data": { + "automations_passive_mode": "Passiv tilstand", + "passive_mode_temperature_increments": "Temperaturtrin for passiv tilstand" + } + }, + "hotwater_climate": { + "name": "Klimastyring af varmt vand", + "description": "Aktivér klimastyring af varmt vand. Se wikien for detaljer.", + "data": { + "hotwater_climate": "Aktivér klimaentiteten", + "experimental_mode": "Aktivér eksperimentel driftstilstand", + "hotwater_auto_mode": "Automatisk varmtvandstilstand", + "hotwater_heat_mode": "Varmtvandsopvarmningstilstand", + "hotwater_boost_mode": "Varmtvandsboosttilstand", + "hotwater_sensor_entity_id": "Varmtvandstemperatursensor" + } + }, + "additional_sensors": { + "name": "Yderligere sensorer", + "description": "Vis valgfrie sensorer for data rapporteret af Wiser.", + "data": { + "schedule_setpoint_rooms": "Rum uden klimastyring" + }, + "data_description": { + "schedule_setpoint_rooms": "Vælg de Wiser-rum, hvor der skal oprettes en sensor for den aktuelle skematemperatur." + } + } + } + }, + "equipment_sensors": { + "title": "Udstyrssensorer", + "description": "Konfigurer entiteter med detaljerede udstyrsdata for kompatible enheder.", + "data": { + "equipment_sensors": "Aktivér udstyrsdatasensorer" + }, + "data_description": { + "equipment_sensors": "Vis en ekstra sensor med detaljerede udstyrsattributter for hvert kompatibelt smartstik, PowerTag og varmeaktuator." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Konfigurer OpenTherm-funktioner.", + "menu_options": { + "opentherm_hot_water_control": "Styring af varmt vand", + "opentherm_sensors": "Sensorer" + } + }, + "opentherm_sensors": { + "title": "OpenTherm-sensorer", + "description": "Vælg en kategori af registrerede OpenTherm-attributter.", + "menu_options": { + "opentherm_readings": "Målinger", + "opentherm_central_heating": "Centralvarme", + "opentherm_hot_water": "Varmt vand", + "opentherm_status": "Status og diagnosticering" + } + }, + "opentherm_hot_water_control": { + "title": "OpenTherm-styring af varmt vand", + "description": "Styr eventuelt kedlens setpunkt for brugsvand via Wiser. Integrationen opretter et OpenTherm Hot Water-skema i Wiser Schedules-panelet. Fra bruger den laveste varmtvandstemperatur, som OpenTherm rapporterer.", + "data": { + "opentherm_hot_water_control": "Aktivér styring af varmt vand", + "opentherm_hot_water_manual_temperature": "Manuel temperatur", + "opentherm_hot_water_sensor_entity_id": "Valgfri ekstern temperatursensor.", + "opentherm_hot_water_presets": "Forudindstillinger" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperatur, der bruges i manuel tilstand.", + "opentherm_hot_water_sensor_entity_id": "Valgfri temperatursensor, der bruges til den aktuelle vandtemperatur. OpenTherm bruges, når sensoren ikke er tilgængelig." + } + }, + "opentherm_readings": { + "title": "OpenTherm-målinger", + "description": "Vælg, hvilke registrerede OpenTherm-målinger der vises som sensorentiteter.", + "data": { + "ch_flow_temperature": "Kedlens fremløbstemperatur", + "ch_pressure_bar": "Centralvarmetryk", + "ch_return_temperature": "Kedlens returtemperatur", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Kedlens røggastemperatur", + "estimated_boiler_output": "Anslået kedelydelse", + "hw_flow_rate": "Varmtvandsflow", + "hw_temperature": "Varmtvandstemperatur", + "maximum_capacity_kw": "Maksimal kedelkapacitet", + "minimum_modulation_level": "Mindste modulationsniveau", + "relative_modulation_level": "Relativt modulationsniveau", + "room_temperature": "Rumtemperatur" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm-centralvarme", + "description": "Vælg, hvilke registrerede centralvarmeattributter der vises som entiteter.", + "data": { + "boiler_ch_max_setpoint_read_write": "Læsning/skrivning af kedlens maksimale centralvarmeindstillingspunkt", + "boiler_ch_max_setpoint_transfer_enable": "Overførsel af kedlens maksimale centralvarmeindstillingspunkt aktiveret", + "boiler_ch_setpoint": "Kedlens centralvarmeindstillingspunkt", + "boiler_ch_setpoint_lower_bound": "Nedre grænse for kedlens centralvarmeindstillingspunkt", + "boiler_ch_setpoint_upper_bound": "Øvre grænse for kedlens centralvarmeindstillingspunkt", + "ch_flow_active_lower_setpoint": "Aktivt nedre centralvarmefremløbsindstillingspunkt", + "ch_flow_active_upper_setpoint": "Aktivt øvre centralvarmefremløbsindstillingspunkt", + "ch1_flow_enabled": "Centralvarmekreds 1 fremløb aktiveret", + "ch1_flow_setpoint": "Centralvarmekreds 1 fremløbsindstillingspunkt", + "ch2_flow_enabled": "Centralvarmekreds 2 fremløb aktiveret", + "ch2_flow_setpoint": "Centralvarmekreds 2 fremløbsindstillingspunkt", + "room_setpoint": "Rumindstillingspunkt" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm-varmt vand", + "description": "Vælg, hvilke registrerede varmtvandsattributter der vises som entiteter.", + "data": { + "boiler_hw_setpoint": "Kedlens varmtvandsindstillingspunkt", + "boiler_hw_setpoint_lower_bound": "Nedre grænse for kedlens varmtvandsindstillingspunkt", + "boiler_hw_setpoint_read_write": "Læsning/skrivning af kedlens varmtvandsindstillingspunkt", + "boiler_hw_setpoint_transfer_enable": "Overførsel af kedlens varmtvandsindstillingspunkt aktiveret", + "boiler_hw_setpoint_upper_bound": "Øvre grænse for kedlens varmtvandsindstillingspunkt", + "hw_enabled": "Varmt vand aktiveret", + "hw_flow_setpoint": "Varmtvandsfremløbets indstillingspunkt" + } + }, + "opentherm_status": { + "title": "OpenTherm-status og diagnosticering", + "description": "Vælg, hvilke registrerede status- og diagnosticeringsattributter der vises som sensorentiteter.", + "data": { + "boiler_fault": "Kedelfejl", + "central_heating_active": "Centralvarme aktiv", + "central_heating_2_active": "Centralvarme 2 aktiv", + "connection_status": "Forbindelsesstatus", + "coprocessor_update_status": "Opdateringsstatus for OpenTherm-coprocessor", + "coprocessor_version": "Version af OpenTherm-coprocessor", + "cooling_active": "Køling aktiv", + "diagnostic_event": "Diagnosticeringshændelse", + "flame_active": "Flamme aktiv", + "flame_statistics": "Flammestatistik", + "hot_water_active": "Varmt vand aktivt", + "operating_mode": "Driftstilstand", + "slave_status": "OpenTherm-slavestatus", + "tracked_room_id": "Id for overvåget rum" + } + } + }, + "abort": { + "opentherm_not_available": "OpenTherm-data er ikke tilgængelige for denne Wiser-hub.", + "equipment_not_available": "Udstyrsdata er ikke tilgængelige for denne Wiser-hub." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "Indstil OpenTherm-parameter", + "description": "Ændrer en understøttet OpenTherm-temperaturparameter på Wiser-hubben.", + "fields": { + "parameter": { + "name": "Parameter", + "description": "OpenTherm-temperaturparameteren, der skal ændres. Det nødvendige API-slutpunkt vælges automatisk." + }, + "temperature": { + "name": "Temperatur", + "description": "Brug en temperatur inden for de grænser, som kedlen rapporterer." + }, + "hub": { + "name": "Wiser-hub", + "description": "Vælg kun en hub, når du har flere Wiser-hubber." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Aktivt nedre centralvarmeindstillingspunkt", + "chFlowActiveUpperSetpoint": "Aktivt øvre centralvarmeindstillingspunkt", + "dhwFlowSetpoint": "Hubbens varmtvandsfremløbsindstillingspunkt", + "dhwSetpoint": "Kedlens varmtvandsindstillingspunkt", + "maxChSetpoint": "Kedlens maksimale centralvarmeindstillingspunkt" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Angiv en OpenTherm-temperatur." + }, + "opentherm_hub_not_found": { + "message": "Den angivne Wiser-hub blev ikke fundet." + }, + "opentherm_hub_not_loaded": { + "message": "Den angivne Wiser-hub er ikke indlæst." + }, + "opentherm_hub_required": { + "message": "Vælg en Wiser-hub, når mere end én hub er konfigureret." + }, + "opentherm_not_supported": { + "message": "Denne hub understøtter ikke OpenTherm." + }, + "opentherm_invalid_parameter": { + "message": "OpenTherm-parameteren eller værdien er ugyldig: {error}" + }, + "opentherm_write_failed": { + "message": "OpenTherm-parameteren {parameter} kunne ikke indstilles: {error}" + }, + "opentherm_confirmation_failed": { + "message": "OpenTherm-parameteren {parameter} forblev ikke på {requested} °C efter ét nyt forsøg og bekræftelsesperioden på {window} sekunder. Wiser rapporterer {reported} °C. Der sendes ikke flere forsøg." + }, + "opentherm_retry_failed": { + "message": "OpenTherm-parameteren {parameter} forblev ikke på {requested} °C, og et nyt forsøg kunne ikke sendes: {error}. Wiser rapporterer {reported} °C. Der sendes ikke flere forsøg." + }, + "opentherm_notification_title": { + "message": "Wiser OpenTherm-kommando" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Opvarmning" + }, + "floor_temperature": { + "name": "Gulvtemperatur" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Måltemperatur for fraværstilstand" + }, + "floor_temperature_offset": { + "name": "Forskydning af gulvtemperatur" + }, + "indoor_discomfort_temperature": { + "name": "Indendørs ubehagstemperatur" + }, + "outdoor_discomfort_temperature": { + "name": "Udendørs ubehagstemperatur" + } + }, + "select": { + "mode": { + "name": "Tilstand" + }, + "power_on_behavior": { + "name": "Startadfærd" + }, + "led_indicator": { + "name": "LED-indikator" + } + }, + "button": { + "boost_all_heating": { + "name": "Boost al opvarmning" + }, + "cancel_all_heating_overrides": { + "name": "Annuller alle varmetilsidesættelser" + }, + "boost_hot_water": { + "name": "Boost varmt vand" + }, + "cancel_hot_water_overrides": { + "name": "Annuller alle varmtvandstilsidesættelser" + }, + "toggle_hot_water": { + "name": "Slå varmt vand til/fra" + }, + "moment": { + "name": "Moment: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Ventilbeskyttelse" + }, + "eco_mode": { + "name": "Økotilstand" + }, + "away_mode_affects_hot_water": { + "name": "Fraværstilstand påvirker varmt vand" + }, + "comfort_mode": { + "name": "Komforttilstand" + }, + "away_mode": { + "name": "Fraværstilstand" + }, + "daylight_saving": { + "name": "Sommertid" + }, + "summer_comfort_enabled": { + "name": "Sommerkomfort aktiveret" + }, + "window_detection": { + "name": "Vinduesregistrering" + }, + "include_in_summer_comfort": { + "name": "Medtag i sommerkomfort" + }, + "device_lock": { + "name": "Enhedslås" + }, + "identify": { + "name": "Identificer" + }, + "outlet": { + "name": "Stikkontakt" + }, + "away_mode_turns_off": { + "name": "Fraværstilstand slukker" + }, + "away_mode_closes": { + "name": "Fraværstilstand lukker" + }, + "passive_mode": { + "name": "Passiv tilstand" + }, + "respect_summer_comfort": { + "name": "Respekter sommerkomfort" + }, + "interacts_with_room_climate": { + "name": "Interagerer med rumklima" + }, + "humidity_interacts_with_room_climate": { + "name": "Luftfugtighed interagerer med rumklima" + }, + "temperature_interacts_with_room_climate": { + "name": "Temperatur interagerer med rumklima" + }, + "illuminance_interacts_with_room_climate": { + "name": "Belysningsstyrke interagerer med rumklima" + }, + "sensor_interacts_with_room_climate": { + "name": "{sensor} interagerer med rumklima" + }, + "manual_heat": { + "name": "Manuel opvarmning" + }, + "hot_water": { + "name": "Varmt vand" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Forebyggelse af sommerubehag" + }, + "summer_comfort_available": { + "name": "Sommerkomfort tilgængelig" + }, + "pcm_device_limit_reached": { + "name": "Grænsen for PCM-enheder er nået" + }, + "smoke_alarm": { + "name": "Røgalarm" + }, + "heat_alarm": { + "name": "Varmealarm" + }, + "tamper_alarm": { + "name": "Manipulationsalarm" + }, + "fault_warning": { + "name": "Fejladvarsel" + }, + "remote_alarm": { + "name": "Fjernalarm" + }, + "battery_defect": { + "name": "Batterifejl" + }, + "controllable": { + "name": "Kan styres" + }, + "pcm_mode": { + "name": "PCM-tilstand" + }, + "window_detection_active": { + "name": "Vinduesregistrering aktiv" + }, + "is_dimmable": { + "name": "Kan dæmpes" + }, + "is_led_indicator_supported": { + "name": "LED-indikator understøttes" + }, + "is_output_mode_supported": { + "name": "Udgangstilstand understøttes" + }, + "is_power_on_behaviour_supported": { + "name": "Startadfærd understøttes" + }, + "is_tilt_supported": { + "name": "Hældning understøttes" + }, + "is_open": { + "name": "Åben" + }, + "is_closed": { + "name": "Lukket" + }, + "active": { + "name": "Aktiv" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Læsning/skrivning af kedlens maksimale centralvarmeindstillingspunkt" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Overførsel af kedlens maksimale centralvarmeindstillingspunkt aktiveret" + }, + "boiler_fault": { + "name": "Kedelfejl" + }, + "boiler_hw_setpoint_read_write": { + "name": "Læsning/skrivning af kedlens varmtvandsindstillingspunkt" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Overførsel af kedlens varmtvandsindstillingspunkt aktiveret" + }, + "central_heating_2_active": { + "name": "Centralvarme 2 aktiv" + }, + "central_heating_active": { + "name": "Centralvarme aktiv" + }, + "ch1_flow_enabled": { + "name": "Centralvarmekreds 1 fremløb aktiveret" + }, + "ch2_flow_enabled": { + "name": "Centralvarmekreds 2 fremløb aktiveret" + }, + "cooling_active": { + "name": "Køling aktiv" + }, + "diagnostic_event": { + "name": "Diagnosticeringshændelse" + }, + "flame_active": { + "name": "Flamme aktiv" + }, + "hot_water_active": { + "name": "Varmt vand aktivt" + }, + "hw_enabled": { + "name": "Varmt vand aktiveret" + } + }, + "sensor": { + "signal": { + "name": "Signal" + }, + "cloud": { + "name": "Sky" + }, + "heating_operation_mode": { + "name": "Driftstilstand for opvarmning" + }, + "heating": { + "name": "Opvarmning", + "state": { + "off": "Fra", + "on": "Til" + } + }, + "heating_channel": { + "name": "Varmekanal {channel}", + "state": { + "off": "Fra", + "on": "Til" + } + }, + "hot_water": { + "name": "Varmt vand", + "state": { + "off": "Fra", + "on": "Til" + } + }, + "hot_water_operation_mode": { + "name": "Driftstilstand for varmt vand" + }, + "target_temperature": { + "name": "Måltemperatur" + }, + "floor_temperature": { + "name": "Gulvtemperatur" + }, + "boiler_flow_temperature": { + "name": "Kedlens fremløbstemperatur" + }, + "boiler_return_temperature": { + "name": "Kedlens returtemperatur" + }, + "flame_statistics": { + "name": "Flammestatistik" + }, + "boiler_ch_setpoint": { + "name": "Kedlens centralvarmeindstillingspunkt" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Nedre grænse for kedlens centralvarmeindstillingspunkt" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Øvre grænse for kedlens centralvarmeindstillingspunkt" + }, + "boiler_exhaust_temperature": { + "name": "Kedlens røggastemperatur" + }, + "boiler_hw_setpoint": { + "name": "Kedlens varmtvandsindstillingspunkt" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Nedre grænse for kedlens varmtvandsindstillingspunkt" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Øvre grænse for kedlens varmtvandsindstillingspunkt" + }, + "ch1_flow_setpoint": { + "name": "Centralvarmekreds 1 fremløbsindstillingspunkt" + }, + "ch2_flow_setpoint": { + "name": "Centralvarmekreds 2 fremløbsindstillingspunkt" + }, + "ch_flow_active_lower_setpoint": { + "name": "Aktivt nedre centralvarmefremløbsindstillingspunkt" + }, + "ch_flow_active_upper_setpoint": { + "name": "Aktivt øvre centralvarmefremløbsindstillingspunkt" + }, + "ch_pressure_bar": { + "name": "Centralvarmetryk" + }, + "connection_status": { + "name": "Forbindelsesstatus" + }, + "coprocessor_update_status": { + "name": "Opdateringsstatus for OpenTherm-coprocessor" + }, + "coprocessor_version": { + "name": "Version af OpenTherm-coprocessor" + }, + "delta_t": { + "name": "Delta-T" + }, + "estimated_boiler_output": { + "name": "Anslået kedelydelse" + }, + "hw_flow_rate": { + "name": "Varmtvandsflow" + }, + "hw_flow_setpoint": { + "name": "Varmtvandsfremløbets indstillingspunkt" + }, + "hw_temperature": { + "name": "Varmtvandstemperatur" + }, + "maximum_capacity_kw": { + "name": "Maksimal kedelkapacitet" + }, + "minimum_modulation_level": { + "name": "Mindste modulationsniveau" + }, + "operating_mode": { + "name": "Driftstilstand" + }, + "relative_modulation_level": { + "name": "Relativt modulationsniveau" + }, + "room_setpoint": { + "name": "Rumindstillingspunkt" + }, + "room_temperature": { + "name": "Rumtemperatur" + }, + "slave_status": { + "name": "OpenTherm-slavestatus" + }, + "tracked_room_id": { + "name": "Id for overvåget rum" + }, + "heating_demand": { + "name": "Varmebehov" + }, + "heating_demand_channel": { + "name": "Varmebehovskanal {channel}" + }, + "hot_water_demand": { + "name": "Varmtvandsbehov" + }, + "total_energy": { + "name": "Samlet energi" + }, + "energy_delivered": { + "name": "Leveret energi" + }, + "energy_received": { + "name": "Modtaget energi" + }, + "equipment_power": { + "name": "Udstyr" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} starter opvarmning", + "stopped_heating": "{entity_name} stopper opvarmning", + "boosted": "{entity_name} er boostet", + "target_temperature_increased": "Måltemperaturen for {entity_name} er hævet", + "target_temperature_decreased": "Måltemperaturen for {entity_name} er sænket" + } + } +} diff --git a/custom_components/wiser/translations/de.json b/custom_components/wiser/translations/de.json index d0107c82..b5ca7554 100644 --- a/custom_components/wiser/translations/de.json +++ b/custom_components/wiser/translations/de.json @@ -3,21 +3,21 @@ "flow_title": "{name}", "step": { "user": { - "title": "Wiser Heat Hub Setup", - "description": "Bitte gib die IP-Adresse und API Schl\u00fcssel vom Wiser Heat Hub ein", + "title": "Wiser Heat Hub einrichten", + "description": "Bitte geben Sie die IP-Adresse und den geheimen Schlüssel des Wiser Heat Hubs ein.", "data": { - "host": "IP Adresse", + "host": "IP-Adresse oder Hostname", "port": "Portnummer", - "password": "API Schl\u00fcssel" + "password": "Geheimer Schlüssel" } }, "zeroconf_confirm": { - "title": "Wiser Heat Hub Setup ({name})", + "title": "Wiser Heat Hub einrichten ({name})", "description": "Bitte geben Sie den geheimen Schlüssel ein, den Sie vom Hub erhalten haben.", "data": { - "host": "IP Adresse", + "host": "IP-Adresse oder Hostname", "port": "Portnummer", - "password": "API Schl\u00fcssel" + "password": "Geheimer Schlüssel" } } }, @@ -27,39 +27,52 @@ "not_wiser_hub": "Das erkannte Gerät ist kein Wiser Heat Hub." }, "error": { - "auth_failure": "Authentifizierung mit Wiser Heat Hub fehlgeschlagen. Pr\u00fcfe den API Key und versuche es erneut.", - "timeout_error": "Timeout bei der Verbindung zum Wiser Heat Hub. Pr\u00fcfe die IP Adresse und versuche es erneut.", - "timeout_error_discovery": "Timeout bei der Verbindung zum Wiser Heat Hub. Überprüfen Sie, ob Ihr Hub noch online ist, und versuchen Sie es erneut.", + "invalid_hot_water_presets": "Eine oder mehrere Warmwasservoreinstellungen sind ungültig.", + "auth_failure": "Authentifizierung beim Wiser Heat Hub fehlgeschlagen. Überprüfen Sie den geheimen Schlüssel und versuchen Sie es erneut.", + "timeout_error": "Zeitüberschreitung bei der Verbindung zum Wiser Heat Hub. Überprüfen Sie die IP-Adresse und versuchen Sie es erneut.", + "timeout_error_discovery": "Zeitüberschreitung bei der Verbindung zum Wiser Heat Hub. Überprüfen Sie, ob Ihr Hub noch online ist, und versuchen Sie es erneut.", "unknown": "Unbekannter Fehler beim Verbinden mit dem Wiser Heat Hub. Bitte überprüfen Sie die Protokolle." } }, "options": { "step": { "init": { - "title": "Wiser Integrationsoptionen", + "title": "Wiser-Integrationsoptionen", "description": "Wiser-Parameter ändern.", "menu_options": { "main_params": "Hauptparameter", - "automation_params": "Aktivieren Sie integrierte Automatisierungen", - "opentherm_sensors": "OpenTherm-Sensoren" + "automation_params": "Automatisierungsparameter", + "opentherm": "OpenTherm", + "equipment_sensors": "Gerätesensoren", + "ui_options": "UI-Optionen" + } + }, + "ui_options": { + "title": "UI-Optionen", + "data": { + "legacy_naming": "Bisherige Namensgebung", + "show_wiser_sidebar": "Wiser in der Seitenleiste anzeigen" + }, + "data_description": { + "legacy_naming": "Raumnamen in Wiser-Gerätenamen aufnehmen. Wenn diese Option aktiviert ist, werden Bereichsnamen beim Neuerstellen der Entitäts-IDs mit dem Standardformat unter Einstellungen → System → Entitäts-ID-Format (Home Assistant 2026.8 und neuer) doppelt verwendet, wenn Bereichs- und Wiser-Raumname übereinstimmen. Diese Option ändert keine bestehenden Entitäts-IDs." } }, "main_params": { - "title": "Wiser Integrationsoptionen", + "title": "Wiser-Integrationsoptionen", "description": "Hauptparameter", "data": { - "host": "IP Adresse", + "host": "IP-Adresse oder Hostname", "port": "Portnummer", - "scan_interval": "Scan Intervall", - "heating_boost_temp": "Boost Temperatur", - "heating_boost_time": "Boost Dauer", - "hotwater_boost_time": "Standard-Warmwasser-Boost-Dauer", + "scan_interval": "Abfrageintervall (Sek.)", + "heating_boost_temp": "Standard-Heizungs-Boost-Temperatur (°C)", + "heating_boost_time": "Standard-Heizungs-Boost-Dauer (Min.)", + "hotwater_boost_time": "Standard-Warmwasser-Boost-Dauer (Min.)", "setpoint_mode": "Sollwert-Modus", "restore_manual_temp_option": "Verwenden Sie diese Temperatur, wenn keine gespeicherte manuelle Temperatur vorhanden ist" } }, "automation_params": { - "title": "Wiser Integrationsoptionen", + "title": "Wiser-Integrationsoptionen", "description": "Aktivieren Sie integrierte Automatisierungen", "sections": { "automations_passive_mode": { @@ -81,9 +94,37 @@ "hotwater_boost_mode": "Warmwasser-Boost-Modus", "hotwater_sensor_entity_id": "Warmwasser-Temperatursensor" } + }, + "additional_sensors": { + "name": "Zusätzliche Sensoren", + "description": "Optionale Sensoren für von Wiser gemeldete Daten bereitstellen.", + "data": { + "schedule_setpoint_rooms": "Räume ohne Klimasteuerung" + }, + "data_description": { + "schedule_setpoint_rooms": "Wählen Sie die Wiser-Räume aus, für die ein Sensor für die aktuelle Zeitplantemperatur erstellt werden soll." + } } } }, + "equipment_sensors": { + "title": "Gerätesensoren", + "description": "Detaillierte Gerätedatenentitäten für kompatible Geräte konfigurieren.", + "data": { + "equipment_sensors": "Gerätedatensensoren aktivieren" + }, + "data_description": { + "equipment_sensors": "Stellt für jede kompatible intelligente Steckdose, jedes PowerTag und jeden Heizungsaktor einen zusätzlichen Sensor mit detaillierten Geräteattributen bereit." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "OpenTherm-Funktionen konfigurieren.", + "menu_options": { + "opentherm_hot_water_control": "Warmwassersteuerung", + "opentherm_sensors": "Sensoren" + } + }, "opentherm_sensors": { "title": "OpenTherm-Sensoren", "description": "Wählen Sie eine Kategorie erkannter OpenTherm-Attribute.", @@ -94,6 +135,20 @@ "opentherm_status": "Status und Diagnose" } }, + "opentherm_hot_water_control": { + "title": "OpenTherm-Warmwassersteuerung", + "description": "Steuert optional den Warmwasser-Sollwert des Kessels über Wiser. Die Integration erstellt den Zeitplan „OpenTherm Hot Water“ im Wiser-Zeitplanbereich. Aus verwendet die von OpenTherm gemeldete minimale Warmwassertemperatur.", + "data": { + "opentherm_hot_water_control": "Warmwassersteuerung aktivieren", + "opentherm_hot_water_manual_temperature": "Manuelle Temperatur", + "opentherm_hot_water_sensor_entity_id": "Optionaler externer Temperatursensor.", + "opentherm_hot_water_presets": "Voreinstellungen" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperatur für den manuellen Modus.", + "opentherm_hot_water_sensor_entity_id": "Optionaler Temperatursensor für die aktuelle Wassertemperatur. Wenn der Sensor nicht verfügbar ist, wird OpenTherm verwendet." + } + }, "opentherm_readings": { "title": "OpenTherm-Messwerte", "description": "Wählen Sie aus, welche erkannten OpenTherm-Messwerte als Sensorentitäten bereitgestellt werden.", @@ -140,7 +195,7 @@ "boiler_hw_setpoint_transfer_enable": "Übertragung des Kessel-Warmwassersollwerts aktiviert", "boiler_hw_setpoint_upper_bound": "Obere Grenze des Kessel-Warmwassersollwerts", "hw_enabled": "Warmwasser aktiviert", - "hw_flow_setpoint": "Warmwasser-Durchflusssollwert" + "hw_flow_setpoint": "Warmwasser-Vorlaufsolltemperatur" } }, "opentherm_status": { @@ -165,7 +220,8 @@ } }, "abort": { - "opentherm_not_available": "Für diesen Wiser Heat Hub sind keine OpenTherm-Daten verfügbar." + "opentherm_not_available": "Für diesen Wiser Heat Hub sind keine OpenTherm-Daten verfügbar.", + "equipment_not_available": "Für diesen Wiser Heat Hub sind keine Gerätedaten verfügbar." } }, "services": { @@ -218,6 +274,9 @@ }, "floor_temperature": { "name": "Fußbodentemperatur" } }, + "water_heater": { + "opentherm_hot_water": { "name": "OpenTherm-Warmwasser" } + }, "number": { "away_mode_target_temperature": { "name": "Zieltemperatur im Abwesenheitsmodus" }, "floor_temperature_offset": { "name": "Korrektur der Fußbodentemperatur" }, @@ -250,6 +309,7 @@ "device_lock": { "name": "Gerätesperre" }, "identify": { "name": "Identifizieren" }, "outlet": { "name": "Steckdose" }, + "ev_charger": { "name": "EV-Ladegerät" }, "away_mode_turns_off": { "name": "Abwesenheitsmodus schaltet aus" }, "away_mode_closes": { "name": "Abwesenheitsmodus schließt" }, "passive_mode": { "name": "Passivmodus" }, @@ -272,6 +332,9 @@ "fault_warning": { "name": "Fehlerwarnung" }, "remote_alarm": { "name": "Fernalarm" }, "battery_defect": { "name": "Batteriedefekt" }, + "ac_mains": { "name": "Netzstrom" }, + "test_mode": { "name": "Testmodus" }, + "hush_mode": { "name": "Stummschaltungsmodus" }, "controllable": { "name": "Steuerbar" }, "pcm_mode": { "name": "PCM-Modus" }, "window_detection_active": { "name": "Fenstererkennung aktiv" }, @@ -300,13 +363,33 @@ }, "sensor": { "signal": { "name": "Signal" }, + "report_count": { "name": "Berichtsanzahl" }, "cloud": { "name": "Cloud" }, "heating_operation_mode": { "name": "Heizungsbetriebsart" }, - "heating": { "name": "Heizung" }, - "heating_channel": { "name": "Heizkanal {channel}" }, - "hot_water": { "name": "Warmwasser" }, + "heating": { + "name": "Heizung", + "state": { + "off": "Aus", + "on": "Ein" + } + }, + "heating_channel": { + "name": "Heizkanal {channel}", + "state": { + "off": "Aus", + "on": "Ein" + } + }, + "hot_water": { + "name": "Warmwasser", + "state": { + "off": "Aus", + "on": "Ein" + } + }, "hot_water_operation_mode": { "name": "Warmwasserbetriebsart" }, "target_temperature": { "name": "Zieltemperatur" }, + "current_schedule_temperature": { "name": "Aktuelle Zeitplantemperatur" }, "floor_temperature": { "name": "Fußbodentemperatur" }, "boiler_flow_temperature": { "name": "Kesselvorlauftemperatur" }, "boiler_return_temperature": { "name": "Kesselrücklauftemperatur" }, @@ -329,7 +412,7 @@ "delta_t": { "name": "Delta-T" }, "estimated_boiler_output": { "name": "Geschätzte Kesselleistung" }, "hw_flow_rate": { "name": "Warmwasser-Durchfluss" }, - "hw_flow_setpoint": { "name": "Warmwasser-Durchflusssollwert" }, + "hw_flow_setpoint": { "name": "Warmwasser-Vorlaufsolltemperatur" }, "hw_temperature": { "name": "Warmwassertemperatur" }, "maximum_capacity_kw": { "name": "Maximale Kesselleistung" }, "minimum_modulation_level": { "name": "Minimale Modulationsstufe" }, @@ -345,7 +428,7 @@ "total_energy": { "name": "Gesamtenergie" }, "energy_delivered": { "name": "Gelieferte Energie" }, "energy_received": { "name": "Empfangene Energie" }, - "equipment_power": { "name": "Geräteleistung" } + "equipment_power": { "name": "Gerät" } } }, "device_automation": { @@ -353,8 +436,8 @@ "started_heating": "{entity_name} beginnt zu heizen", "stopped_heating": "{entity_name} stoppt die Heizung", "boosted": "{entity_name} wird verstärkt", - "target_temperature_increased": "{entity_name} zieltemperatur erhöht", - "target_temperature_decreased": "{entity_name} zieltemperatur gesenkt" + "target_temperature_increased": "Die Zieltemperatur von {entity_name} steigt", + "target_temperature_decreased": "Die Zieltemperatur von {entity_name} sinkt" } } } diff --git a/custom_components/wiser/translations/en-GB.json b/custom_components/wiser/translations/en-GB.json new file mode 100644 index 00000000..f3727f58 --- /dev/null +++ b/custom_components/wiser/translations/en-GB.json @@ -0,0 +1,443 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Wiser Heat Hub Setup", + "description": "Please enter the IP address and secret key obtained from the hub.", + "data": { + "host": "IP Address or Hostname", + "port": "Port number", + "password": "Secret Key" + } + }, + "zeroconf_confirm": { + "title": "Wiser Heat Hub Setup ({name})", + "description": "Please enter the secret key obtained from the hub.", + "data": { + "host": "IP Address or Hostname", + "port": "Port number", + "password": "Secret Key" + } + } + }, + "abort": { + "already_in_progress": "Wiser Heat Hub configuration is already in progress.", + "already_configured": "This device is already configured.", + "not_wiser_hub": "The discovered device is not a Wiser Heat Hub." + }, + "error": { + "invalid_hot_water_presets": "One or more hot-water presets are invalid.", + "auth_failure": "Unable to authenticate with the Wiser hub. Check the secret key and try again.", + "timeout_error": "Timed out trying to connect to the Wiser hub. Check the IP address and try again.", + "timeout_error_discovery": "Timed out trying to connect to the Wiser hub. Check that your hub is still online and try again.", + "unknown": "Unknown error connecting to the Wiser hub. Please check the logs." + } + }, + "options": { + "step": { + "init": { + "title": "Wiser Integration Options", + "description": "Select parameters to amend", + "menu_options": { + "main_params": "Main Parameters", + "automation_params": "Automation Parameters", + "opentherm": "OpenTherm", + "equipment_sensors": "Equipment sensors", + "ui_options": "UI options" + } + }, + "ui_options": { + "title": "UI options", + "data": { + "legacy_naming": "Legacy naming", + "show_wiser_sidebar": "Show Wiser in sidebar" + }, + "data_description": { + "legacy_naming": "Include room names in Wiser device names. If enabled, using Home Assistant to regenerate entity IDs with the default format in Settings → System → Entity ID format (Home Assistant 2026.8 and later) will duplicate area names when the area and Wiser room names match. Changing this option does not rename existing entity IDs." + } + }, + "main_params": { + "title": "Wiser Integration Options", + "description": "Main Parameters", + "data": { + "host": "IP Address or Hostname", + "port": "Port number", + "scan_interval": "Scan Interval (secs)", + "heating_boost_temp": "Default Heating Boost Temperature (°C)", + "heating_boost_time": "Default Heating Boost Duration (mins)", + "hotwater_boost_time": "Default Hot Water Boost Duration (mins)", + "setpoint_mode": "Setpoint Mode", + "restore_manual_temp_option": "Use This Temperature If No Manual Temperature Is Stored" + } + }, + "automation_params": { + "title": "Wiser Integration Options", + "description": "Enable built-in automations", + "sections": { + "automations_passive_mode": { + "name": "Passive Mode", + "description": "Enable passive TRV capability. Each room needs to be enabled for it to function.", + "data": { + "automations_passive_mode": "Passive Mode", + "passive_mode_temperature_increments": "Passive Mode Temperature Increments" + } + }, + "hotwater_climate": { + "name": "Hot Water Climate Control", + "description": "Enable hot-water climate control. See the wiki for details.", + "data": { + "hotwater_climate": "Enable the climate entity", + "experimental_mode": "Enable experimental working mode", + "hotwater_auto_mode": "Hot Water Auto Mode", + "hotwater_heat_mode": "Hot Water Heat Mode", + "hotwater_boost_mode": "Hot Water Boost Mode", + "hotwater_sensor_entity_id": "Hot Water Temperature Sensor" + } + }, + "additional_sensors": { + "name": "Additional sensors", + "description": "Expose optional sensors for data reported by Wiser.", + "data": { + "schedule_setpoint_rooms": "Rooms without climate control" + }, + "data_description": { + "schedule_setpoint_rooms": "Select the Wiser rooms where a current schedule temperature sensor should be created." + } + } + } + }, + "equipment_sensors": { + "title": "Equipment sensors", + "description": "Configure detailed equipment data entities for compatible devices.", + "data": { + "equipment_sensors": "Enable equipment data sensors" + }, + "data_description": { + "equipment_sensors": "Expose an additional sensor containing detailed equipment attributes for each compatible smart plug, PowerTag, and heating actuator." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Configure OpenTherm features.", + "menu_options": { + "opentherm_hot_water_control": "Hot water control", + "opentherm_sensors": "Sensors" + } + }, + "opentherm_sensors": { + "title": "OpenTherm sensors", + "description": "Choose a category of detected OpenTherm attributes.", + "menu_options": { + "opentherm_readings": "Readings", + "opentherm_central_heating": "Central heating", + "opentherm_hot_water": "Hot water", + "opentherm_status": "Status and diagnostics" + } + }, + "opentherm_hot_water_control": { + "title": "OpenTherm hot-water control", + "description": "Optionally control the boiler domestic-hot-water setpoint through Wiser. The integration creates an OpenTherm Hot Water schedule in the Wiser Schedules panel. Off uses the minimum hot-water temperature reported by OpenTherm.", + "data": { + "opentherm_hot_water_control": "Enable hot-water control", + "opentherm_hot_water_manual_temperature": "Manual temperature", + "opentherm_hot_water_sensor_entity_id": "Optional external temperature sensor.", + "opentherm_hot_water_presets": "Presets" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperature used by Manual mode.", + "opentherm_hot_water_sensor_entity_id": "Optional temperature sensor used for the current water temperature. OpenTherm is used when the sensor is unavailable." + } + }, + "opentherm_readings": { + "title": "OpenTherm readings", + "description": "Choose which detected OpenTherm readings are exposed as sensor entities.", + "data": { + "ch_flow_temperature": "Boiler flow temperature", + "ch_pressure_bar": "CH pressure", + "ch_return_temperature": "Boiler return temperature", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Boiler exhaust temperature", + "estimated_boiler_output": "Estimated boiler output", + "hw_flow_rate": "Hot water flow rate", + "hw_temperature": "Hot water temperature", + "maximum_capacity_kw": "Maximum boiler capacity", + "minimum_modulation_level": "Minimum modulation level", + "relative_modulation_level": "Relative modulation level", + "room_temperature": "Room temperature" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm central heating", + "description": "Choose which detected central-heating attributes are exposed as entities.", + "data": { + "boiler_ch_max_setpoint_read_write": "Boiler CH maximum setpoint read/write", + "boiler_ch_max_setpoint_transfer_enable": "Boiler CH maximum setpoint transfer enabled", + "boiler_ch_setpoint": "Boiler CH setpoint", + "boiler_ch_setpoint_lower_bound": "Boiler CH setpoint lower bound", + "boiler_ch_setpoint_upper_bound": "Boiler CH setpoint upper bound", + "ch_flow_active_lower_setpoint": "CH flow active lower setpoint", + "ch_flow_active_upper_setpoint": "CH flow active upper setpoint", + "ch1_flow_enabled": "CH1 flow enabled", + "ch1_flow_setpoint": "CH1 flow setpoint", + "ch2_flow_enabled": "CH2 flow enabled", + "ch2_flow_setpoint": "CH2 flow setpoint", + "room_setpoint": "Room setpoint" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm hot water", + "description": "Choose which detected hot-water attributes are exposed as entities.", + "data": { + "boiler_hw_setpoint": "Boiler hot water setpoint", + "boiler_hw_setpoint_lower_bound": "Boiler hot water setpoint lower bound", + "boiler_hw_setpoint_read_write": "Boiler hot water setpoint read/write", + "boiler_hw_setpoint_transfer_enable": "Boiler hot water setpoint transfer enabled", + "boiler_hw_setpoint_upper_bound": "Boiler hot water setpoint upper bound", + "hw_enabled": "Hot water enabled", + "hw_flow_setpoint": "Hot water flow setpoint" + } + }, + "opentherm_status": { + "title": "OpenTherm status and diagnostics", + "description": "Choose which detected status and diagnostic attributes are exposed as sensor entities.", + "data": { + "boiler_fault": "Boiler fault", + "central_heating_active": "Central heating active", + "central_heating_2_active": "Central heating 2 active", + "connection_status": "Connection status", + "coprocessor_update_status": "OpenTherm coprocessor update status", + "coprocessor_version": "OpenTherm coprocessor version", + "cooling_active": "Cooling active", + "diagnostic_event": "Diagnostic event", + "flame_active": "Flame active", + "flame_statistics": "Flame statistics", + "hot_water_active": "Hot water active", + "operating_mode": "Operating mode", + "slave_status": "OpenTherm slave status", + "tracked_room_id": "Tracked room ID" + } + } + }, + "abort": { + "opentherm_not_available": "OpenTherm data is not available for this Wiser hub.", + "equipment_not_available": "Equipment data is not available for this Wiser hub." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "Set OpenTherm parameter", + "description": "Changes a supported OpenTherm temperature parameter on the Wiser hub.", + "fields": { + "parameter": { + "name": "Parameter", + "description": "OpenTherm temperature parameter to change. The required API endpoint is selected automatically." + }, + "temperature": { + "name": "Temperature", + "description": "Use a temperature within the limits reported by your boiler." + }, + "hub": { + "name": "Wiser hub", + "description": "Only select a hub when you have multiple Wiser hubs." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Active central-heating lower setpoint", + "chFlowActiveUpperSetpoint": "Active central-heating upper setpoint", + "dhwFlowSetpoint": "Hub hot-water flow setpoint", + "dhwSetpoint": "Boiler hot-water setpoint", + "maxChSetpoint": "Boiler maximum central-heating setpoint" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { "message": "Please provide an OpenTherm temperature." }, + "opentherm_hub_not_found": { "message": "The specified Wiser hub was not found." }, + "opentherm_hub_not_loaded": { "message": "The specified Wiser hub is not loaded." }, + "opentherm_hub_required": { "message": "Please select a Wiser hub when more than one hub is configured." }, + "opentherm_not_supported": { "message": "This hub does not have OpenTherm functionality." }, + "opentherm_invalid_parameter": { "message": "The OpenTherm parameter or value is invalid: {error}" }, + "opentherm_write_failed": { "message": "Unable to set OpenTherm parameter {parameter}: {error}" }, + "opentherm_confirmation_failed": { "message": "OpenTherm parameter {parameter} did not remain at {requested} °C after one retry and the {window}-second confirmation window. Wiser reports {reported} °C. No further retry will be sent." }, + "opentherm_retry_failed": { "message": "OpenTherm parameter {parameter} did not remain at {requested} °C, and its retry could not be sent: {error}. Wiser reports {reported} °C. No further retry will be sent." }, + "opentherm_notification_title": { "message": "Wiser OpenTherm command" } + }, + "entity": { + "climate": { + "heating": { + "name": "Heating" + }, + "floor_temperature": { "name": "Floor temperature" } + }, + "water_heater": { + "opentherm_hot_water": { "name": "OpenTherm hot water" } + }, + "number": { + "away_mode_target_temperature": { "name": "Away mode target temperature" }, + "floor_temperature_offset": { "name": "Floor temperature offset" }, + "indoor_discomfort_temperature": { "name": "Indoor discomfort temperature" }, + "outdoor_discomfort_temperature": { "name": "Outdoor discomfort temperature" } + }, + "select": { + "mode": { "name": "Mode" }, + "power_on_behavior": { "name": "Power-on behaviour" }, + "led_indicator": { "name": "LED indicator" } + }, + "button": { + "boost_all_heating": { "name": "Boost all heating" }, + "cancel_all_heating_overrides": { "name": "Cancel all heating overrides" }, + "boost_hot_water": { "name": "Boost hot water" }, + "cancel_hot_water_overrides": { "name": "Cancel hot water overrides" }, + "toggle_hot_water": { "name": "Toggle hot water" }, + "moment": { "name": "Moment: {name}" } + }, + "switch": { + "valve_protection": { "name": "Valve protection" }, + "eco_mode": { "name": "Eco mode" }, + "away_mode_affects_hot_water": { "name": "Away mode affects hot water" }, + "comfort_mode": { "name": "Comfort mode" }, + "away_mode": { "name": "Away mode" }, + "daylight_saving": { "name": "Daylight saving" }, + "summer_comfort_enabled": { "name": "Summer comfort enabled" }, + "window_detection": { "name": "Window detection" }, + "include_in_summer_comfort": { "name": "Include in summer comfort" }, + "device_lock": { "name": "Device lock" }, + "identify": { "name": "Identify" }, + "outlet": { "name": "Socket" }, + "ev_charger": { "name": "EV charger" }, + "away_mode_turns_off": { "name": "Away mode turns off" }, + "away_mode_closes": { "name": "Away mode closes" }, + "passive_mode": { "name": "Passive mode" }, + "respect_summer_comfort": { "name": "Respect summer comfort" }, + "interacts_with_room_climate": { "name": "Interacts with room climate" }, + "humidity_interacts_with_room_climate": { "name": "Humidity interacts with room climate" }, + "temperature_interacts_with_room_climate": { "name": "Temperature interacts with room climate" }, + "illuminance_interacts_with_room_climate": { "name": "Illuminance interacts with room climate" }, + "sensor_interacts_with_room_climate": { "name": "{sensor} interacts with room climate" }, + "manual_heat": { "name": "Manual heat" }, + "hot_water": { "name": "Hot water" } + }, + "binary_sensor": { + "summer_discomfort_prevention": { "name": "Summer discomfort prevention" }, + "summer_comfort_available": { "name": "Summer comfort available" }, + "pcm_device_limit_reached": { "name": "PCM device limit reached" }, + "smoke_alarm": { "name": "Smoke alarm" }, + "heat_alarm": { "name": "Heat alarm" }, + "tamper_alarm": { "name": "Tamper alarm" }, + "fault_warning": { "name": "Fault warning" }, + "remote_alarm": { "name": "Remote alarm" }, + "battery_defect": { "name": "Battery defect" }, + "ac_mains": { "name": "AC mains" }, + "test_mode": { "name": "Test mode" }, + "hush_mode": { "name": "Hush mode" }, + "controllable": { "name": "Controllable" }, + "pcm_mode": { "name": "PCM mode" }, + "window_detection_active": { "name": "Window detection active" }, + "is_dimmable": { "name": "Dimmable" }, + "is_led_indicator_supported": { "name": "LED indicator supported" }, + "is_output_mode_supported": { "name": "Output mode supported" }, + "is_power_on_behaviour_supported": { "name": "Power-on behaviour supported" }, + "is_tilt_supported": { "name": "Tilt supported" }, + "is_open": { "name": "Open" }, + "is_closed": { "name": "Closed" }, + "active": { "name": "Active" }, + "boiler_ch_max_setpoint_read_write": { "name": "Boiler CH maximum setpoint read/write" }, + "boiler_ch_max_setpoint_transfer_enable": { "name": "Boiler CH maximum setpoint transfer enabled" }, + "boiler_fault": { "name": "Boiler fault" }, + "boiler_hw_setpoint_read_write": { "name": "Boiler hot water setpoint read/write" }, + "boiler_hw_setpoint_transfer_enable": { "name": "Boiler hot water setpoint transfer enabled" }, + "central_heating_2_active": { "name": "Central heating 2 active" }, + "central_heating_active": { "name": "Central heating active" }, + "ch1_flow_enabled": { "name": "CH1 flow enabled" }, + "ch2_flow_enabled": { "name": "CH2 flow enabled" }, + "cooling_active": { "name": "Cooling active" }, + "diagnostic_event": { "name": "Diagnostic event" }, + "flame_active": { "name": "Flame active" }, + "hot_water_active": { "name": "Hot water active" }, + "hw_enabled": { "name": "Hot water enabled" } + }, + "sensor": { + "signal": { "name": "Signal" }, + "report_count": { "name": "Report count" }, + "cloud": { "name": "Cloud" }, + "heating_operation_mode": { "name": "Heating operation mode" }, + "heating": { + "name": "Heating", + "state": { + "off": "Off", + "on": "On" + } + }, + "heating_channel": { + "name": "Heating channel {channel}", + "state": { + "off": "Off", + "on": "On" + } + }, + "hot_water": { + "name": "Hot water", + "state": { + "off": "Off", + "on": "On" + } + }, + "hot_water_operation_mode": { "name": "Hot water operation mode" }, + "target_temperature": { "name": "Target temperature" }, + "current_schedule_temperature": { "name": "Current schedule temperature" }, + "floor_temperature": { "name": "Floor temperature" }, + "boiler_flow_temperature": { "name": "Boiler flow temperature" }, + "boiler_return_temperature": { "name": "Boiler return temperature" }, + "flame_statistics": { "name": "Flame statistics" }, + "boiler_ch_setpoint": { "name": "Boiler CH setpoint" }, + "boiler_ch_setpoint_lower_bound": { "name": "Boiler CH setpoint lower bound" }, + "boiler_ch_setpoint_upper_bound": { "name": "Boiler CH setpoint upper bound" }, + "boiler_exhaust_temperature": { "name": "Boiler exhaust temperature" }, + "boiler_hw_setpoint": { "name": "Boiler hot water setpoint" }, + "boiler_hw_setpoint_lower_bound": { "name": "Boiler hot water setpoint lower bound" }, + "boiler_hw_setpoint_upper_bound": { "name": "Boiler hot water setpoint upper bound" }, + "ch1_flow_setpoint": { "name": "CH1 flow setpoint" }, + "ch2_flow_setpoint": { "name": "CH2 flow setpoint" }, + "ch_flow_active_lower_setpoint": { "name": "CH flow active lower setpoint" }, + "ch_flow_active_upper_setpoint": { "name": "CH flow active upper setpoint" }, + "ch_pressure_bar": { "name": "CH pressure" }, + "connection_status": { "name": "Connection status" }, + "coprocessor_update_status": { "name": "OpenTherm coprocessor update status" }, + "coprocessor_version": { "name": "OpenTherm coprocessor version" }, + "delta_t": { "name": "Delta-T" }, + "estimated_boiler_output": { "name": "Estimated boiler output" }, + "hw_flow_rate": { "name": "Hot water flow rate" }, + "hw_flow_setpoint": { "name": "Hot water flow setpoint" }, + "hw_temperature": { "name": "Hot water temperature" }, + "maximum_capacity_kw": { "name": "Maximum boiler capacity" }, + "minimum_modulation_level": { "name": "Minimum modulation level" }, + "operating_mode": { "name": "Operating mode" }, + "relative_modulation_level": { "name": "Relative modulation level" }, + "room_setpoint": { "name": "Room setpoint" }, + "room_temperature": { "name": "Room temperature" }, + "slave_status": { "name": "OpenTherm slave status" }, + "tracked_room_id": { "name": "Tracked room ID" }, + "heating_demand": { "name": "Heating demand" }, + "heating_demand_channel": { "name": "Heating demand channel {channel}" }, + "hot_water_demand": { "name": "Hot water demand" }, + "total_energy": { "name": "Total energy" }, + "energy_delivered": { "name": "Energy delivered" }, + "energy_received": { "name": "Energy received" }, + "equipment_power": { "name": "Equipment" } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} starts heating", + "stopped_heating": "{entity_name} stops heating", + "boosted": "{entity_name} is boosted", + "target_temperature_increased": "{entity_name} target temperature increased", + "target_temperature_decreased": "{entity_name} target temperature decreased" + } + } +} diff --git a/custom_components/wiser/translations/en.json b/custom_components/wiser/translations/en.json index 5ba00193..17d93c41 100755 --- a/custom_components/wiser/translations/en.json +++ b/custom_components/wiser/translations/en.json @@ -4,7 +4,7 @@ "step": { "user": { "title": "Wiser Heat Hub Setup", - "description": "Please enter the ip address and secret key obtained from the hub.", + "description": "Please enter the IP address and secret key obtained from the hub.", "data": { "host": "IP Address or Hostname", "port": "Port number", @@ -13,7 +13,7 @@ }, "zeroconf_confirm": { "title": "Wiser Heat Hub Setup ({name})", - "description": "Please enter the secret key obtained from the hub", + "description": "Please enter the secret key obtained from the hub.", "data": { "host": "IP Address or Hostname", "port": "Port number", @@ -27,10 +27,11 @@ "not_wiser_hub": "The discovered device is not a Wiser Heat Hub." }, "error": { - "auth_failure": "Unable to authenticate with the Wiser Hub. Check the secret key and try again.", - "timeout_error": "Timed out trying to connect to the Wiser Hub. Check the IP address and try again.", - "timeout_error_discovery": "Timed out trying to connect to the Wiser Hub. Check your hub is still online and try again.", - "unknown": "Unknown error connecting to the Wiser hub. Please check the logs." + "invalid_hot_water_presets": "One or more hot-water presets are invalid.", + "auth_failure": "Unable to authenticate with the Wiser hub. Check the secret key and try again.", + "timeout_error": "Timed out trying to connect to the Wiser hub. Check the IP address and try again.", + "timeout_error_discovery": "Timed out trying to connect to the Wiser hub. Check that your hub is still online and try again.", + "unknown": "Unknown error connecting to the Wiser hub. Please check the logs." } }, "options": { @@ -41,7 +42,19 @@ "menu_options": { "main_params": "Main Parameters", "automation_params": "Automation Parameters", - "opentherm_sensors": "OpenTherm sensors" + "opentherm": "OpenTherm", + "equipment_sensors": "Equipment sensors", + "ui_options": "UI options" + } + }, + "ui_options": { + "title": "UI options", + "data": { + "legacy_naming": "Legacy naming", + "show_wiser_sidebar": "Show Wiser in sidebar" + }, + "data_description": { + "legacy_naming": "Include room names in Wiser device names. If enabled, using Home Assistant to regenerate entity IDs with the default format in Settings → System → Entity ID format (Home Assistant 2026.8 and later) will duplicate area names when the area and Wiser room names match. Changing this option does not rename existing entity IDs." } }, "main_params": { @@ -55,12 +68,12 @@ "heating_boost_time": "Default Heating Boost Duration (mins)", "hotwater_boost_time": "Default Hot Water Boost Duration (mins)", "setpoint_mode": "Setpoint Mode", - "restore_manual_temp_option": "Use This Temperature If No Stored Manual Temperature" + "restore_manual_temp_option": "Use This Temperature If No Manual Temperature Is Stored" } }, "automation_params": { "title": "Wiser Integration Options", - "description": "Enable in-built automations", + "description": "Enable built-in automations", "sections": { "automations_passive_mode": { "name": "Passive Mode", @@ -81,9 +94,37 @@ "hotwater_boost_mode": "Hot Water Boost Mode", "hotwater_sensor_entity_id": "Hot Water Temperature Sensor" } + }, + "additional_sensors": { + "name": "Additional sensors", + "description": "Expose optional sensors for data reported by Wiser.", + "data": { + "schedule_setpoint_rooms": "Rooms without climate control" + }, + "data_description": { + "schedule_setpoint_rooms": "Select the Wiser rooms where a current schedule temperature sensor should be created." + } } } }, + "equipment_sensors": { + "title": "Equipment sensors", + "description": "Configure detailed equipment data entities for compatible devices.", + "data": { + "equipment_sensors": "Enable equipment data sensors" + }, + "data_description": { + "equipment_sensors": "Expose an additional sensor containing detailed equipment attributes for each compatible smart plug, PowerTag, and heating actuator." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Configure OpenTherm features.", + "menu_options": { + "opentherm_hot_water_control": "Hot water control", + "opentherm_sensors": "Sensors" + } + }, "opentherm_sensors": { "title": "OpenTherm sensors", "description": "Choose a category of detected OpenTherm attributes.", @@ -94,6 +135,20 @@ "opentherm_status": "Status and diagnostics" } }, + "opentherm_hot_water_control": { + "title": "OpenTherm hot-water control", + "description": "Optionally control the boiler domestic-hot-water setpoint through Wiser. The integration creates an OpenTherm Hot Water schedule in the Wiser Schedules panel. Off uses the minimum hot-water temperature reported by OpenTherm.", + "data": { + "opentherm_hot_water_control": "Enable hot-water control", + "opentherm_hot_water_manual_temperature": "Manual temperature", + "opentherm_hot_water_sensor_entity_id": "Optional external temperature sensor.", + "opentherm_hot_water_presets": "Presets" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperature used by Manual mode.", + "opentherm_hot_water_sensor_entity_id": "Optional temperature sensor used for the current water temperature. OpenTherm is used when the sensor is unavailable." + } + }, "opentherm_readings": { "title": "OpenTherm readings", "description": "Choose which detected OpenTherm readings are exposed as sensor entities.", @@ -165,7 +220,8 @@ } }, "abort": { - "opentherm_not_available": "OpenTherm data is not available for this Wiser hub." + "opentherm_not_available": "OpenTherm data is not available for this Wiser hub.", + "equipment_not_available": "Equipment data is not available for this Wiser hub." } }, "services": { @@ -218,6 +274,9 @@ }, "floor_temperature": { "name": "Floor temperature" } }, + "water_heater": { + "opentherm_hot_water": { "name": "OpenTherm hot water" } + }, "number": { "away_mode_target_temperature": { "name": "Away mode target temperature" }, "floor_temperature_offset": { "name": "Floor temperature offset" }, @@ -250,6 +309,7 @@ "device_lock": { "name": "Device lock" }, "identify": { "name": "Identify" }, "outlet": { "name": "Outlet" }, + "ev_charger": { "name": "EV charger" }, "away_mode_turns_off": { "name": "Away mode turns off" }, "away_mode_closes": { "name": "Away mode closes" }, "passive_mode": { "name": "Passive mode" }, @@ -272,6 +332,9 @@ "fault_warning": { "name": "Fault warning" }, "remote_alarm": { "name": "Remote alarm" }, "battery_defect": { "name": "Battery defect" }, + "ac_mains": { "name": "AC mains" }, + "test_mode": { "name": "Test mode" }, + "hush_mode": { "name": "Hush mode" }, "controllable": { "name": "Controllable" }, "pcm_mode": { "name": "PCM mode" }, "window_detection_active": { "name": "Window detection active" }, @@ -300,13 +363,33 @@ }, "sensor": { "signal": { "name": "Signal" }, + "report_count": { "name": "Report count" }, "cloud": { "name": "Cloud" }, "heating_operation_mode": { "name": "Heating operation mode" }, - "heating": { "name": "Heating" }, - "heating_channel": { "name": "Heating channel {channel}" }, - "hot_water": { "name": "Hot water" }, + "heating": { + "name": "Heating", + "state": { + "off": "Off", + "on": "On" + } + }, + "heating_channel": { + "name": "Heating channel {channel}", + "state": { + "off": "Off", + "on": "On" + } + }, + "hot_water": { + "name": "Hot water", + "state": { + "off": "Off", + "on": "On" + } + }, "hot_water_operation_mode": { "name": "Hot water operation mode" }, "target_temperature": { "name": "Target temperature" }, + "current_schedule_temperature": { "name": "Current schedule temperature" }, "floor_temperature": { "name": "Floor temperature" }, "boiler_flow_temperature": { "name": "Boiler flow temperature" }, "boiler_return_temperature": { "name": "Boiler return temperature" }, @@ -345,7 +428,7 @@ "total_energy": { "name": "Total energy" }, "energy_delivered": { "name": "Energy delivered" }, "energy_received": { "name": "Energy received" }, - "equipment_power": { "name": "Equipment power" } + "equipment_power": { "name": "Equipment" } } }, "device_automation": { diff --git a/custom_components/wiser/translations/es.json b/custom_components/wiser/translations/es.json new file mode 100644 index 00000000..7520dfe2 --- /dev/null +++ b/custom_components/wiser/translations/es.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Configuración del Heat Hub Wiser", + "description": "Introduzca la dirección IP y la clave secreta obtenida del hub.", + "data": { + "host": "Dirección IP o nombre de host", + "port": "Número de puerto", + "password": "Clave secreta" + } + }, + "zeroconf_confirm": { + "title": "Configuración del Heat Hub Wiser ({name})", + "description": "Introduzca la clave secreta obtenida del hub.", + "data": { + "host": "Dirección IP o nombre de host", + "port": "Número de puerto", + "password": "Clave secreta" + } + } + }, + "abort": { + "already_in_progress": "La configuración del Heat Hub Wiser ya está en curso.", + "already_configured": "Este dispositivo ya está configurado.", + "not_wiser_hub": "El dispositivo descubierto no es un Heat Hub Wiser." + }, + "error": { + "invalid_hot_water_presets": "Uno o más preajustes de agua caliente no son válidos.", + "auth_failure": "No se pudo autenticar con el hub Wiser. Compruebe la clave secreta y vuelva a intentarlo.", + "timeout_error": "Se agotó el tiempo de espera al conectarse al hub Wiser. Compruebe la dirección IP y vuelva a intentarlo.", + "timeout_error_discovery": "Se agotó el tiempo de espera al conectarse al hub Wiser. Compruebe que el hub siga en línea y vuelva a intentarlo.", + "unknown": "Se produjo un error desconocido al conectarse al hub Wiser. Consulte los registros." + } + }, + "options": { + "step": { + "init": { + "title": "Opciones de la integración Wiser", + "description": "Seleccione los parámetros que desea modificar", + "menu_options": { + "main_params": "Parámetros principales", + "automation_params": "Parámetros de automatización", + "opentherm": "OpenTherm", + "equipment_sensors": "Sensores de equipo", + "ui_options": "Opciones de la interfaz de usuario" + } + }, + "ui_options": { + "title": "Opciones de la interfaz de usuario", + "data": { + "legacy_naming": "Nomenclatura heredada", + "show_wiser_sidebar": "Mostrar Wiser en la barra lateral" + }, + "data_description": { + "legacy_naming": "Incluye los nombres de las habitaciones en los nombres de los dispositivos Wiser. Si se habilita, al usar Home Assistant para regenerar los ID de entidad con el formato predeterminado en Configuración → Sistema → Formato de ID de entidad (Home Assistant 2026.8 y posteriores), se duplicarán los nombres de área cuando coincidan con los nombres de las habitaciones Wiser. Cambiar esta opción no modifica los ID de entidad existentes." + } + }, + "main_params": { + "title": "Opciones de la integración Wiser", + "description": "Parámetros principales", + "data": { + "host": "Dirección IP o nombre de host", + "port": "Número de puerto", + "scan_interval": "Intervalo de actualización (segundos)", + "heating_boost_temp": "Temperatura de refuerzo de calefacción predeterminada (°C)", + "heating_boost_time": "Duración predeterminada del refuerzo de calefacción (minutos)", + "hotwater_boost_time": "Duración predeterminada del refuerzo de agua caliente (minutos)", + "setpoint_mode": "Modo de consigna", + "restore_manual_temp_option": "Usar esta temperatura si no hay ninguna temperatura manual almacenada" + } + }, + "automation_params": { + "title": "Opciones de la integración Wiser", + "description": "Habilitar automatizaciones integradas", + "sections": { + "automations_passive_mode": { + "name": "Modo pasivo", + "description": "Habilitar la capacidad TRV pasiva. Cada habitación necesita estar habilitada para su funcionamiento.", + "data": { + "automations_passive_mode": "Modo pasivo", + "passive_mode_temperature_increments": "Incrementos de temperatura en modo pasivo" + } + }, + "hotwater_climate": { + "name": "Control climático de agua caliente", + "description": "Habilita el control climático de agua caliente. Consulte la wiki para obtener más detalles.", + "data": { + "hotwater_climate": "Habilitar la entidad de climatización", + "experimental_mode": "Habilitar el modo de funcionamiento experimental", + "hotwater_auto_mode": "Modo automático de agua caliente", + "hotwater_heat_mode": "Modo de calentamiento de agua caliente", + "hotwater_boost_mode": "Modo de refuerzo de agua caliente", + "hotwater_sensor_entity_id": "Sensor de temperatura de agua caliente" + } + }, + "additional_sensors": { + "name": "Sensores adicionales", + "description": "Exponga sensores opcionales para los datos informados por Wiser.", + "data": { + "schedule_setpoint_rooms": "Habitaciones sin control de climatización" + }, + "data_description": { + "schedule_setpoint_rooms": "Seleccione las habitaciones Wiser en las que se debe crear un sensor de temperatura de la programación actual." + } + } + } + }, + "equipment_sensors": { + "title": "Sensores de equipos", + "description": "Configure entidades de datos detalladas de equipos para dispositivos compatibles.", + "data": { + "equipment_sensors": "Habilitar sensores de datos del equipo" + }, + "data_description": { + "equipment_sensors": "Expone un sensor adicional con atributos detallados del equipo para cada enchufe inteligente, PowerTag y actuador de calefacción compatible." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Configure las funciones de OpenTherm.", + "menu_options": { + "opentherm_hot_water_control": "Control de agua caliente", + "opentherm_sensors": "Sensores" + } + }, + "opentherm_sensors": { + "title": "Sensores OpenTherm", + "description": "Elija una categoría de atributos OpenTherm detectados.", + "menu_options": { + "opentherm_readings": "Lecturas", + "opentherm_central_heating": "Calefacción central", + "opentherm_hot_water": "Agua caliente", + "opentherm_status": "Estado y diagnóstico" + } + }, + "opentherm_hot_water_control": { + "title": "Control de agua caliente OpenTherm", + "description": "Controle opcionalmente la consigna de agua caliente sanitaria de la caldera a través de Wiser. La integración crea una programación OpenTherm Hot Water en el panel Wiser Schedules. Apagado usa la temperatura mínima de agua caliente informada por OpenTherm.", + "data": { + "opentherm_hot_water_control": "Habilitar el control de agua caliente", + "opentherm_hot_water_manual_temperature": "Temperatura manual", + "opentherm_hot_water_sensor_entity_id": "Sensor de temperatura externo opcional.", + "opentherm_hot_water_presets": "Preajustes" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperatura utilizada por el modo manual.", + "opentherm_hot_water_sensor_entity_id": "Sensor de temperatura opcional utilizado para la temperatura actual del agua. Se utiliza OpenTherm cuando el sensor no está disponible." + } + }, + "opentherm_readings": { + "title": "Lecturas OpenTherm", + "description": "Elija qué lecturas OpenTherm detectadas se muestran como entidades de sensor.", + "data": { + "ch_flow_temperature": "Temperatura de ida de la caldera", + "ch_pressure_bar": "Presión de calefacción", + "ch_return_temperature": "Temperatura de retorno de la caldera", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Temperatura de escape de la caldera", + "estimated_boiler_output": "Potencia estimada de la caldera", + "hw_flow_rate": "Caudal de agua caliente", + "hw_temperature": "Temperatura del agua caliente", + "maximum_capacity_kw": "Capacidad máxima de la caldera", + "minimum_modulation_level": "Nivel mínimo de modulación", + "relative_modulation_level": "Nivel relativo de modulación", + "room_temperature": "Temperatura ambiente" + } + }, + "opentherm_central_heating": { + "title": "Calefacción central OpenTherm", + "description": "Elija qué atributos de calefacción central detectados se exponen como entidades.", + "data": { + "boiler_ch_max_setpoint_read_write": "Lectura/escritura de la consigna máxima de calefacción de la caldera", + "boiler_ch_max_setpoint_transfer_enable": "Transferencia de la consigna máxima de calefacción de la caldera habilitada", + "boiler_ch_setpoint": "Consigna de calefacción de la caldera", + "boiler_ch_setpoint_lower_bound": "Límite inferior de la consigna de calefacción de la caldera", + "boiler_ch_setpoint_upper_bound": "Límite superior de la consigna de calefacción de la caldera", + "ch_flow_active_lower_setpoint": "Consigna inferior activa del caudal de calefacción", + "ch_flow_active_upper_setpoint": "Consigna superior activa del caudal de calefacción", + "ch1_flow_enabled": "Flujo CH1 habilitado", + "ch1_flow_setpoint": "Consigna de caudal CH1", + "ch2_flow_enabled": "Flujo CH2 habilitado", + "ch2_flow_setpoint": "Consigna de caudal CH2", + "room_setpoint": "Consigna de la habitación" + } + }, + "opentherm_hot_water": { + "title": "Agua caliente OpenTherm", + "description": "Elija qué atributos de agua caliente detectados se exponen como entidades.", + "data": { + "boiler_hw_setpoint": "Punto de consigna de agua caliente de la caldera", + "boiler_hw_setpoint_lower_bound": "Límite inferior del punto de consigna de agua caliente de la caldera", + "boiler_hw_setpoint_read_write": "Lectura/escritura del punto de consigna de agua caliente de la caldera", + "boiler_hw_setpoint_transfer_enable": "Transferencia del punto de consigna de agua caliente de la caldera habilitada", + "boiler_hw_setpoint_upper_bound": "Límite superior del punto de consigna de agua caliente de la caldera", + "hw_enabled": "Agua caliente habilitada", + "hw_flow_setpoint": "Consigna del caudal de agua caliente" + } + }, + "opentherm_status": { + "title": "Estado y diagnóstico de OpenTherm", + "description": "Elija qué estados detectados y atributos de diagnóstico se exponen como entidades de sensor.", + "data": { + "boiler_fault": "Fallo de la caldera", + "central_heating_active": "Calefacción central activa", + "central_heating_2_active": "Calefacción central 2 activa", + "connection_status": "Estado de conexión", + "coprocessor_update_status": "Estado de actualización del coprocesador OpenTherm", + "coprocessor_version": "Versión del coprocesador OpenTherm", + "cooling_active": "Enfriamiento activo", + "diagnostic_event": "Evento de diagnóstico", + "flame_active": "Llama activa", + "flame_statistics": "Estadísticas de llama", + "hot_water_active": "Agua caliente activa", + "operating_mode": "Modo de funcionamiento", + "slave_status": "Estado esclavo OpenTherm", + "tracked_room_id": "ID de habitación rastreada" + } + } + }, + "abort": { + "opentherm_not_available": "Los datos de OpenTherm no están disponibles para este concentrador Wiser.", + "equipment_not_available": "Los datos del equipo no están disponibles para este concentrador Wiser." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "Establece el parámetro OpenTherm", + "description": "Cambia un parámetro de temperatura OpenTherm compatible en el concentrador Wiser.", + "fields": { + "parameter": { + "name": "Parámetro", + "description": "Parámetro de temperatura OpenTherm que se desea cambiar. El endpoint de API necesario se selecciona automáticamente." + }, + "temperature": { + "name": "Temperatura", + "description": "Utilice una temperatura dentro de los límites informados por su caldera." + }, + "hub": { + "name": "Hub Wiser", + "description": "Seleccione un hub únicamente si tiene varios hubs Wiser." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Consigna inferior de calefacción central activa", + "chFlowActiveUpperSetpoint": "Consigna superior de calefacción central activa", + "dhwFlowSetpoint": "Consigna caudal de agua caliente del hub", + "dhwSetpoint": "Consigna de agua caliente de caldera", + "maxChSetpoint": "Consigna máxima de calefacción central de caldera" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Proporcione una temperatura OpenTherm." + }, + "opentherm_hub_not_found": { + "message": "No se encontró el concentrador Wiser especificado." + }, + "opentherm_hub_not_loaded": { + "message": "El concentrador Wiser especificado no está cargado." + }, + "opentherm_hub_required": { + "message": "Seleccione un concentrador Wiser cuando esté configurado más de un concentrador." + }, + "opentherm_not_supported": { + "message": "Este concentrador no tiene la funcionalidad OpenTherm." + }, + "opentherm_invalid_parameter": { + "message": "El parámetro o valor OpenTherm no es válido: {error}" + }, + "opentherm_write_failed": { + "message": "No se puede establecer el parámetro OpenTherm {parameter}: {error}" + }, + "opentherm_confirmation_failed": { + "message": "El parámetro OpenTherm {parameter} no se mantuvo en {requested} °C tras un reintento y el periodo de confirmación de {window} segundos. Wiser informa de {reported} °C. No se realizarán más reintentos." + }, + "opentherm_retry_failed": { + "message": "El parámetro OpenTherm {parameter} no se mantuvo en {requested} °C y no se pudo enviar el reintento: {error}. Wiser informa de {reported} °C. No se realizarán más reintentos." + }, + "opentherm_notification_title": { + "message": "Comando OpenTherm de Wiser" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Calefacción" + }, + "floor_temperature": { + "name": "Temperatura del suelo" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Temperatura objetivo del modo ausente" + }, + "floor_temperature_offset": { + "name": "Compensación de la temperatura del suelo" + }, + "indoor_discomfort_temperature": { + "name": "Temperatura de incomodidad interior" + }, + "outdoor_discomfort_temperature": { + "name": "Temperatura de incomodidad exterior" + } + }, + "select": { + "mode": { + "name": "Modo" + }, + "power_on_behavior": { + "name": "Comportamiento de encendido" + }, + "led_indicator": { + "name": "Indicador LED" + } + }, + "button": { + "boost_all_heating": { + "name": "Reforzar toda la calefacción" + }, + "cancel_all_heating_overrides": { + "name": "Cancelar todas las anulaciones de calefacción" + }, + "boost_hot_water": { + "name": "Reforzar el agua caliente" + }, + "cancel_hot_water_overrides": { + "name": "Cancelar anulaciones de agua caliente" + }, + "toggle_hot_water": { + "name": "Alternar agua caliente" + }, + "moment": { + "name": "Momento: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Protección de válvula" + }, + "eco_mode": { + "name": "Modo Eco" + }, + "away_mode_affects_hot_water": { + "name": "El modo Ausente afecta el agua caliente" + }, + "comfort_mode": { + "name": "Modo Confort" + }, + "away_mode": { + "name": "Modo Ausente" + }, + "daylight_saving": { + "name": "Horario de verano" + }, + "summer_comfort_enabled": { + "name": "Confort de verano habilitado" + }, + "window_detection": { + "name": "Detección de ventana" + }, + "include_in_summer_comfort": { + "name": "Incluir en el confort de verano" + }, + "device_lock": { + "name": "Bloqueo del dispositivo" + }, + "identify": { + "name": "Identificar" + }, + "outlet": { + "name": "Toma de corriente" + }, + "away_mode_turns_off": { + "name": "El modo Ausente apaga" + }, + "away_mode_closes": { + "name": "El modo Ausente cierra" + }, + "passive_mode": { + "name": "Modo pasivo" + }, + "respect_summer_comfort": { + "name": "Respetar el confort de verano" + }, + "interacts_with_room_climate": { + "name": "Interactúa con la climatización de la habitación" + }, + "humidity_interacts_with_room_climate": { + "name": "La humedad interactúa con la climatización de la habitación" + }, + "temperature_interacts_with_room_climate": { + "name": "La temperatura interactúa con la climatización de la habitación" + }, + "illuminance_interacts_with_room_climate": { + "name": "La iluminancia interactúa con la climatización de la habitación" + }, + "sensor_interacts_with_room_climate": { + "name": "{sensor} interactúa con la climatización de la habitación" + }, + "manual_heat": { + "name": "Calefacción manual" + }, + "hot_water": { + "name": "Agua caliente" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Prevención de molestias de verano" + }, + "summer_comfort_available": { + "name": "Confort de verano disponible" + }, + "pcm_device_limit_reached": { + "name": "Límite del dispositivo PCM alcanzado" + }, + "smoke_alarm": { + "name": "Alarma de humo" + }, + "heat_alarm": { + "name": "Alarma de calor" + }, + "tamper_alarm": { + "name": "Alarma de manipulación" + }, + "fault_warning": { + "name": "Advertencia de fallo" + }, + "remote_alarm": { + "name": "Alarma remota" + }, + "battery_defect": { + "name": "Defecto de batería" + }, + "controllable": { + "name": "Controlable" + }, + "pcm_mode": { + "name": "Modo PCM" + }, + "window_detection_active": { + "name": "Detección de ventana activa" + }, + "is_dimmable": { + "name": "Regulable" + }, + "is_led_indicator_supported": { + "name": "Indicador LED compatible" + }, + "is_output_mode_supported": { + "name": "Modo de salida compatible" + }, + "is_power_on_behaviour_supported": { + "name": "Comportamiento de encendido compatible" + }, + "is_tilt_supported": { + "name": "Inclinación compatible" + }, + "is_open": { + "name": "Abierto" + }, + "is_closed": { + "name": "Cerrado" + }, + "active": { + "name": "Activo" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Lectura/escritura del setpoint máximo de caldera" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Transferencia del setpoint máximo de caldera habilitada" + }, + "boiler_fault": { + "name": "Fallo de caldera" + }, + "boiler_hw_setpoint_read_write": { + "name": "Lectura/escritura de setpoint de agua caliente de caldera" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Transferencia de la consigna de agua caliente de la caldera habilitada" + }, + "central_heating_2_active": { + "name": "Calefacción central 2 activa" + }, + "central_heating_active": { + "name": "Calefacción central activa" + }, + "ch1_flow_enabled": { + "name": "Flujo CH1 habilitado" + }, + "ch2_flow_enabled": { + "name": "Flujo CH2 habilitado" + }, + "cooling_active": { + "name": "Refrigeración activa" + }, + "diagnostic_event": { + "name": "Evento de diagnóstico" + }, + "flame_active": { + "name": "Llama activa" + }, + "hot_water_active": { + "name": "Agua caliente activa" + }, + "hw_enabled": { + "name": "Agua caliente habilitada" + } + }, + "sensor": { + "signal": { + "name": "Señal" + }, + "cloud": { + "name": "Nube" + }, + "heating_operation_mode": { + "name": "Modo de funcionamiento de la calefacción" + }, + "heating": { + "name": "Calefacción", + "state": { + "off": "Apagado", + "on": "Encendido" + } + }, + "heating_channel": { + "name": "Canal de calefacción {channel}", + "state": { + "off": "Apagado", + "on": "Encendido" + } + }, + "hot_water": { + "name": "Agua caliente", + "state": { + "off": "Apagado", + "on": "Encendido" + } + }, + "hot_water_operation_mode": { + "name": "Modo de funcionamiento del agua caliente" + }, + "target_temperature": { + "name": "Temperatura objetivo" + }, + "floor_temperature": { + "name": "Temperatura del suelo" + }, + "boiler_flow_temperature": { + "name": "Temperatura de ida de la caldera" + }, + "boiler_return_temperature": { + "name": "Temperatura de retorno de la caldera" + }, + "flame_statistics": { + "name": "Estadísticas de llama" + }, + "boiler_ch_setpoint": { + "name": "Consigna de calefacción de la caldera" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Límite inferior de la consigna de calefacción de la caldera" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Límite superior de la consigna de calefacción de la caldera" + }, + "boiler_exhaust_temperature": { + "name": "Temperatura de escape de la caldera" + }, + "boiler_hw_setpoint": { + "name": "Punto de consigna de agua caliente de la caldera" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Límite inferior del punto de consigna de agua caliente de la caldera" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Límite superior del punto de consigna de agua caliente de la caldera" + }, + "ch1_flow_setpoint": { + "name": "Consigna de caudal CH1" + }, + "ch2_flow_setpoint": { + "name": "Consigna de caudal CH2" + }, + "ch_flow_active_lower_setpoint": { + "name": "Consigna inferior activa del caudal de calefacción" + }, + "ch_flow_active_upper_setpoint": { + "name": "Consigna superior activa del caudal de calefacción" + }, + "ch_pressure_bar": { + "name": "Presión de CH" + }, + "connection_status": { + "name": "Estado de conexión" + }, + "coprocessor_update_status": { + "name": "Estado de actualización del coprocesador OpenTherm" + }, + "coprocessor_version": { + "name": "Versión del coprocesador OpenTherm" + }, + "delta_t": { + "name": "Delta-T" + }, + "estimated_boiler_output": { + "name": "Potencia estimada de la caldera" + }, + "hw_flow_rate": { + "name": "Caudal de agua caliente" + }, + "hw_flow_setpoint": { + "name": "Punto de ajuste del caudal de agua caliente" + }, + "hw_temperature": { + "name": "Temperatura del agua caliente" + }, + "maximum_capacity_kw": { + "name": "Capacidad máxima de la caldera" + }, + "minimum_modulation_level": { + "name": "Nivel mínimo de modulación" + }, + "operating_mode": { + "name": "Modo de funcionamiento" + }, + "relative_modulation_level": { + "name": "Nivel de modulación relativo" + }, + "room_setpoint": { + "name": "Consigna ambiente" + }, + "room_temperature": { + "name": "Temperatura ambiente" + }, + "slave_status": { + "name": "Estado esclavo OpenTherm" + }, + "tracked_room_id": { + "name": "ID de habitación rastreada" + }, + "heating_demand": { + "name": "Demanda de calefacción" + }, + "heating_demand_channel": { + "name": "Canal de demanda de calefacción {channel}" + }, + "hot_water_demand": { + "name": "Demanda de agua caliente" + }, + "total_energy": { + "name": "Energía total" + }, + "energy_delivered": { + "name": "Energía entregada" + }, + "energy_received": { + "name": "Energía recibida" + }, + "equipment_power": { + "name": "Equipo" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} inicia la calefacción", + "stopped_heating": "{entity_name} deja de calentar", + "boosted": "Se ha reforzado {entity_name}", + "target_temperature_increased": "Ha aumentado la temperatura objetivo de {entity_name}", + "target_temperature_decreased": "Ha disminuido la temperatura objetivo de {entity_name}" + } + } +} diff --git a/custom_components/wiser/translations/fi.json b/custom_components/wiser/translations/fi.json new file mode 100644 index 00000000..7d66bad2 --- /dev/null +++ b/custom_components/wiser/translations/fi.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Wiser Heat Hubin määritys", + "description": "Anna keskittimestä saatu IP-osoite ja salainen avain.", + "data": { + "host": "IP-osoite tai isäntänimi", + "port": "Portin numero", + "password": "Salainen avain" + } + }, + "zeroconf_confirm": { + "title": "Wiser Heat Hubin määritys ({name})", + "description": "Anna keskittimestä saatu salainen avain.", + "data": { + "host": "IP-osoite tai isäntänimi", + "port": "Portin numero", + "password": "Salainen avain" + } + } + }, + "abort": { + "already_in_progress": "Wiser Heat Hubin määritys on jo käynnissä.", + "already_configured": "Tämä laite on jo määritetty.", + "not_wiser_hub": "Löydetty laite ei ole Wiser Heat Hub." + }, + "error": { + "invalid_hot_water_presets": "Yksi tai useampi käyttöveden esiasetus on virheellinen.", + "auth_failure": "Todennus Wiser-keskittimeen epäonnistui. Tarkista salainen avain ja yritä uudelleen.", + "timeout_error": "Yhteys Wiser-keskittimeen aikakatkaistiin. Tarkista IP-osoite ja yritä uudelleen.", + "timeout_error_discovery": "Yhteys Wiser-keskittimeen aikakatkaistiin. Tarkista, että keskitin on yhä verkossa, ja yritä uudelleen.", + "unknown": "Tuntematon virhe yhdistettäessä Wiser-keskittimeen. Tarkista lokit." + } + }, + "options": { + "step": { + "init": { + "title": "Wiser-integraation asetukset", + "description": "Valitse muutettavat parametrit", + "menu_options": { + "main_params": "Pääparametrit", + "automation_params": "Automaatioparametrit", + "opentherm": "OpenTherm", + "equipment_sensors": "Laiteanturit", + "ui_options": "Käyttöliittymän asetukset" + } + }, + "ui_options": { + "title": "Käyttöliittymän asetukset", + "data": { + "legacy_naming": "Vanha nimeämistapa", + "show_wiser_sidebar": "Näytä Wiser sivupalkissa" + }, + "data_description": { + "legacy_naming": "Sisällytä huoneiden nimet Wiser-laitteiden nimiin. Jos tämä on käytössä, entiteettitunnusten luominen uudelleen Home Assistantin oletusmuodolla kohdassa Asetukset → Järjestelmä → Entiteettitunnuksen muoto (Home Assistant 2026.8 ja uudemmat) kahdentaa alueiden nimet, kun alueen ja Wiser-huoneen nimet ovat samat. Tämän asetuksen muuttaminen ei nimeä nykyisiä entiteettitunnuksia uudelleen." + } + }, + "main_params": { + "title": "Wiser-integraation asetukset", + "description": "Pääparametrit", + "data": { + "host": "IP-osoite tai isäntänimi", + "port": "Portin numero", + "scan_interval": "Päivitysväli (s)", + "heating_boost_temp": "Lämmitystehostuksen oletuslämpötila (°C)", + "heating_boost_time": "Lämmitystehostuksen oletuskesto (min)", + "hotwater_boost_time": "Käyttövesitehostuksen oletuskesto (min)", + "setpoint_mode": "Asetusarvotila", + "restore_manual_temp_option": "Käytä tätä lämpötilaa, jos manuaalista lämpötilaa ei ole tallennettu" + } + }, + "automation_params": { + "title": "Wiser-integraation asetukset", + "description": "Ota sisäiset automaatiot käyttöön", + "sections": { + "automations_passive_mode": { + "name": "Passiivinen tila", + "description": "Ota passiivinen TRV-toiminto käyttöön. Jokainen huone on otettava erikseen käyttöön.", + "data": { + "automations_passive_mode": "Passiivinen tila", + "passive_mode_temperature_increments": "Passiivisen tilan lämpötilaportaat" + } + }, + "hotwater_climate": { + "name": "Käyttöveden ilmastointiohjaus", + "description": "Ota käyttöveden ilmastointiohjaus käyttöön. Katso lisätietoja wikistä.", + "data": { + "hotwater_climate": "Ota ilmastointientiteetti käyttöön", + "experimental_mode": "Ota kokeellinen toimintatila käyttöön", + "hotwater_auto_mode": "Käyttöveden automaattitila", + "hotwater_heat_mode": "Käyttöveden lämmitystila", + "hotwater_boost_mode": "Käyttöveden tehostustila", + "hotwater_sensor_entity_id": "Käyttöveden lämpötila-anturi" + } + }, + "additional_sensors": { + "name": "Lisäanturit", + "description": "Tuo näkyviin valinnaisia antureita Wiserin ilmoittamille tiedoille.", + "data": { + "schedule_setpoint_rooms": "Huoneet ilman ilmastointiohjausta" + }, + "data_description": { + "schedule_setpoint_rooms": "Valitse Wiser-huoneet, joihin luodaan nykyisen aikataulun lämpötila-anturi." + } + } + } + }, + "equipment_sensors": { + "title": "Laiteanturit", + "description": "Määritä yksityiskohtaiset laitetietoentiteetit yhteensopiville laitteille.", + "data": { + "equipment_sensors": "Ota laitetietoanturit käyttöön" + }, + "data_description": { + "equipment_sensors": "Näytä lisäanturi, joka sisältää yksityiskohtaiset laiteominaisuudet jokaiselle yhteensopivalle älypistorasialle, PowerTagille ja lämmitystoimilaitteelle." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Määritä OpenTherm-ominaisuudet.", + "menu_options": { + "opentherm_hot_water_control": "Käyttöveden ohjaus", + "opentherm_sensors": "Anturit" + } + }, + "opentherm_sensors": { + "title": "OpenTherm-anturit", + "description": "Valitse havaittujen OpenTherm-ominaisuuksien luokka.", + "menu_options": { + "opentherm_readings": "Lukemat", + "opentherm_central_heating": "Keskuslämmitys", + "opentherm_hot_water": "Käyttövesi", + "opentherm_status": "Tila ja diagnostiikka" + } + }, + "opentherm_hot_water_control": { + "title": "OpenTherm-käyttöveden ohjaus", + "description": "Ohjaa valinnaisesti kattilan käyttöveden asetusarvoa Wiserin kautta. Integraatio luo OpenTherm Hot Water -aikataulun Wiser Schedules -paneeliin. Pois-tila käyttää OpenThermin ilmoittamaa käyttöveden vähimmäislämpötilaa.", + "data": { + "opentherm_hot_water_control": "Ota käyttöveden ohjaus käyttöön", + "opentherm_hot_water_manual_temperature": "Manuaalinen lämpötila", + "opentherm_hot_water_sensor_entity_id": "Valinnainen ulkoinen lämpötila-anturi.", + "opentherm_hot_water_presets": "Esiasetukset" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Manuaalitilassa käytettävä lämpötila.", + "opentherm_hot_water_sensor_entity_id": "Valinnainen lämpötila-anturi, jota käytetään nykyiselle veden lämpötilalle. OpenThermiä käytetään, kun anturi ei ole käytettävissä." + } + }, + "opentherm_readings": { + "title": "OpenTherm-lukemat", + "description": "Valitse, mitkä havaitut OpenTherm-lukemat näytetään anturientiteetteinä.", + "data": { + "ch_flow_temperature": "Kattilan menoveden lämpötila", + "ch_pressure_bar": "Keskuslämmityksen paine", + "ch_return_temperature": "Kattilan paluuveden lämpötila", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Kattilan savukaasun lämpötila", + "estimated_boiler_output": "Kattilan arvioitu teho", + "hw_flow_rate": "Käyttöveden virtausnopeus", + "hw_temperature": "Käyttöveden lämpötila", + "maximum_capacity_kw": "Kattilan enimmäisteho", + "minimum_modulation_level": "Modulaation vähimmäistaso", + "relative_modulation_level": "Suhteellinen modulaatiotaso", + "room_temperature": "Huonelämpötila" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm-keskuslämmitys", + "description": "Valitse, mitkä havaitut keskuslämmityksen ominaisuudet näytetään entiteetteinä.", + "data": { + "boiler_ch_max_setpoint_read_write": "Kattilan keskuslämmityksen enimmäisasetusarvon luku/kirjoitus", + "boiler_ch_max_setpoint_transfer_enable": "Kattilan keskuslämmityksen enimmäisasetusarvon siirto käytössä", + "boiler_ch_setpoint": "Kattilan keskuslämmityksen asetusarvo", + "boiler_ch_setpoint_lower_bound": "Kattilan keskuslämmityksen asetusarvon alaraja", + "boiler_ch_setpoint_upper_bound": "Kattilan keskuslämmityksen asetusarvon yläraja", + "ch_flow_active_lower_setpoint": "Keskuslämmityksen menoveden aktiivinen ala-asetusarvo", + "ch_flow_active_upper_setpoint": "Keskuslämmityksen menoveden aktiivinen yläasetusarvo", + "ch1_flow_enabled": "Keskuslämmityspiirin 1 meno käytössä", + "ch1_flow_setpoint": "Keskuslämmityspiirin 1 menoveden asetusarvo", + "ch2_flow_enabled": "Keskuslämmityspiirin 2 meno käytössä", + "ch2_flow_setpoint": "Keskuslämmityspiirin 2 menoveden asetusarvo", + "room_setpoint": "Huoneen asetusarvo" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm-käyttövesi", + "description": "Valitse, mitkä havaitut käyttöveden ominaisuudet näytetään entiteetteinä.", + "data": { + "boiler_hw_setpoint": "Kattilan käyttöveden asetusarvo", + "boiler_hw_setpoint_lower_bound": "Kattilan käyttöveden asetusarvon alaraja", + "boiler_hw_setpoint_read_write": "Kattilan käyttöveden asetusarvon luku/kirjoitus", + "boiler_hw_setpoint_transfer_enable": "Kattilan käyttöveden asetusarvon siirto käytössä", + "boiler_hw_setpoint_upper_bound": "Kattilan käyttöveden asetusarvon yläraja", + "hw_enabled": "Käyttövesi käytössä", + "hw_flow_setpoint": "Käyttöveden menoveden asetusarvo" + } + }, + "opentherm_status": { + "title": "OpenTherm-tila ja diagnostiikka", + "description": "Valitse, mitkä havaitut tila- ja diagnostiikkaominaisuudet näytetään anturientiteetteinä.", + "data": { + "boiler_fault": "Kattilavika", + "central_heating_active": "Keskuslämmitys aktiivinen", + "central_heating_2_active": "Keskuslämmitys 2 aktiivinen", + "connection_status": "Yhteyden tila", + "coprocessor_update_status": "OpenTherm-apuprosessorin päivitystila", + "coprocessor_version": "OpenTherm-apuprosessorin versio", + "cooling_active": "Jäähdytys aktiivinen", + "diagnostic_event": "Diagnostiikkatapahtuma", + "flame_active": "Liekki aktiivinen", + "flame_statistics": "Liekkitilastot", + "hot_water_active": "Käyttövesi aktiivinen", + "operating_mode": "Toimintatila", + "slave_status": "OpenTherm-orjalaitteen tila", + "tracked_room_id": "Seurattavan huoneen tunnus" + } + } + }, + "abort": { + "opentherm_not_available": "OpenTherm-tietoja ei ole saatavilla tälle Wiser-keskittimelle.", + "equipment_not_available": "Laitetietoja ei ole saatavilla tälle Wiser-keskittimelle." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "Aseta OpenTherm-parametri", + "description": "Muuttaa tuettua OpenTherm-lämpötilaparametria Wiser-keskittimessä.", + "fields": { + "parameter": { + "name": "Parametri", + "description": "Muutettava OpenTherm-lämpötilaparametri. Tarvittava API-päätepiste valitaan automaattisesti." + }, + "temperature": { + "name": "Lämpötila", + "description": "Käytä kattilan ilmoittamissa rajoissa olevaa lämpötilaa." + }, + "hub": { + "name": "Wiser-keskitin", + "description": "Valitse keskitin vain, jos käytössä on useita Wiser-keskittimiä." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Keskuslämmityksen aktiivinen ala-asetusarvo", + "chFlowActiveUpperSetpoint": "Keskuslämmityksen aktiivinen yläasetusarvo", + "dhwFlowSetpoint": "Keskittimen käyttöveden menoveden asetusarvo", + "dhwSetpoint": "Kattilan käyttöveden asetusarvo", + "maxChSetpoint": "Kattilan keskuslämmityksen enimmäisasetusarvo" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Anna OpenTherm-lämpötila." + }, + "opentherm_hub_not_found": { + "message": "Määritettyä Wiser-keskitintä ei löytynyt." + }, + "opentherm_hub_not_loaded": { + "message": "Määritettyä Wiser-keskitintä ei ole ladattu." + }, + "opentherm_hub_required": { + "message": "Valitse Wiser-keskitin, kun useampi kuin yksi keskitin on määritetty." + }, + "opentherm_not_supported": { + "message": "Tässä keskittimessä ei ole OpenTherm-toimintoa." + }, + "opentherm_invalid_parameter": { + "message": "OpenTherm-parametri tai arvo on virheellinen: {error}" + }, + "opentherm_write_failed": { + "message": "OpenTherm-parametria {parameter} ei voitu asettaa: {error}" + }, + "opentherm_confirmation_failed": { + "message": "OpenTherm-parametri {parameter} ei pysynyt arvossa {requested} °C yhden uusintayrityksen ja {window} sekunnin vahvistusjakson jälkeen. Wiser ilmoittaa {reported} °C. Uusia yrityksiä ei lähetetä." + }, + "opentherm_retry_failed": { + "message": "OpenTherm-parametri {parameter} ei pysynyt arvossa {requested} °C eikä uusintayritystä voitu lähettää: {error}. Wiser ilmoittaa {reported} °C. Uusia yrityksiä ei lähetetä." + }, + "opentherm_notification_title": { + "message": "Wiser OpenTherm -komento" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Lämmitys" + }, + "floor_temperature": { + "name": "Lattialämpötila" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Poissa-tilan tavoitelämpötila" + }, + "floor_temperature_offset": { + "name": "Lattialämpötilan poikkeama" + }, + "indoor_discomfort_temperature": { + "name": "Sisätilojen epämukavuuslämpötila" + }, + "outdoor_discomfort_temperature": { + "name": "Ulkoilman epämukavuuslämpötila" + } + }, + "select": { + "mode": { + "name": "Tila" + }, + "power_on_behavior": { + "name": "Käynnistyskäyttäytyminen" + }, + "led_indicator": { + "name": "LED-merkkivalo" + } + }, + "button": { + "boost_all_heating": { + "name": "Tehosta kaikkea lämmitystä" + }, + "cancel_all_heating_overrides": { + "name": "Peruuta kaikki lämmityksen ohitukset" + }, + "boost_hot_water": { + "name": "Tehosta käyttövettä" + }, + "cancel_hot_water_overrides": { + "name": "Peruuta kaikki käyttöveden ohitukset" + }, + "toggle_hot_water": { + "name": "Vaihda käyttöveden tila" + }, + "moment": { + "name": "Hetki: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Venttiilin suojaus" + }, + "eco_mode": { + "name": "Säästötila" + }, + "away_mode_affects_hot_water": { + "name": "Poissa-tila vaikuttaa käyttöveteen" + }, + "comfort_mode": { + "name": "Mukavuustila" + }, + "away_mode": { + "name": "Poissa-tila" + }, + "daylight_saving": { + "name": "Kesäaika" + }, + "summer_comfort_enabled": { + "name": "Kesämukavuus käytössä" + }, + "window_detection": { + "name": "Ikkunantunnistus" + }, + "include_in_summer_comfort": { + "name": "Sisällytä kesämukavuuteen" + }, + "device_lock": { + "name": "Laitelukitus" + }, + "identify": { + "name": "Tunnista" + }, + "outlet": { + "name": "Pistorasia" + }, + "away_mode_turns_off": { + "name": "Poissa-tila sammuttaa" + }, + "away_mode_closes": { + "name": "Poissa-tila sulkee" + }, + "passive_mode": { + "name": "Passiivinen tila" + }, + "respect_summer_comfort": { + "name": "Noudata kesämukavuutta" + }, + "interacts_with_room_climate": { + "name": "Vaikuttaa huoneilmastoon" + }, + "humidity_interacts_with_room_climate": { + "name": "Ilmankosteus vaikuttaa huoneilmastoon" + }, + "temperature_interacts_with_room_climate": { + "name": "Lämpötila vaikuttaa huoneilmastoon" + }, + "illuminance_interacts_with_room_climate": { + "name": "Valaistusvoimakkuus vaikuttaa huoneilmastoon" + }, + "sensor_interacts_with_room_climate": { + "name": "{sensor} vaikuttaa huoneilmastoon" + }, + "manual_heat": { + "name": "Manuaalinen lämmitys" + }, + "hot_water": { + "name": "Käyttövesi" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Kesäajan epämukavuuden esto" + }, + "summer_comfort_available": { + "name": "Kesämukavuus saatavilla" + }, + "pcm_device_limit_reached": { + "name": "PCM-laiteraja saavutettu" + }, + "smoke_alarm": { + "name": "Palovaroitin" + }, + "heat_alarm": { + "name": "Lämpöhälytys" + }, + "tamper_alarm": { + "name": "Peukalointihälytys" + }, + "fault_warning": { + "name": "Vikavaroitus" + }, + "remote_alarm": { + "name": "Etähälytys" + }, + "battery_defect": { + "name": "Akkuvika" + }, + "controllable": { + "name": "Ohjattavissa" + }, + "pcm_mode": { + "name": "PCM-tila" + }, + "window_detection_active": { + "name": "Ikkunantunnistus aktiivinen" + }, + "is_dimmable": { + "name": "Himmennettävä" + }, + "is_led_indicator_supported": { + "name": "LED-merkkivaloa tuetaan" + }, + "is_output_mode_supported": { + "name": "Lähtötilaa tuetaan" + }, + "is_power_on_behaviour_supported": { + "name": "Käynnistyskäyttäytymistä tuetaan" + }, + "is_tilt_supported": { + "name": "Kallistusta tuetaan" + }, + "is_open": { + "name": "Auki" + }, + "is_closed": { + "name": "Suljettu" + }, + "active": { + "name": "Aktiivinen" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Kattilan keskuslämmityksen enimmäisasetusarvon luku/kirjoitus" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Kattilan keskuslämmityksen enimmäisasetusarvon siirto käytössä" + }, + "boiler_fault": { + "name": "Kattilavika" + }, + "boiler_hw_setpoint_read_write": { + "name": "Kattilan käyttöveden asetusarvon luku/kirjoitus" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Kattilan käyttöveden asetusarvon siirto käytössä" + }, + "central_heating_2_active": { + "name": "Keskuslämmitys 2 aktiivinen" + }, + "central_heating_active": { + "name": "Keskuslämmitys aktiivinen" + }, + "ch1_flow_enabled": { + "name": "Keskuslämmityspiirin 1 meno käytössä" + }, + "ch2_flow_enabled": { + "name": "Keskuslämmityspiirin 2 meno käytössä" + }, + "cooling_active": { + "name": "Jäähdytys aktiivinen" + }, + "diagnostic_event": { + "name": "Diagnostiikkatapahtuma" + }, + "flame_active": { + "name": "Liekki aktiivinen" + }, + "hot_water_active": { + "name": "Käyttövesi aktiivinen" + }, + "hw_enabled": { + "name": "Käyttövesi käytössä" + } + }, + "sensor": { + "signal": { + "name": "Signaali" + }, + "cloud": { + "name": "Pilvi" + }, + "heating_operation_mode": { + "name": "Lämmityksen toimintatila" + }, + "heating": { + "name": "Lämmitys", + "state": { + "off": "Pois", + "on": "Päällä" + } + }, + "heating_channel": { + "name": "Lämmityskanava {channel}", + "state": { + "off": "Pois", + "on": "Päällä" + } + }, + "hot_water": { + "name": "Käyttövesi", + "state": { + "off": "Pois", + "on": "Päällä" + } + }, + "hot_water_operation_mode": { + "name": "Käyttöveden toimintatila" + }, + "target_temperature": { + "name": "Tavoitelämpötila" + }, + "floor_temperature": { + "name": "Lattialämpötila" + }, + "boiler_flow_temperature": { + "name": "Kattilan menoveden lämpötila" + }, + "boiler_return_temperature": { + "name": "Kattilan paluuveden lämpötila" + }, + "flame_statistics": { + "name": "Liekkitilastot" + }, + "boiler_ch_setpoint": { + "name": "Kattilan keskuslämmityksen asetusarvo" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Kattilan keskuslämmityksen asetusarvon alaraja" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Kattilan keskuslämmityksen asetusarvon yläraja" + }, + "boiler_exhaust_temperature": { + "name": "Kattilan savukaasun lämpötila" + }, + "boiler_hw_setpoint": { + "name": "Kattilan käyttöveden asetusarvo" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Kattilan käyttöveden asetusarvon alaraja" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Kattilan käyttöveden asetusarvon yläraja" + }, + "ch1_flow_setpoint": { + "name": "Keskuslämmityspiirin 1 menoveden asetusarvo" + }, + "ch2_flow_setpoint": { + "name": "Keskuslämmityspiirin 2 menoveden asetusarvo" + }, + "ch_flow_active_lower_setpoint": { + "name": "Keskuslämmityksen menoveden aktiivinen ala-asetusarvo" + }, + "ch_flow_active_upper_setpoint": { + "name": "Keskuslämmityksen menoveden aktiivinen yläasetusarvo" + }, + "ch_pressure_bar": { + "name": "Keskuslämmityksen paine" + }, + "connection_status": { + "name": "Yhteyden tila" + }, + "coprocessor_update_status": { + "name": "OpenTherm-apuprosessorin päivitystila" + }, + "coprocessor_version": { + "name": "OpenTherm-apuprosessorin versio" + }, + "delta_t": { + "name": "Delta-T" + }, + "estimated_boiler_output": { + "name": "Kattilan arvioitu teho" + }, + "hw_flow_rate": { + "name": "Käyttöveden virtausnopeus" + }, + "hw_flow_setpoint": { + "name": "Käyttöveden menoveden asetusarvo" + }, + "hw_temperature": { + "name": "Käyttöveden lämpötila" + }, + "maximum_capacity_kw": { + "name": "Kattilan enimmäisteho" + }, + "minimum_modulation_level": { + "name": "Modulaation vähimmäistaso" + }, + "operating_mode": { + "name": "Toimintatila" + }, + "relative_modulation_level": { + "name": "Suhteellinen modulaatiotaso" + }, + "room_setpoint": { + "name": "Huoneen asetusarvo" + }, + "room_temperature": { + "name": "Huonelämpötila" + }, + "slave_status": { + "name": "OpenTherm-orjalaitteen tila" + }, + "tracked_room_id": { + "name": "Seurattavan huoneen tunnus" + }, + "heating_demand": { + "name": "Lämmitystarve" + }, + "heating_demand_channel": { + "name": "Lämmitystarvekanava {channel}" + }, + "hot_water_demand": { + "name": "Käyttöveden tarve" + }, + "total_energy": { + "name": "Kokonaisenergia" + }, + "energy_delivered": { + "name": "Toimitettu energia" + }, + "energy_received": { + "name": "Vastaanotettu energia" + }, + "equipment_power": { + "name": "Laitteisto" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} aloittaa lämmityksen", + "stopped_heating": "{entity_name} lopettaa lämmityksen", + "boosted": "{entity_name} on tehostettu", + "target_temperature_increased": "Entiteetin {entity_name} tavoitelämpötilaa nostettiin", + "target_temperature_decreased": "Entiteetin {entity_name} tavoitelämpötilaa laskettiin" + } + } +} diff --git a/custom_components/wiser/translations/fr.json b/custom_components/wiser/translations/fr.json index 4d9a6475..818fe24e 100644 --- a/custom_components/wiser/translations/fr.json +++ b/custom_components/wiser/translations/fr.json @@ -4,20 +4,20 @@ "step": { "user": { "title": "Configuration de la Box Wiser", - "description": "Entrez l'adresse IP et la clé secrète SVP.", + "description": "Veuillez saisir l’adresse IP et la clé secrète obtenue auprès de la box Wiser.", "data": { - "host": "Adresse IP ou nom d'hote", + "host": "Adresse IP ou nom d’hôte", "port": "Numéro de port", - "password": "Clé Secrète" + "password": "Clé secrète" } }, "zeroconf_confirm": { "title": "Configuration de la Box Wiser ({name})", "description": "Entrez la clé secrète obtenue de la Box Wiser.", "data": { - "host": "Adresse IP ou nom d'hote", + "host": "Adresse IP ou nom d’hôte", "port": "Numéro de port", - "password": "Clé Secrète" + "password": "Clé secrète" } } }, @@ -27,39 +27,52 @@ "not_wiser_hub": "L’appareil détecté n’est pas une passerelle Wiser." }, "error": { - "auth_failure": "Authentification après de la Wiser Box impossible. Controlez la clé secrète et rééssayez.", - "timeout_error": "Timed out de la connection à la Box Wiser. Controlez l'adresse IP et rééssayez.", - "timeout_error_discovery": "Timed out de la connection à la Box Wiser. Vérifiez que votre Box Wiser est toujours en ligne et rééssayer.", - "unknown": "Erreur inconnue lors de la connexion à la Box Wiser. Controlez les logs, SVP." + "invalid_hot_water_presets": "Un ou plusieurs préréglages d’eau chaude sont invalides.", + "auth_failure": "Impossible de s’authentifier auprès de la box Wiser. Vérifiez la clé secrète et réessayez.", + "timeout_error": "Le délai de connexion à la box Wiser a expiré. Vérifiez l’adresse IP et réessayez.", + "timeout_error_discovery": "Le délai de connexion à la box Wiser a expiré. Vérifiez que votre box Wiser est toujours en ligne et réessayez.", + "unknown": "Erreur inconnue lors de la connexion à la box Wiser. Veuillez consulter les journaux." } }, "options": { "step": { "init": { - "title": "Wiser possibilités d'intégration", + "title": "Options de l’intégration Wiser", "description": "Réglages des paramètres Wiser.", "menu_options": { "main_params": "Paramètres principaux", "automation_params": "Paramètres d'automatisation", - "opentherm_sensors": "Capteurs OpenTherm" + "opentherm": "OpenTherm", + "equipment_sensors": "Capteurs d’équipement", + "ui_options": "Options de l’interface" + } + }, + "ui_options": { + "title": "Options de l’interface", + "data": { + "legacy_naming": "Nommage historique", + "show_wiser_sidebar": "Afficher Wiser dans la barre latérale" + }, + "data_description": { + "legacy_naming": "Inclure le nom des pièces dans le nom des appareils Wiser. Si cette option est activée, la régénération des identifiants d’entité avec le format par défaut dans Paramètres → Système → Format des identifiants d’entité (Home Assistant 2026.8 et versions ultérieures) répétera le nom de la zone si celui-ci correspond au nom de la pièce Wiser. Cette option ne renomme pas les identifiants d’entité existants." } }, "main_params": { - "title": "Wiser possibilités d'intégration", + "title": "Options de l’intégration Wiser", "description": "Paramètres principaux", "data": { - "host": "Adresse IP ou nom d'hote", + "host": "Adresse IP ou nom d’hôte", "port": "Numéro de port", - "scan_interval": " Période d'acquisition", - "heating_boost_temp": "Défault temperature de boost chauffage", - "heating_boost_time": "Défault durée de boost chauffage", - "hotwater_boost_time": "Défault durée de boost eau chaude", - "setpoint_mode": "Mode de fonctionnement consigne", - "restore_manual_temp_option": "Utilisez cette température si aucune température manuelle stockée" + "scan_interval": "Intervalle d’actualisation (s)", + "heating_boost_temp": "Température de boost du chauffage par défaut (°C)", + "heating_boost_time": "Durée de boost du chauffage par défaut (min)", + "hotwater_boost_time": "Durée de boost de l’eau chaude par défaut (min)", + "setpoint_mode": "Mode de consigne", + "restore_manual_temp_option": "Utiliser cette température si aucune température manuelle n’est enregistrée" } }, "automation_params": { - "title": "Wiser possibilités d'intégration", + "title": "Options de l’intégration Wiser", "description": "Activer les automatisations intégrées", "sections": { "automations_passive_mode": { @@ -81,9 +94,37 @@ "hotwater_boost_mode": "Mode boost de l’eau chaude", "hotwater_sensor_entity_id": "Capteur de température de l’eau chaude" } + }, + "additional_sensors": { + "name": "Capteurs supplémentaires", + "description": "Exposer des capteurs facultatifs pour les données communiquées par Wiser.", + "data": { + "schedule_setpoint_rooms": "Pièces sans contrôle climatique" + }, + "data_description": { + "schedule_setpoint_rooms": "Sélectionnez les pièces Wiser pour lesquelles un capteur de température programmée actuelle doit être créé." + } } } }, + "equipment_sensors": { + "title": "Capteurs d’équipement", + "description": "Configurez les entités de données détaillées des équipements compatibles.", + "data": { + "equipment_sensors": "Activer les capteurs de données des équipements" + }, + "data_description": { + "equipment_sensors": "Expose un capteur supplémentaire contenant les attributs détaillés de l’équipement pour chaque prise intelligente, PowerTag et actionneur de chauffage compatible." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Configurer les fonctions OpenTherm.", + "menu_options": { + "opentherm_hot_water_control": "Commande d’eau chaude", + "opentherm_sensors": "Capteurs" + } + }, "opentherm_sensors": { "title": "Capteurs OpenTherm", "description": "Choisissez une catégorie d’attributs OpenTherm détectés.", @@ -94,6 +135,20 @@ "opentherm_status": "État et diagnostics" } }, + "opentherm_hot_water_control": { + "title": "Commande d’eau chaude OpenTherm", + "description": "Contrôle facultativement la consigne d’eau chaude sanitaire de la chaudière via Wiser. L’intégration crée le planning « OpenTherm Hot Water » dans le panneau des plannings Wiser. Le mode Arrêt utilise la température minimale d’eau chaude indiquée par OpenTherm.", + "data": { + "opentherm_hot_water_control": "Activer la commande d’eau chaude", + "opentherm_hot_water_manual_temperature": "Température manuelle", + "opentherm_hot_water_sensor_entity_id": "Capteur de température externe facultatif.", + "opentherm_hot_water_presets": "Préréglages" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Température utilisée en mode manuel.", + "opentherm_hot_water_sensor_entity_id": "Capteur de température facultatif utilisé pour la température actuelle de l’eau. OpenTherm est utilisé lorsque le capteur est indisponible." + } + }, "opentherm_readings": { "title": "Mesures OpenTherm", "description": "Choisissez les mesures OpenTherm détectées à exposer en tant qu’entités de capteur.", @@ -140,7 +195,7 @@ "boiler_hw_setpoint_transfer_enable": "Transfert de la consigne d’eau chaude activé", "boiler_hw_setpoint_upper_bound": "Limite supérieure de la consigne d’eau chaude", "hw_enabled": "Eau chaude activée", - "hw_flow_setpoint": "Consigne de débit d’eau chaude" + "hw_flow_setpoint": "Consigne de température de départ d’eau chaude" } }, "opentherm_status": { @@ -165,7 +220,8 @@ } }, "abort": { - "opentherm_not_available": "Les données OpenTherm ne sont pas disponibles pour cette passerelle Wiser." + "opentherm_not_available": "Les données OpenTherm ne sont pas disponibles pour cette passerelle Wiser.", + "equipment_not_available": "Les données des équipements ne sont pas disponibles pour cette passerelle Wiser." } }, "services": { @@ -218,6 +274,9 @@ }, "floor_temperature": { "name": "Température du sol" } }, + "water_heater": { + "opentherm_hot_water": { "name": "Eau chaude OpenTherm" } + }, "number": { "away_mode_target_temperature": { "name": "Température cible du mode absence" }, "floor_temperature_offset": { "name": "Décalage de température du sol" }, @@ -250,6 +309,7 @@ "device_lock": { "name": "Verrouillage de l’appareil" }, "identify": { "name": "Identifier" }, "outlet": { "name": "Prise" }, + "ev_charger": { "name": "Chargeur de véhicule électrique" }, "away_mode_turns_off": { "name": "Le mode absence éteint" }, "away_mode_closes": { "name": "Le mode absence ferme" }, "passive_mode": { "name": "Mode passif" }, @@ -272,6 +332,9 @@ "fault_warning": { "name": "Avertissement de défaut" }, "remote_alarm": { "name": "Alarme distante" }, "battery_defect": { "name": "Défaut de batterie" }, + "ac_mains": { "name": "Alimentation secteur" }, + "test_mode": { "name": "Mode test" }, + "hush_mode": { "name": "Mode silencieux" }, "controllable": { "name": "Contrôlable" }, "pcm_mode": { "name": "Mode PCM" }, "window_detection_active": { "name": "Détection de fenêtre active" }, @@ -300,13 +363,33 @@ }, "sensor": { "signal": { "name": "Signal" }, + "report_count": { "name": "Nombre de rapports" }, "cloud": { "name": "Cloud" }, "heating_operation_mode": { "name": "Mode de fonctionnement du chauffage" }, - "heating": { "name": "Chauffage" }, - "heating_channel": { "name": "Canal de chauffage {channel}" }, - "hot_water": { "name": "Eau chaude" }, + "heating": { + "name": "Chauffage", + "state": { + "off": "Éteint", + "on": "Allumé" + } + }, + "heating_channel": { + "name": "Canal de chauffage {channel}", + "state": { + "off": "Éteint", + "on": "Allumé" + } + }, + "hot_water": { + "name": "Eau chaude", + "state": { + "off": "Éteint", + "on": "Allumé" + } + }, "hot_water_operation_mode": { "name": "Mode de fonctionnement de l’eau chaude" }, "target_temperature": { "name": "Température cible" }, + "current_schedule_temperature": { "name": "Température programmée actuelle" }, "floor_temperature": { "name": "Température du sol" }, "boiler_flow_temperature": { "name": "Température de départ de la chaudière" }, "boiler_return_temperature": { "name": "Température de retour de la chaudière" }, @@ -329,7 +412,7 @@ "delta_t": { "name": "Delta-T" }, "estimated_boiler_output": { "name": "Puissance estimée de la chaudière" }, "hw_flow_rate": { "name": "Débit d’eau chaude" }, - "hw_flow_setpoint": { "name": "Consigne de débit d’eau chaude" }, + "hw_flow_setpoint": { "name": "Consigne de température de départ d’eau chaude" }, "hw_temperature": { "name": "Température de l’eau chaude" }, "maximum_capacity_kw": { "name": "Puissance maximale de la chaudière" }, "minimum_modulation_level": { "name": "Niveau minimal de modulation" }, @@ -345,16 +428,16 @@ "total_energy": { "name": "Énergie totale" }, "energy_delivered": { "name": "Énergie fournie" }, "energy_received": { "name": "Énergie reçue" }, - "equipment_power": { "name": "Puissance de l’équipement" } + "equipment_power": { "name": "Équipement" } } }, "device_automation": { "trigger_type": { - "started_heating": "{entity_name} chauffe démarrée", - "stopped_heating": "{entity_name} chauffe arrêté", + "started_heating": "{entity_name} commence à chauffer", + "stopped_heating": "{entity_name} cesse de chauffer", "boosted": "{entity_name} est boosté", - "target_temperature_increased": "{entity_name} température cible a diminué", - "target_temperature_decreased": "{entity_name} température cible augmentée" + "target_temperature_increased": "La température cible de {entity_name} augmente", + "target_temperature_decreased": "La température cible de {entity_name} diminue" } } } diff --git a/custom_components/wiser/translations/hu.json b/custom_components/wiser/translations/hu.json new file mode 100644 index 00000000..45296929 --- /dev/null +++ b/custom_components/wiser/translations/hu.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Wiser fűtési központ beállítása", + "description": "Adja meg a központ IP-címét és a hozzá tartozó titkos kulcsot.", + "data": { + "host": "IP-cím vagy állomásnév", + "port": "Portszám", + "password": "Titkos kulcs" + } + }, + "zeroconf_confirm": { + "title": "Wiser fűtési központ beállítása ({name})", + "description": "Adja meg a központtól kapott titkos kulcsot.", + "data": { + "host": "IP-cím vagy állomásnév", + "port": "Portszám", + "password": "Titkos kulcs" + } + } + }, + "abort": { + "already_in_progress": "A Wiser fűtési központ konfigurálása már folyamatban van.", + "already_configured": "Ez az eszköz már konfigurálva van.", + "not_wiser_hub": "A felismert eszköz nem Wiser fűtési központ." + }, + "error": { + "invalid_hot_water_presets": "Egy vagy több melegvíz-előbeállítás érvénytelen.", + "auth_failure": "Nem sikerült hitelesíteni a Wiser központnál. Ellenőrizze a titkos kulcsot, majd próbálja újra.", + "timeout_error": "Időtúllépés történt a Wiser központhoz való csatlakozáskor. Ellenőrizze az IP-címet, majd próbálja újra.", + "timeout_error_discovery": "Időtúllépés történt a Wiser központhoz való csatlakozáskor. Ellenőrizze, hogy a központ továbbra is elérhető-e, majd próbálja újra.", + "unknown": "Ismeretlen hiba történt a Wiser központhoz való csatlakozáskor. Ellenőrizze a naplókat." + } + }, + "options": { + "step": { + "init": { + "title": "Wiser integráció beállításai", + "description": "Válassza ki a módosítandó paramétereket", + "menu_options": { + "main_params": "Fő paraméterek", + "automation_params": "Automatizálási paraméterek", + "opentherm": "OpenTherm", + "equipment_sensors": "Berendezésérzékelők", + "ui_options": "Felhasználói felület beállításai" + } + }, + "ui_options": { + "title": "Felhasználói felület beállításai", + "data": { + "legacy_naming": "Korábbi elnevezési mód", + "show_wiser_sidebar": "Wiser megjelenítése az oldalsávon" + }, + "data_description": { + "legacy_naming": "A helyiségek neveinek hozzáadása a Wiser-eszközök nevéhez. Ha engedélyezve van, az entitásazonosítók Home Assistant általi újragenerálása az alapértelmezett formátummal a Beállítások → Rendszer → Entitásazonosító-formátum menüben (Home Assistant 2026.8-tól) megkettőzi a területneveket, ha a terület és a Wiser-helyiség neve megegyezik. A beállítás módosítása nem nevezi át a meglévő entitásazonosítókat." + } + }, + "main_params": { + "title": "Wiser integráció beállításai", + "description": "Fő paraméterek", + "data": { + "host": "IP-cím vagy állomásnév", + "port": "Portszám", + "scan_interval": "Lekérdezési időköz (másodperc)", + "heating_boost_temp": "Fűtési gyorsfelfűtés alapértelmezett hőmérséklete (°C)", + "heating_boost_time": "Fűtési gyorsfelfűtés alapértelmezett időtartama (perc)", + "hotwater_boost_time": "Melegvíz-gyorsfelfűtés alapértelmezett időtartama (perc)", + "setpoint_mode": "Célhőmérséklet-beállítási mód", + "restore_manual_temp_option": "Ezt a hőmérsékletet használja, ha nincs tárolt kézi hőmérséklet" + } + }, + "automation_params": { + "title": "Wiser integráció beállításai", + "description": "Beépített automatizálások engedélyezése", + "sections": { + "automations_passive_mode": { + "name": "Passzív mód", + "description": "A passzív termosztatikus radiátorszelep (TRV) funkció engedélyezése. A működéshez helyiségenként külön engedélyezni kell.", + "data": { + "automations_passive_mode": "Passzív mód", + "passive_mode_temperature_increments": "Passzív mód hőmérsékleti lépésköze" + } + }, + "hotwater_climate": { + "name": "Melegvíz-hőmérséklet szabályozása", + "description": "A melegvíz-hőmérséklet szabályozásának engedélyezése. Részletek a wikiben.", + "data": { + "hotwater_climate": "Klímaentitás engedélyezése", + "experimental_mode": "Kísérleti üzemmód engedélyezése", + "hotwater_auto_mode": "Melegvíz automatikus módja", + "hotwater_heat_mode": "Melegvíz fűtési módja", + "hotwater_boost_mode": "Melegvíz gyorsfelfűtési módja", + "hotwater_sensor_entity_id": "Melegvíz-hőmérséklet érzékelője" + } + }, + "additional_sensors": { + "name": "További érzékelők", + "description": "Opcionális érzékelők megjelenítése a Wiser által jelentett adatokhoz.", + "data": { + "schedule_setpoint_rooms": "Klímavezérlés nélküli helyiségek" + }, + "data_description": { + "schedule_setpoint_rooms": "Válassza ki azokat a Wiser helyiségeket, amelyekhez aktuális ütemezési hőmérséklet-érzékelőt kell létrehozni." + } + } + } + }, + "equipment_sensors": { + "title": "Berendezésérzékelők", + "description": "Részletes berendezésadat-entitások beállítása kompatibilis eszközökhöz.", + "data": { + "equipment_sensors": "Berendezésadat-érzékelők engedélyezése" + }, + "data_description": { + "equipment_sensors": "További érzékelő megjelenítése részletes berendezésjellemzőkkel minden kompatibilis okoskonnektorhoz, PowerTag eszközhöz és fűtési szelepmozgatóhoz." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "OpenTherm funkciók beállítása.", + "menu_options": { + "opentherm_hot_water_control": "Melegvíz-vezérlés", + "opentherm_sensors": "Érzékelők" + } + }, + "opentherm_sensors": { + "title": "OpenTherm érzékelők", + "description": "Válasszon kategóriát a felismert OpenTherm-attribútumok közül.", + "menu_options": { + "opentherm_readings": "Mért értékek", + "opentherm_central_heating": "Központi fűtés", + "opentherm_hot_water": "Meleg víz", + "opentherm_status": "Állapot és diagnosztika" + } + }, + "opentherm_hot_water_control": { + "title": "OpenTherm melegvíz-vezérlés", + "description": "A kazán használati melegvíz-alapjelének opcionális vezérlése a Wiseren keresztül. Az integráció létrehoz egy OpenTherm Hot Water ütemezést a Wiser Schedules panelen. A Ki állapot az OpenTherm által jelentett minimális melegvíz-hőmérsékletet használja.", + "data": { + "opentherm_hot_water_control": "Melegvíz-vezérlés engedélyezése", + "opentherm_hot_water_manual_temperature": "Kézi hőmérséklet", + "opentherm_hot_water_sensor_entity_id": "Opcionális külső hőmérséklet-érzékelő.", + "opentherm_hot_water_presets": "Előbeállítások" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "A kézi módban használt hőmérséklet.", + "opentherm_hot_water_sensor_entity_id": "Opcionális hőmérséklet-érzékelő az aktuális vízhőmérséklethez. Ha az érzékelő nem érhető el, az OpenTherm értéke kerül felhasználásra." + } + }, + "opentherm_readings": { + "title": "OpenTherm mért értékek", + "description": "Válassza ki, mely felismert OpenTherm-mért értékek jelenjenek meg érzékelőentitásként.", + "data": { + "ch_flow_temperature": "Kazán előremenő hőmérséklete", + "ch_pressure_bar": "Központi fűtés nyomása", + "ch_return_temperature": "Kazán visszatérő hőmérséklete", + "delta_t": "Hőmérséklet-különbség (ΔT)", + "boiler_exhaust_temperature": "Kazán füstgázhőmérséklete", + "estimated_boiler_output": "Kazán becsült teljesítménye", + "hw_flow_rate": "Melegvíz térfogatárama", + "hw_temperature": "Melegvíz hőmérséklete", + "maximum_capacity_kw": "Kazán maximális teljesítménye", + "minimum_modulation_level": "Minimális modulációs szint", + "relative_modulation_level": "Relatív modulációs szint", + "room_temperature": "Helyiség hőmérséklete" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm központi fűtés", + "description": "Válassza ki, mely felismert központifűtés-attribútumok jelenjenek meg entitásként.", + "data": { + "boiler_ch_max_setpoint_read_write": "Kazán központifűtés-maximális célértékének olvasása/írása", + "boiler_ch_max_setpoint_transfer_enable": "Kazán központifűtés-maximális célértékének átvitele engedélyezve", + "boiler_ch_setpoint": "Kazán központifűtés-célértéke", + "boiler_ch_setpoint_lower_bound": "Kazán központifűtés-célértékének alsó határa", + "boiler_ch_setpoint_upper_bound": "Kazán központifűtés-célértékének felső határa", + "ch_flow_active_lower_setpoint": "Központi fűtés előremenő aktív alsó célértéke", + "ch_flow_active_upper_setpoint": "Központi fűtés előremenő aktív felső célértéke", + "ch1_flow_enabled": "1. fűtési kör előremenő ága engedélyezve", + "ch1_flow_setpoint": "1. fűtési kör előremenő célértéke", + "ch2_flow_enabled": "2. fűtési kör előremenő ága engedélyezve", + "ch2_flow_setpoint": "2. fűtési kör előremenő célértéke", + "room_setpoint": "Helyiség célhőmérséklete" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm meleg víz", + "description": "Válassza ki, mely felismert melegvíz-attribútumok jelenjenek meg entitásként.", + "data": { + "boiler_hw_setpoint": "Kazán melegvíz-célhőmérséklete", + "boiler_hw_setpoint_lower_bound": "Kazán melegvíz-célhőmérsékletének alsó határa", + "boiler_hw_setpoint_read_write": "Kazán melegvíz-célhőmérsékletének olvasása/írása", + "boiler_hw_setpoint_transfer_enable": "Kazán melegvíz-célhőmérsékletének átvitele engedélyezve", + "boiler_hw_setpoint_upper_bound": "Kazán melegvíz-célhőmérsékletének felső határa", + "hw_enabled": "Melegvíz-készítés engedélyezve", + "hw_flow_setpoint": "Melegvíz előremenő célhőmérséklete" + } + }, + "opentherm_status": { + "title": "OpenTherm állapot és diagnosztika", + "description": "Válassza ki, mely felismert állapot- és diagnosztikai attribútumok jelenjenek meg érzékelőentitásként.", + "data": { + "boiler_fault": "Kazánhiba", + "central_heating_active": "Központi fűtés aktív", + "central_heating_2_active": "2. központi fűtési kör aktív", + "connection_status": "Kapcsolat állapota", + "coprocessor_update_status": "OpenTherm koprocesszor frissítési állapota", + "coprocessor_version": "OpenTherm koprocesszor verziója", + "cooling_active": "Hűtés aktív", + "diagnostic_event": "Diagnosztikai esemény", + "flame_active": "Láng aktív", + "flame_statistics": "Lángstatisztika", + "hot_water_active": "Melegvíz-készítés aktív", + "operating_mode": "Üzemmód", + "slave_status": "OpenTherm alárendelt eszköz állapota", + "tracked_room_id": "Követett helyiség azonosítója" + } + } + }, + "abort": { + "opentherm_not_available": "Ehhez a Wiser központhoz nem érhetők el OpenTherm-adatok.", + "equipment_not_available": "Ehhez a Wiser központhoz nem érhetők el berendezésadatok." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "OpenTherm-paraméter beállítása", + "description": "Egy támogatott OpenTherm-hőmérsékleti paraméter módosítása a Wiser központon.", + "fields": { + "parameter": { + "name": "Paraméter", + "description": "A módosítandó OpenTherm-hőmérsékleti paraméter. A szükséges API-végpont automatikusan kerül kiválasztásra." + }, + "temperature": { + "name": "Hőmérséklet", + "description": "A kazán által megadott határértékeken belüli hőmérsékletet használjon." + }, + "hub": { + "name": "Wiser központ", + "description": "Csak akkor válasszon központot, ha több Wiser központja van." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Központi fűtés aktív alsó célhőmérséklete", + "chFlowActiveUpperSetpoint": "Központi fűtés aktív felső célhőmérséklete", + "dhwFlowSetpoint": "Központ melegvíz-előremenő célhőmérséklete", + "dhwSetpoint": "Kazán melegvíz-célhőmérséklete", + "maxChSetpoint": "Kazán központifűtés-maximális célhőmérséklete" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Adjon meg egy OpenTherm-hőmérsékletet." + }, + "opentherm_hub_not_found": { + "message": "A megadott Wiser központ nem található." + }, + "opentherm_hub_not_loaded": { + "message": "A megadott Wiser központ nincs betöltve." + }, + "opentherm_hub_required": { + "message": "Ha több központ van konfigurálva, válasszon egy Wiser központot." + }, + "opentherm_not_supported": { + "message": "Ez a központ nem támogatja az OpenTherm funkciót." + }, + "opentherm_invalid_parameter": { + "message": "Érvénytelen OpenTherm-paraméter vagy érték: {error}" + }, + "opentherm_write_failed": { + "message": "Nem sikerült beállítani az OpenTherm-paramétert ({parameter}): {error}" + }, + "opentherm_confirmation_failed": { + "message": "Az OpenTherm-paraméter ({parameter}) egy újrapróbálkozás és a {window} másodperces ellenőrzési időablak után sem maradt a kért {requested} °C értéken. A Wiser által jelentett érték: {reported} °C. Nem történik további újrapróbálkozás." + }, + "opentherm_retry_failed": { + "message": "Az OpenTherm-paraméter ({parameter}) nem maradt a kért {requested} °C értéken, és az újrapróbálkozást nem sikerült elküldeni: {error}. A Wiser által jelentett érték: {reported} °C. Nem történik további újrapróbálkozás." + }, + "opentherm_notification_title": { + "message": "Wiser OpenTherm parancs" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Fűtés" + }, + "floor_temperature": { + "name": "Padlóhőmérséklet" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Távolléti mód célhőmérséklete" + }, + "floor_temperature_offset": { + "name": "Padlóhőmérséklet korrekciója" + }, + "indoor_discomfort_temperature": { + "name": "Beltéri diszkomfort-hőmérséklet" + }, + "outdoor_discomfort_temperature": { + "name": "Kültéri diszkomfort-hőmérséklet" + } + }, + "select": { + "mode": { + "name": "Mód" + }, + "power_on_behavior": { + "name": "Bekapcsolás utáni viselkedés" + }, + "led_indicator": { + "name": "LED-jelzőfény" + } + }, + "button": { + "boost_all_heating": { + "name": "Összes fűtés gyorsfelfűtése" + }, + "cancel_all_heating_overrides": { + "name": "Összes fűtési felülbírálás törlése" + }, + "boost_hot_water": { + "name": "Melegvíz gyorsfelfűtése" + }, + "cancel_hot_water_overrides": { + "name": "Melegvíz-felülbírálások törlése" + }, + "toggle_hot_water": { + "name": "Melegvíz be-/kikapcsolása" + }, + "moment": { + "name": "Pillanat: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Szelepvédelem" + }, + "eco_mode": { + "name": "Takarékos mód" + }, + "away_mode_affects_hot_water": { + "name": "A távolléti mód a melegvízre is hatással van" + }, + "comfort_mode": { + "name": "Komfort mód" + }, + "away_mode": { + "name": "Távolléti mód" + }, + "daylight_saving": { + "name": "Nyári időszámítás" + }, + "summer_comfort_enabled": { + "name": "Nyári komfort engedélyezve" + }, + "window_detection": { + "name": "Ablaknyitás-érzékelés" + }, + "include_in_summer_comfort": { + "name": "Bevonás a nyári komfortba" + }, + "device_lock": { + "name": "Eszköz zárolása" + }, + "identify": { + "name": "Azonosítás" + }, + "outlet": { + "name": "Aljzat" + }, + "away_mode_turns_off": { + "name": "Távolléti módban kikapcsol" + }, + "away_mode_closes": { + "name": "Távolléti módban bezár" + }, + "passive_mode": { + "name": "Passzív mód" + }, + "respect_summer_comfort": { + "name": "Nyári komfort figyelembevétele" + }, + "interacts_with_room_climate": { + "name": "Hatással van a helyiség klímaszabályozására" + }, + "humidity_interacts_with_room_climate": { + "name": "A páratartalom hatással van a helyiség klímaszabályozására" + }, + "temperature_interacts_with_room_climate": { + "name": "A hőmérséklet hatással van a helyiség klímaszabályozására" + }, + "illuminance_interacts_with_room_climate": { + "name": "A megvilágítás hatással van a helyiség klímaszabályozására" + }, + "sensor_interacts_with_room_climate": { + "name": "A(z) {sensor} hatással van a helyiség klímaszabályozására" + }, + "manual_heat": { + "name": "Kézi fűtés" + }, + "hot_water": { + "name": "Meleg víz" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Nyári túlmelegedés megelőzése" + }, + "summer_comfort_available": { + "name": "Nyári komfort elérhető" + }, + "pcm_device_limit_reached": { + "name": "Elérte a PCM-eszközök maximális számát" + }, + "smoke_alarm": { + "name": "Füstjelzés" + }, + "heat_alarm": { + "name": "Hőriasztás" + }, + "tamper_alarm": { + "name": "Szabotázsriasztás" + }, + "fault_warning": { + "name": "Hibajelzés" + }, + "remote_alarm": { + "name": "Távoli riasztás" + }, + "battery_defect": { + "name": "Akkumulátorhiba" + }, + "controllable": { + "name": "Vezérelhető" + }, + "pcm_mode": { + "name": "PCM mód" + }, + "window_detection_active": { + "name": "Ablaknyitás-érzékelés aktív" + }, + "is_dimmable": { + "name": "Fényerőszabályozható" + }, + "is_led_indicator_supported": { + "name": "LED-jelzőfény támogatott" + }, + "is_output_mode_supported": { + "name": "Kimeneti mód támogatott" + }, + "is_power_on_behaviour_supported": { + "name": "Bekapcsolás utáni viselkedés támogatott" + }, + "is_tilt_supported": { + "name": "Döntés támogatott" + }, + "is_open": { + "name": "Nyitva" + }, + "is_closed": { + "name": "Zárva" + }, + "active": { + "name": "Aktív" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Kazán központifűtés-maximális célértékének olvasása/írása" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Kazán központifűtés-maximális célértékének átvitele engedélyezve" + }, + "boiler_fault": { + "name": "Kazánhiba" + }, + "boiler_hw_setpoint_read_write": { + "name": "Kazán melegvíz-célhőmérsékletének olvasása/írása" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Kazán melegvíz-célhőmérsékletének átvitele engedélyezve" + }, + "central_heating_2_active": { + "name": "2. központi fűtési kör aktív" + }, + "central_heating_active": { + "name": "Központi fűtés aktív" + }, + "ch1_flow_enabled": { + "name": "1. fűtési kör előremenő ága engedélyezve" + }, + "ch2_flow_enabled": { + "name": "2. fűtési kör előremenő ága engedélyezve" + }, + "cooling_active": { + "name": "Hűtés aktív" + }, + "diagnostic_event": { + "name": "Diagnosztikai esemény" + }, + "flame_active": { + "name": "Láng aktív" + }, + "hot_water_active": { + "name": "Melegvíz-készítés aktív" + }, + "hw_enabled": { + "name": "Melegvíz-készítés engedélyezve" + } + }, + "sensor": { + "signal": { + "name": "Jel" + }, + "cloud": { + "name": "Felhő" + }, + "heating_operation_mode": { + "name": "Fűtési üzemmód" + }, + "heating": { + "name": "Fűtés", + "state": { + "off": "Ki", + "on": "Be" + } + }, + "heating_channel": { + "name": "Fűtési csatorna {channel}", + "state": { + "off": "Ki", + "on": "Be" + } + }, + "hot_water": { + "name": "Meleg víz", + "state": { + "off": "Ki", + "on": "Be" + } + }, + "hot_water_operation_mode": { + "name": "Melegvíz üzemmód" + }, + "target_temperature": { + "name": "Célhőmérséklet" + }, + "floor_temperature": { + "name": "Padlóhőmérséklet" + }, + "boiler_flow_temperature": { + "name": "Kazán előremenő hőmérséklete" + }, + "boiler_return_temperature": { + "name": "Kazán visszatérő hőmérséklete" + }, + "flame_statistics": { + "name": "Lángstatisztika" + }, + "boiler_ch_setpoint": { + "name": "Kazán központifűtés-célértéke" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Kazán központifűtés-célértékének alsó határa" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Kazán központifűtés-célértékének felső határa" + }, + "boiler_exhaust_temperature": { + "name": "Kazán füstgázhőmérséklete" + }, + "boiler_hw_setpoint": { + "name": "Kazán melegvíz-célhőmérséklete" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Kazán melegvíz-célhőmérsékletének alsó határa" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Kazán melegvíz-célhőmérsékletének felső határa" + }, + "ch1_flow_setpoint": { + "name": "1. fűtési kör előremenő célértéke" + }, + "ch2_flow_setpoint": { + "name": "2. fűtési kör előremenő célértéke" + }, + "ch_flow_active_lower_setpoint": { + "name": "Központi fűtés előremenő aktív alsó célértéke" + }, + "ch_flow_active_upper_setpoint": { + "name": "Központi fűtés előremenő aktív felső célértéke" + }, + "ch_pressure_bar": { + "name": "Központi fűtés nyomása" + }, + "connection_status": { + "name": "Kapcsolat állapota" + }, + "coprocessor_update_status": { + "name": "OpenTherm koprocesszor frissítési állapota" + }, + "coprocessor_version": { + "name": "OpenTherm koprocesszor verziója" + }, + "delta_t": { + "name": "Hőmérséklet-különbség (ΔT)" + }, + "estimated_boiler_output": { + "name": "Kazán becsült teljesítménye" + }, + "hw_flow_rate": { + "name": "Melegvíz térfogatárama" + }, + "hw_flow_setpoint": { + "name": "Melegvíz előremenő célhőmérséklete" + }, + "hw_temperature": { + "name": "Melegvíz hőmérséklete" + }, + "maximum_capacity_kw": { + "name": "Kazán maximális teljesítménye" + }, + "minimum_modulation_level": { + "name": "Minimális modulációs szint" + }, + "operating_mode": { + "name": "Üzemmód" + }, + "relative_modulation_level": { + "name": "Relatív modulációs szint" + }, + "room_setpoint": { + "name": "Helyiség célhőmérséklete" + }, + "room_temperature": { + "name": "Helyiség hőmérséklete" + }, + "slave_status": { + "name": "OpenTherm alárendelt eszköz állapota" + }, + "tracked_room_id": { + "name": "Követett helyiség azonosítója" + }, + "heating_demand": { + "name": "Fűtési igény" + }, + "heating_demand_channel": { + "name": "Fűtési igény – {channel}. csatorna" + }, + "hot_water_demand": { + "name": "Melegvíz-igény" + }, + "total_energy": { + "name": "Összes energia" + }, + "energy_delivered": { + "name": "Leadott energia" + }, + "energy_received": { + "name": "Felvett energia" + }, + "equipment_power": { + "name": "Berendezés teljesítménye" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} fűtése elindul", + "stopped_heating": "{entity_name} fűtése leáll", + "boosted": "{entity_name} gyorsfelfűtése aktiválódik", + "target_temperature_increased": "{entity_name} célhőmérséklete emelkedett", + "target_temperature_decreased": "{entity_name} célhőmérséklete csökkent" + } + } +} diff --git a/custom_components/wiser/translations/nb.json b/custom_components/wiser/translations/nb.json new file mode 100644 index 00000000..42c4f021 --- /dev/null +++ b/custom_components/wiser/translations/nb.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Oppsett av Wiser Heat Hub", + "description": "Skriv inn IP-adressen og den hemmelige nøkkelen som ble hentet fra huben.", + "data": { + "host": "IP-adresse eller vertsnavn", + "port": "Portnummer", + "password": "Hemmelig nøkkel" + } + }, + "zeroconf_confirm": { + "title": "Oppsett av Wiser Heat Hub ({name})", + "description": "Skriv inn den hemmelige nøkkelen som ble hentet fra huben.", + "data": { + "host": "IP-adresse eller vertsnavn", + "port": "Portnummer", + "password": "Hemmelig nøkkel" + } + } + }, + "abort": { + "already_in_progress": "Konfigurasjon av Wiser Heat Hub pågår allerede.", + "already_configured": "Denne enheten er allerede konfigurert.", + "not_wiser_hub": "Den oppdagede enheten er ikke en Wiser Heat Hub." + }, + "error": { + "invalid_hot_water_presets": "En eller flere varmtvannsforhåndsinnstillinger er ugyldige.", + "auth_failure": "Kan ikke autentisere mot Wiser-huben. Kontroller den hemmelige nøkkelen og prøv igjen.", + "timeout_error": "Tidsavbrudd ved tilkobling til Wiser-huben. Kontroller IP-adressen og prøv igjen.", + "timeout_error_discovery": "Tidsavbrudd ved tilkobling til Wiser-huben. Kontroller at huben fortsatt er tilkoblet, og prøv igjen.", + "unknown": "Ukjent feil ved tilkobling til Wiser-huben. Kontroller loggene." + } + }, + "options": { + "step": { + "init": { + "title": "Alternativer for Wiser-integrasjonen", + "description": "Velg parametere som skal endres", + "menu_options": { + "main_params": "Hovedparametere", + "automation_params": "Automatiseringsparametere", + "opentherm": "OpenTherm", + "equipment_sensors": "Utstyrssensorer", + "ui_options": "Brukergrensesnittalternativer" + } + }, + "ui_options": { + "title": "Brukergrensesnittalternativer", + "data": { + "legacy_naming": "Eldre navngivning", + "show_wiser_sidebar": "Vis Wiser i sidepanelet" + }, + "data_description": { + "legacy_naming": "Ta med romnavn i navnene på Wiser-enheter. Hvis dette er aktivert, vil regenerering av entitets-ID-er i Home Assistant med standardformatet under Innstillinger → System → Entitets-ID-format (Home Assistant 2026.8 og nyere) duplisere områdenavn når området og Wiser-rommet har samme navn. Endring av dette valget gir ikke nytt navn til eksisterende entitets-ID-er." + } + }, + "main_params": { + "title": "Alternativer for Wiser-integrasjonen", + "description": "Hovedparametere", + "data": { + "host": "IP-adresse eller vertsnavn", + "port": "Portnummer", + "scan_interval": "Skanneintervall (sek.)", + "heating_boost_temp": "Standardtemperatur for varmeboost (°C)", + "heating_boost_time": "Standardvarighet for varmeboost (min.)", + "hotwater_boost_time": "Standardvarighet for varmtvannsboost (min.)", + "setpoint_mode": "Settpunktmodus", + "restore_manual_temp_option": "Bruk denne temperaturen hvis ingen manuell temperatur er lagret" + } + }, + "automation_params": { + "title": "Alternativer for Wiser-integrasjonen", + "description": "Aktiver innebygde automatiseringer", + "sections": { + "automations_passive_mode": { + "name": "Passiv modus", + "description": "Aktiver passiv TRV-funksjon. Hvert rom må aktiveres for at den skal fungere.", + "data": { + "automations_passive_mode": "Passiv modus", + "passive_mode_temperature_increments": "Temperaturtrinn for passiv modus" + } + }, + "hotwater_climate": { + "name": "Klimastyring av varmtvann", + "description": "Aktiver klimastyring av varmtvann. Se wikien for detaljer.", + "data": { + "hotwater_climate": "Aktiver klimaentiteten", + "experimental_mode": "Aktiver eksperimentell driftsmodus", + "hotwater_auto_mode": "Automatisk varmtvannsmodus", + "hotwater_heat_mode": "Varmtvannsoppvarmingsmodus", + "hotwater_boost_mode": "Varmtvannsboostmodus", + "hotwater_sensor_entity_id": "Varmtvannstemperatursensor" + } + }, + "additional_sensors": { + "name": "Ekstra sensorer", + "description": "Vis valgfrie sensorer for data rapportert av Wiser.", + "data": { + "schedule_setpoint_rooms": "Rom uten klimastyring" + }, + "data_description": { + "schedule_setpoint_rooms": "Velg Wiser-rommene der det skal opprettes en sensor for gjeldende tidsplantemperatur." + } + } + } + }, + "equipment_sensors": { + "title": "Utstyrssensorer", + "description": "Konfigurer entiteter med detaljerte utstyrsdata for kompatible enheter.", + "data": { + "equipment_sensors": "Aktiver utstyrsdatasensorer" + }, + "data_description": { + "equipment_sensors": "Vis en ekstra sensor med detaljerte utstyrsattributter for hver kompatible smartplugg, PowerTag og varmeaktuator." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Konfigurer OpenTherm-funksjoner.", + "menu_options": { + "opentherm_hot_water_control": "Varmtvannsstyring", + "opentherm_sensors": "Sensorer" + } + }, + "opentherm_sensors": { + "title": "OpenTherm-sensorer", + "description": "Velg en kategori med oppdagede OpenTherm-attributter.", + "menu_options": { + "opentherm_readings": "Avlesninger", + "opentherm_central_heating": "Sentralvarme", + "opentherm_hot_water": "Varmtvann", + "opentherm_status": "Status og diagnostikk" + } + }, + "opentherm_hot_water_control": { + "title": "OpenTherm-varmtvannsstyring", + "description": "Styr eventuelt kjelens settpunkt for forbruksvann via Wiser. Integrasjonen oppretter en OpenTherm Hot Water-tidsplan i Wiser Schedules-panelet. Av bruker den laveste varmtvannstemperaturen som OpenTherm rapporterer.", + "data": { + "opentherm_hot_water_control": "Aktiver varmtvannsstyring", + "opentherm_hot_water_manual_temperature": "Manuell temperatur", + "opentherm_hot_water_sensor_entity_id": "Valgfri ekstern temperatursensor.", + "opentherm_hot_water_presets": "Forhåndsinnstillinger" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperatur som brukes i manuell modus.", + "opentherm_hot_water_sensor_entity_id": "Valgfri temperatursensor som brukes for gjeldende vanntemperatur. OpenTherm brukes når sensoren ikke er tilgjengelig." + } + }, + "opentherm_readings": { + "title": "OpenTherm-avlesninger", + "description": "Velg hvilke oppdagede OpenTherm-avlesninger som skal vises som sensorentiteter.", + "data": { + "ch_flow_temperature": "Kjelens turtemperatur", + "ch_pressure_bar": "Sentralvarmetrykk", + "ch_return_temperature": "Kjelens returtemperatur", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Kjelens avgastemperatur", + "estimated_boiler_output": "Anslått kjeleeffekt", + "hw_flow_rate": "Varmtvannsstrøm", + "hw_temperature": "Varmtvannstemperatur", + "maximum_capacity_kw": "Maksimal kjelekapasitet", + "minimum_modulation_level": "Minste modulasjonsnivå", + "relative_modulation_level": "Relativt modulasjonsnivå", + "room_temperature": "Romtemperatur" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm-sentralvarme", + "description": "Velg hvilke oppdagede sentralvarmeattributter som skal vises som entiteter.", + "data": { + "boiler_ch_max_setpoint_read_write": "Lesing/skriving av kjelens maksimale sentralvarmesettpunkt", + "boiler_ch_max_setpoint_transfer_enable": "Overføring av kjelens maksimale sentralvarmesettpunkt aktivert", + "boiler_ch_setpoint": "Kjelens sentralvarmesettpunkt", + "boiler_ch_setpoint_lower_bound": "Nedre grense for kjelens sentralvarmesettpunkt", + "boiler_ch_setpoint_upper_bound": "Øvre grense for kjelens sentralvarmesettpunkt", + "ch_flow_active_lower_setpoint": "Aktivt nedre settpunkt for sentralvarmetur", + "ch_flow_active_upper_setpoint": "Aktivt øvre settpunkt for sentralvarmetur", + "ch1_flow_enabled": "Sentralvarmekrets 1 tur aktivert", + "ch1_flow_setpoint": "Settpunkt for sentralvarmekrets 1 tur", + "ch2_flow_enabled": "Sentralvarmekrets 2 tur aktivert", + "ch2_flow_setpoint": "Settpunkt for sentralvarmekrets 2 tur", + "room_setpoint": "Romsettpunkt" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm-varmtvann", + "description": "Velg hvilke oppdagede varmtvannsattributter som skal vises som entiteter.", + "data": { + "boiler_hw_setpoint": "Kjelens varmtvannssettpunkt", + "boiler_hw_setpoint_lower_bound": "Nedre grense for kjelens varmtvannssettpunkt", + "boiler_hw_setpoint_read_write": "Lesing/skriving av kjelens varmtvannssettpunkt", + "boiler_hw_setpoint_transfer_enable": "Overføring av kjelens varmtvannssettpunkt aktivert", + "boiler_hw_setpoint_upper_bound": "Øvre grense for kjelens varmtvannssettpunkt", + "hw_enabled": "Varmtvann aktivert", + "hw_flow_setpoint": "Settpunkt for varmtvannstur" + } + }, + "opentherm_status": { + "title": "OpenTherm-status og diagnostikk", + "description": "Velg hvilke oppdagede status- og diagnoseattributter som skal vises som sensorentiteter.", + "data": { + "boiler_fault": "Kjelfeil", + "central_heating_active": "Sentralvarme aktiv", + "central_heating_2_active": "Sentralvarme 2 aktiv", + "connection_status": "Tilkoblingsstatus", + "coprocessor_update_status": "Oppdateringsstatus for OpenTherm-koprosessor", + "coprocessor_version": "Versjon av OpenTherm-koprosessor", + "cooling_active": "Kjøling aktiv", + "diagnostic_event": "Diagnosehendelse", + "flame_active": "Flamme aktiv", + "flame_statistics": "Flammestatistikk", + "hot_water_active": "Varmtvann aktivt", + "operating_mode": "Driftsmodus", + "slave_status": "OpenTherm-slavestatus", + "tracked_room_id": "ID for sporet rom" + } + } + }, + "abort": { + "opentherm_not_available": "OpenTherm-data er ikke tilgjengelige for denne Wiser-huben.", + "equipment_not_available": "Utstyrsdata er ikke tilgjengelige for denne Wiser-huben." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "Angi OpenTherm-parameter", + "description": "Endrer en støttet OpenTherm-temperaturparameter på Wiser-huben.", + "fields": { + "parameter": { + "name": "Parameter", + "description": "OpenTherm-temperaturparameteren som skal endres. Nødvendig API-endepunkt velges automatisk." + }, + "temperature": { + "name": "Temperatur", + "description": "Bruk en temperatur innenfor grensene som kjelen rapporterer." + }, + "hub": { + "name": "Wiser-hub", + "description": "Velg bare en hub når du har flere Wiser-huber." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Aktivt nedre sentralvarmesettpunkt", + "chFlowActiveUpperSetpoint": "Aktivt øvre sentralvarmesettpunkt", + "dhwFlowSetpoint": "Hubens settpunkt for varmtvannstur", + "dhwSetpoint": "Kjelens varmtvannssettpunkt", + "maxChSetpoint": "Kjelens maksimale sentralvarmesettpunkt" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Angi en OpenTherm-temperatur." + }, + "opentherm_hub_not_found": { + "message": "Den angitte Wiser-huben ble ikke funnet." + }, + "opentherm_hub_not_loaded": { + "message": "Den angitte Wiser-huben er ikke lastet inn." + }, + "opentherm_hub_required": { + "message": "Velg en Wiser-hub når mer enn én hub er konfigurert." + }, + "opentherm_not_supported": { + "message": "Denne huben har ikke OpenTherm-funksjonalitet." + }, + "opentherm_invalid_parameter": { + "message": "OpenTherm-parameteren eller verdien er ugyldig: {error}" + }, + "opentherm_write_failed": { + "message": "Kan ikke angi OpenTherm-parameteren {parameter}: {error}" + }, + "opentherm_confirmation_failed": { + "message": "OpenTherm-parameteren {parameter} forble ikke på {requested} °C etter ett nytt forsøk og bekreftelsesvinduet på {window} sekunder. Wiser rapporterer {reported} °C. Ingen flere forsøk sendes." + }, + "opentherm_retry_failed": { + "message": "OpenTherm-parameteren {parameter} forble ikke på {requested} °C, og et nytt forsøk kunne ikke sendes: {error}. Wiser rapporterer {reported} °C. Ingen flere forsøk sendes." + }, + "opentherm_notification_title": { + "message": "Wiser OpenTherm-kommando" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Oppvarming" + }, + "floor_temperature": { + "name": "Gulvtemperatur" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Måltemperatur for bortemodus" + }, + "floor_temperature_offset": { + "name": "Forskyvning av gulvtemperatur" + }, + "indoor_discomfort_temperature": { + "name": "Innendørs ubehagstemperatur" + }, + "outdoor_discomfort_temperature": { + "name": "Utendørs ubehagstemperatur" + } + }, + "select": { + "mode": { + "name": "Modus" + }, + "power_on_behavior": { + "name": "Oppstartsadferd" + }, + "led_indicator": { + "name": "LED-indikator" + } + }, + "button": { + "boost_all_heating": { + "name": "Boost all oppvarming" + }, + "cancel_all_heating_overrides": { + "name": "Avbryt alle overstyringer av oppvarming" + }, + "boost_hot_water": { + "name": "Boost varmtvann" + }, + "cancel_hot_water_overrides": { + "name": "Avbryt alle overstyringer av varmtvann" + }, + "toggle_hot_water": { + "name": "Slå varmtvann av/på" + }, + "moment": { + "name": "Øyeblikk: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Ventilbeskyttelse" + }, + "eco_mode": { + "name": "Økomodus" + }, + "away_mode_affects_hot_water": { + "name": "Bortemodus påvirker varmtvann" + }, + "comfort_mode": { + "name": "Komfortmodus" + }, + "away_mode": { + "name": "Bortemodus" + }, + "daylight_saving": { + "name": "Sommertid" + }, + "summer_comfort_enabled": { + "name": "Sommerkomfort aktivert" + }, + "window_detection": { + "name": "Vindusregistrering" + }, + "include_in_summer_comfort": { + "name": "Ta med i sommerkomfort" + }, + "device_lock": { + "name": "Enhetslås" + }, + "identify": { + "name": "Identifiser" + }, + "outlet": { + "name": "Stikkontakt" + }, + "away_mode_turns_off": { + "name": "Bortemodus slår av" + }, + "away_mode_closes": { + "name": "Bortemodus lukker" + }, + "passive_mode": { + "name": "Passiv modus" + }, + "respect_summer_comfort": { + "name": "Respekter sommerkomfort" + }, + "interacts_with_room_climate": { + "name": "Samhandler med romklima" + }, + "humidity_interacts_with_room_climate": { + "name": "Luftfuktighet samhandler med romklima" + }, + "temperature_interacts_with_room_climate": { + "name": "Temperatur samhandler med romklima" + }, + "illuminance_interacts_with_room_climate": { + "name": "Belysningsstyrke samhandler med romklima" + }, + "sensor_interacts_with_room_climate": { + "name": "{sensor} samhandler med romklima" + }, + "manual_heat": { + "name": "Manuell oppvarming" + }, + "hot_water": { + "name": "Varmtvann" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Forebygging av sommerubehag" + }, + "summer_comfort_available": { + "name": "Sommerkomfort tilgjengelig" + }, + "pcm_device_limit_reached": { + "name": "Grensen for PCM-enheter er nådd" + }, + "smoke_alarm": { + "name": "Røykalarm" + }, + "heat_alarm": { + "name": "Varmealarm" + }, + "tamper_alarm": { + "name": "Sabotasjealarm" + }, + "fault_warning": { + "name": "Feilvarsel" + }, + "remote_alarm": { + "name": "Fjernalarm" + }, + "battery_defect": { + "name": "Batterifeil" + }, + "controllable": { + "name": "Styrbar" + }, + "pcm_mode": { + "name": "PCM-modus" + }, + "window_detection_active": { + "name": "Vindusregistrering aktiv" + }, + "is_dimmable": { + "name": "Kan dimmes" + }, + "is_led_indicator_supported": { + "name": "LED-indikator støttes" + }, + "is_output_mode_supported": { + "name": "Utgangsmodus støttes" + }, + "is_power_on_behaviour_supported": { + "name": "Oppstartsadferd støttes" + }, + "is_tilt_supported": { + "name": "Vipping støttes" + }, + "is_open": { + "name": "Åpen" + }, + "is_closed": { + "name": "Lukket" + }, + "active": { + "name": "Aktiv" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Lesing/skriving av kjelens maksimale sentralvarmesettpunkt" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Overføring av kjelens maksimale sentralvarmesettpunkt aktivert" + }, + "boiler_fault": { + "name": "Kjelfeil" + }, + "boiler_hw_setpoint_read_write": { + "name": "Lesing/skriving av kjelens varmtvannssettpunkt" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Overføring av kjelens varmtvannssettpunkt aktivert" + }, + "central_heating_2_active": { + "name": "Sentralvarme 2 aktiv" + }, + "central_heating_active": { + "name": "Sentralvarme aktiv" + }, + "ch1_flow_enabled": { + "name": "Sentralvarmekrets 1 tur aktivert" + }, + "ch2_flow_enabled": { + "name": "Sentralvarmekrets 2 tur aktivert" + }, + "cooling_active": { + "name": "Kjøling aktiv" + }, + "diagnostic_event": { + "name": "Diagnosehendelse" + }, + "flame_active": { + "name": "Flamme aktiv" + }, + "hot_water_active": { + "name": "Varmtvann aktivt" + }, + "hw_enabled": { + "name": "Varmtvann aktivert" + } + }, + "sensor": { + "signal": { + "name": "Signal" + }, + "cloud": { + "name": "Sky" + }, + "heating_operation_mode": { + "name": "Driftsmodus for oppvarming" + }, + "heating": { + "name": "Oppvarming", + "state": { + "off": "Av", + "on": "På" + } + }, + "heating_channel": { + "name": "Varmekanal {channel}", + "state": { + "off": "Av", + "on": "På" + } + }, + "hot_water": { + "name": "Varmtvann", + "state": { + "off": "Av", + "on": "På" + } + }, + "hot_water_operation_mode": { + "name": "Driftsmodus for varmtvann" + }, + "target_temperature": { + "name": "Måltemperatur" + }, + "floor_temperature": { + "name": "Gulvtemperatur" + }, + "boiler_flow_temperature": { + "name": "Kjelens turtemperatur" + }, + "boiler_return_temperature": { + "name": "Kjelens returtemperatur" + }, + "flame_statistics": { + "name": "Flammestatistikk" + }, + "boiler_ch_setpoint": { + "name": "Kjelens sentralvarmesettpunkt" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Nedre grense for kjelens sentralvarmesettpunkt" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Øvre grense for kjelens sentralvarmesettpunkt" + }, + "boiler_exhaust_temperature": { + "name": "Kjelens avgastemperatur" + }, + "boiler_hw_setpoint": { + "name": "Kjelens varmtvannssettpunkt" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Nedre grense for kjelens varmtvannssettpunkt" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Øvre grense for kjelens varmtvannssettpunkt" + }, + "ch1_flow_setpoint": { + "name": "Settpunkt for sentralvarmekrets 1 tur" + }, + "ch2_flow_setpoint": { + "name": "Settpunkt for sentralvarmekrets 2 tur" + }, + "ch_flow_active_lower_setpoint": { + "name": "Aktivt nedre settpunkt for sentralvarmetur" + }, + "ch_flow_active_upper_setpoint": { + "name": "Aktivt øvre settpunkt for sentralvarmetur" + }, + "ch_pressure_bar": { + "name": "Sentralvarmetrykk" + }, + "connection_status": { + "name": "Tilkoblingsstatus" + }, + "coprocessor_update_status": { + "name": "Oppdateringsstatus for OpenTherm-koprosessor" + }, + "coprocessor_version": { + "name": "Versjon av OpenTherm-koprosessor" + }, + "delta_t": { + "name": "Delta-T" + }, + "estimated_boiler_output": { + "name": "Anslått kjeleeffekt" + }, + "hw_flow_rate": { + "name": "Varmtvannsstrøm" + }, + "hw_flow_setpoint": { + "name": "Settpunkt for varmtvannstur" + }, + "hw_temperature": { + "name": "Varmtvannstemperatur" + }, + "maximum_capacity_kw": { + "name": "Maksimal kjelekapasitet" + }, + "minimum_modulation_level": { + "name": "Minste modulasjonsnivå" + }, + "operating_mode": { + "name": "Driftsmodus" + }, + "relative_modulation_level": { + "name": "Relativt modulasjonsnivå" + }, + "room_setpoint": { + "name": "Romsettpunkt" + }, + "room_temperature": { + "name": "Romtemperatur" + }, + "slave_status": { + "name": "OpenTherm-slavestatus" + }, + "tracked_room_id": { + "name": "ID for sporet rom" + }, + "heating_demand": { + "name": "Varmebehov" + }, + "heating_demand_channel": { + "name": "Varmebehovskanal {channel}" + }, + "hot_water_demand": { + "name": "Varmtvannsbehov" + }, + "total_energy": { + "name": "Total energi" + }, + "energy_delivered": { + "name": "Levert energi" + }, + "energy_received": { + "name": "Mottatt energi" + }, + "equipment_power": { + "name": "Utstyr" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} starter oppvarming", + "stopped_heating": "{entity_name} stopper oppvarming", + "boosted": "{entity_name} er boostet", + "target_temperature_increased": "Måltemperaturen for {entity_name} ble økt", + "target_temperature_decreased": "Måltemperaturen for {entity_name} ble redusert" + } + } +} diff --git a/custom_components/wiser/translations/nl.json b/custom_components/wiser/translations/nl.json new file mode 100644 index 00000000..adf77ab3 --- /dev/null +++ b/custom_components/wiser/translations/nl.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Wiser Heat Hub instellen", + "description": "Voer het IP-adres en de geheime sleutel in die u van de hub hebt verkregen.", + "data": { + "host": "IP-adres of hostnaam", + "port": "Poortnummer", + "password": "Geheime sleutel" + } + }, + "zeroconf_confirm": { + "title": "Wiser Heat Hub instellen ({name})", + "description": "Voer de geheime sleutel in die u van de hub hebt verkregen.", + "data": { + "host": "IP-adres of hostnaam", + "port": "Poortnummer", + "password": "Geheime sleutel" + } + } + }, + "abort": { + "already_in_progress": "De configuratie van de Wiser Heat Hub wordt al uitgevoerd.", + "already_configured": "Dit apparaat is al geconfigureerd.", + "not_wiser_hub": "Het ontdekte apparaat is geen Wiser Heat Hub." + }, + "error": { + "invalid_hot_water_presets": "Een of meer warmwatervoorinstellingen zijn ongeldig.", + "auth_failure": "Kan niet verifiëren met de Wiser-hub. Controleer de geheime sleutel en probeer het opnieuw.", + "timeout_error": "Er is een time-out opgetreden bij het verbinden met de Wiser-hub. Controleer het IP-adres en probeer het opnieuw.", + "timeout_error_discovery": "Er is een time-out opgetreden bij het verbinden met de Wiser-hub. Controleer of uw hub nog steeds online is en probeer het opnieuw.", + "unknown": "Onbekende fout bij het verbinden met de Wiser-hub. Controleer de logboeken." + } + }, + "options": { + "step": { + "init": { + "title": "Opties voor de Wiser-integratie", + "description": "Selecteer parameters om te wijzigen", + "menu_options": { + "main_params": "Hoofdparameters", + "automation_params": "Automatiseringsparameters", + "opentherm": "OpenTherm", + "equipment_sensors": "Apparatuursensoren", + "ui_options": "Opties voor de gebruikersinterface" + } + }, + "ui_options": { + "title": "Opties voor de gebruikersinterface", + "data": { + "legacy_naming": "Oude naamgeving", + "show_wiser_sidebar": "Toon Wiser in zijbalk" + }, + "data_description": { + "legacy_naming": "Neem kamernamen op in de namen van Wiser-apparaten. Indien ingeschakeld, worden gebiedsnamen gedupliceerd wanneer Home Assistant entiteits-ID's opnieuw genereert met de standaardindeling via Instellingen → Systeem → Indeling entiteits-ID (Home Assistant 2026.8 en later) en de namen van het gebied en de Wiser-kamer overeenkomen. Als u deze optie wijzigt, worden bestaande entiteits-ID's niet hernoemd." + } + }, + "main_params": { + "title": "Opties voor de Wiser-integratie", + "description": "Hoofdparameters", + "data": { + "host": "IP-adres of hostnaam", + "port": "Poortnummer", + "scan_interval": "Update-interval (seconden)", + "heating_boost_temp": "Standaard verwarmingsboosttemperatuur (°C)", + "heating_boost_time": "Standaard verwarmingsboostduur (min.)", + "hotwater_boost_time": "Standaard warmwaterboostduur (min.)", + "setpoint_mode": "Instelpuntmodus", + "restore_manual_temp_option": "Gebruik deze temperatuur als er geen handmatige temperatuur is opgeslagen" + } + }, + "automation_params": { + "title": "Opties voor de Wiser-integratie", + "description": "Ingebouwde automatiseringen inschakelen", + "sections": { + "automations_passive_mode": { + "name": "Passieve modus", + "description": "Passieve TRV-mogelijkheden inschakelen. Elke kamer moet worden ingeschakeld om te kunnen functioneren.", + "data": { + "automations_passive_mode": "Passieve modus", + "passive_mode_temperature_increments": "Temperatuurstappen voor passieve modus" + } + }, + "hotwater_climate": { + "name": "Warmwaterklimaatregeling", + "description": "Schakel klimaatregeling voor warm water in. Zie de wiki voor details.", + "data": { + "hotwater_climate": "Schakel de klimaatentiteit in", + "experimental_mode": "Experimentele bedrijfsmodus inschakelen", + "hotwater_auto_mode": "Automatische modus voor warm water", + "hotwater_heat_mode": "Modus voor warm water", + "hotwater_boost_mode": "Boostmodus voor warm water", + "hotwater_sensor_entity_id": "Warmwatertemperatuursensor" + } + }, + "additional_sensors": { + "name": "Extra sensoren", + "description": "Maak optionele sensoren beschikbaar voor gegevens die Wiser rapporteert.", + "data": { + "schedule_setpoint_rooms": "Ruimtes zonder klimaatregeling" + }, + "data_description": { + "schedule_setpoint_rooms": "Selecteer de Wiser-ruimtes waarvoor een sensor voor de huidige schematemperatuur moet worden aangemaakt." + } + } + } + }, + "equipment_sensors": { + "title": "Apparatuursensoren", + "description": "Configureer gedetailleerde apparatuurgegevensentiteiten voor compatibele apparaten.", + "data": { + "equipment_sensors": "Schakel apparatuurgegevenssensoren in" + }, + "data_description": { + "equipment_sensors": "Maakt een extra sensor beschikbaar met gedetailleerde apparatuurkenmerken voor elke compatibele slimme stekker, PowerTag en verwarmingsactuator." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Configureer OpenTherm-functies.", + "menu_options": { + "opentherm_hot_water_control": "Warmwaterregeling", + "opentherm_sensors": "Sensoren" + } + }, + "opentherm_sensors": { + "title": "OpenTherm-sensoren", + "description": "Kies een categorie gedetecteerde OpenTherm-attributen.", + "menu_options": { + "opentherm_readings": "Uitlezingen", + "opentherm_central_heating": "Centrale verwarming", + "opentherm_hot_water": "Warm water", + "opentherm_status": "Status en diagnostiek" + } + }, + "opentherm_hot_water_control": { + "title": "OpenTherm-warmwaterregeling", + "description": "Regel optioneel het tapwater-setpoint van de ketel via Wiser. De integratie maakt een OpenTherm Hot Water-schema aan in het Wiser Schedules-paneel. Uit gebruikt de minimale warmwatertemperatuur die OpenTherm rapporteert.", + "data": { + "opentherm_hot_water_control": "Warmwaterregeling inschakelen", + "opentherm_hot_water_manual_temperature": "Handmatige temperatuur", + "opentherm_hot_water_sensor_entity_id": "Optionele externe temperatuursensor.", + "opentherm_hot_water_presets": "Voorinstellingen" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperatuur die in de handmatige modus wordt gebruikt.", + "opentherm_hot_water_sensor_entity_id": "Optionele temperatuursensor voor de huidige watertemperatuur. OpenTherm wordt gebruikt wanneer de sensor niet beschikbaar is." + } + }, + "opentherm_readings": { + "title": "OpenTherm uitlezingen", + "description": "Kies welke gedetecteerde OpenTherm-metingen worden weergegeven als sensorentiteiten.", + "data": { + "ch_flow_temperature": "Ketelaanvoertemperatuur", + "ch_pressure_bar": "CV-druk", + "ch_return_temperature": "Ketelretourtemperatuur", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Rookgastemperatuur van de ketel", + "estimated_boiler_output": "Geschat ketelvermogen", + "hw_flow_rate": "Warmwaterdebiet", + "hw_temperature": "Warmwatertemperatuur", + "maximum_capacity_kw": "Maximaal ketelvermogen", + "minimum_modulation_level": "Minimaal modulatieniveau", + "relative_modulation_level": "Relatief modulatieniveau", + "room_temperature": "Kamertemperatuur" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm centrale verwarming", + "description": "Kies welke gedetecteerde kenmerken voor centrale verwarming als entiteiten worden weergegeven.", + "data": { + "boiler_ch_max_setpoint_read_write": "Maximale CV-instelwaarde ketel lezen/schrijven", + "boiler_ch_max_setpoint_transfer_enable": "Overdracht maximale CV-instelwaarde ketel ingeschakeld", + "boiler_ch_setpoint": "CV-instelwaarde ketel", + "boiler_ch_setpoint_lower_bound": "Ondergrens CV-instelwaarde ketel", + "boiler_ch_setpoint_upper_bound": "Bovengrens CV-instelwaarde ketel", + "ch_flow_active_lower_setpoint": "Actieve onderste CV-aanvoerinstelwaarde", + "ch_flow_active_upper_setpoint": "Actieve bovenste CV-aanvoerinstelwaarde", + "ch1_flow_enabled": "CH1-debiet ingeschakeld", + "ch1_flow_setpoint": "CV1-aanvoerinstelwaarde", + "ch2_flow_enabled": "CH2-debiet ingeschakeld", + "ch2_flow_setpoint": "CV2-aanvoerinstelwaarde", + "room_setpoint": "Kamerinstelwaarde" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm warm water", + "description": "Kies welke gedetecteerde warmwaterkenmerken als entiteiten worden weergegeven.", + "data": { + "boiler_hw_setpoint": "Ketelwarmwaterinstelpunt", + "boiler_hw_setpoint_lower_bound": "Ketelwarmwaterinstelpunt ondergrens", + "boiler_hw_setpoint_read_write": "Ketelwarmwaterinstelpunt lezen/schrijven", + "boiler_hw_setpoint_transfer_enable": "Ketelwarmwaterinstelpuntoverdracht ingeschakeld", + "boiler_hw_setpoint_upper_bound": "Ketelwarmwaterinstelpunt bovengrens", + "hw_enabled": "Warm water ingeschakeld", + "hw_flow_setpoint": "Instelpunt warmwaterdebiet" + } + }, + "opentherm_status": { + "title": "OpenTherm status en diagnostiek", + "description": "Kies welke gedetecteerde status en diagnostische kenmerken worden weergegeven als sensorentiteiten.", + "data": { + "boiler_fault": "Ketelstoring", + "central_heating_active": "Centrale verwarming actief", + "central_heating_2_active": "Centrale verwarming 2 actief", + "connection_status": "Verbindingsstatus", + "coprocessor_update_status": "Updatestatus OpenTherm-coprocessor", + "coprocessor_version": "OpenTherm coprocessorversie", + "cooling_active": "Koeling actief", + "diagnostic_event": "Diagnosegebeurtenis", + "flame_active": "Vlam actief", + "flame_statistics": "Vlamstatistieken", + "hot_water_active": "Warm water actief", + "operating_mode": "Bedrijfsmodus", + "slave_status": "OpenTherm slave-status", + "tracked_room_id": "ID van gevolgde kamer" + } + } + }, + "abort": { + "opentherm_not_available": "OpenTherm-gegevens zijn niet beschikbaar voor deze Wiser-hub.", + "equipment_not_available": "Apparatuurgegevens zijn niet beschikbaar voor deze Wiser-hub." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "OpenTherm-parameter instellen", + "description": "Wijzigt een ondersteunde OpenTherm-temperatuurparameter op de Wiser-hub.", + "fields": { + "parameter": { + "name": "Parameter", + "description": "OpenTherm-temperatuurparameter die moet worden gewijzigd. Het vereiste API-eindpunt wordt automatisch geselecteerd." + }, + "temperature": { + "name": "Temperatuur", + "description": "Gebruik een temperatuur binnen de grenzen die door uw ketel worden gerapporteerd." + }, + "hub": { + "name": "Wiser-hub", + "description": "Selecteer alleen een hub als u meerdere Wiser-hubs hebt." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Actieve CV-onderinstelling", + "chFlowActiveUpperSetpoint": "Actieve CV-boveninstelling", + "dhwFlowSetpoint": "Hub warmwateraanvoersetpoint", + "dhwSetpoint": "Ketel warmwatersetpoint", + "maxChSetpoint": "Ketel maximale CV-setpoint" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Geef een OpenTherm-temperatuur op." + }, + "opentherm_hub_not_found": { + "message": "De opgegeven Wiser-hub is niet gevonden." + }, + "opentherm_hub_not_loaded": { + "message": "De opgegeven Wiser-hub is niet geladen." + }, + "opentherm_hub_required": { + "message": "Selecteer een Wiser-hub als er meer dan één hub is geconfigureerd." + }, + "opentherm_not_supported": { + "message": "Deze hub heeft geen OpenTherm-functionaliteit." + }, + "opentherm_invalid_parameter": { + "message": "De parameter of waarde OpenTherm is ongeldig: {error}" + }, + "opentherm_write_failed": { + "message": "Kan OpenTherm-parameter {parameter} niet instellen: {error}" + }, + "opentherm_confirmation_failed": { + "message": "OpenTherm-parameter {parameter} bleef na één nieuwe poging en de bevestigingsperiode van {window} seconden niet op {requested} °C. Wiser rapporteert {reported} °C. Er worden geen verdere pogingen verzonden." + }, + "opentherm_retry_failed": { + "message": "OpenTherm-parameter {parameter} bleef niet op {requested} °C en de nieuwe poging kon niet worden verzonden: {error}. Wiser rapporteert {reported} °C. Er worden geen verdere pogingen verzonden." + }, + "opentherm_notification_title": { + "message": "Wiser OpenTherm-opdracht" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Verwarming" + }, + "floor_temperature": { + "name": "Vloertemperatuur" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Doeltemperatuur afwezigheidsmodus" + }, + "floor_temperature_offset": { + "name": "Offset vloertemperatuur" + }, + "indoor_discomfort_temperature": { + "name": "Ongemakstemperatuur binnen" + }, + "outdoor_discomfort_temperature": { + "name": "Ongemakstemperatuur buiten" + } + }, + "select": { + "mode": { + "name": "Modus" + }, + "power_on_behavior": { + "name": "Inschakelgedrag" + }, + "led_indicator": { + "name": "LED-indicator" + } + }, + "button": { + "boost_all_heating": { + "name": "Alle verwarming boosten" + }, + "cancel_all_heating_overrides": { + "name": "Alle verwarmingsoverrides annuleren" + }, + "boost_hot_water": { + "name": "Warm water boosten" + }, + "cancel_hot_water_overrides": { + "name": "Warmwateroverrides annuleren" + }, + "toggle_hot_water": { + "name": "Warm water omschakelen" + }, + "moment": { + "name": "Moment: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Klepbescherming" + }, + "eco_mode": { + "name": "Eco-modus" + }, + "away_mode_affects_hot_water": { + "name": "Afwezig-modus beïnvloedt warm water" + }, + "comfort_mode": { + "name": "Comfort-modus" + }, + "away_mode": { + "name": "Afwezig-modus" + }, + "daylight_saving": { + "name": "Zomertijd" + }, + "summer_comfort_enabled": { + "name": "Zomercomfort ingeschakeld" + }, + "window_detection": { + "name": "Raamdetectie" + }, + "include_in_summer_comfort": { + "name": "Meenemen in zomercomfort" + }, + "device_lock": { + "name": "Apparaatvergrendeling" + }, + "identify": { + "name": "Identificeren" + }, + "outlet": { + "name": "Stopcontact" + }, + "away_mode_turns_off": { + "name": "Schakelt uit in afwezigheidsmodus" + }, + "away_mode_closes": { + "name": "Sluit in afwezigheidsmodus" + }, + "passive_mode": { + "name": "Passieve modus" + }, + "respect_summer_comfort": { + "name": "Zomercomfort respecteren" + }, + "interacts_with_room_climate": { + "name": "Heeft invloed op het binnenklimaat" + }, + "humidity_interacts_with_room_climate": { + "name": "Vochtigheid heeft invloed op het binnenklimaat" + }, + "temperature_interacts_with_room_climate": { + "name": "Temperatuur heeft invloed op het binnenklimaat" + }, + "illuminance_interacts_with_room_climate": { + "name": "Verlichtingssterkte heeft invloed op het binnenklimaat" + }, + "sensor_interacts_with_room_climate": { + "name": "{sensor} heeft invloed op het binnenklimaat" + }, + "manual_heat": { + "name": "Handmatige verwarming" + }, + "hot_water": { + "name": "Warm water" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Preventie van zomerongemakken" + }, + "summer_comfort_available": { + "name": "Zomercomfort beschikbaar" + }, + "pcm_device_limit_reached": { + "name": "PCM-apparaatlimiet bereikt" + }, + "smoke_alarm": { + "name": "Rookmelder" + }, + "heat_alarm": { + "name": "Hittealarm" + }, + "tamper_alarm": { + "name": "Sabotagealarm" + }, + "fault_warning": { + "name": "Foutwaarschuwing" + }, + "remote_alarm": { + "name": "Alarm op afstand" + }, + "battery_defect": { + "name": "Batterij defect" + }, + "controllable": { + "name": "Bestuurbaar" + }, + "pcm_mode": { + "name": "PCM-modus" + }, + "window_detection_active": { + "name": "Raamdetectie actief" + }, + "is_dimmable": { + "name": "Dimbaar" + }, + "is_led_indicator_supported": { + "name": "LED-indicator ondersteund" + }, + "is_output_mode_supported": { + "name": "Uitgangsmodus ondersteund" + }, + "is_power_on_behaviour_supported": { + "name": "Inschakelgedrag ondersteund" + }, + "is_tilt_supported": { + "name": "Kantelfunctie ondersteund" + }, + "is_open": { + "name": "Open" + }, + "is_closed": { + "name": "Gesloten" + }, + "active": { + "name": "Actief" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Maximale CV-instelwaarde ketel lezen/schrijven" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Overdracht maximale CV-instelwaarde ketel ingeschakeld" + }, + "boiler_fault": { + "name": "Ketelstoring" + }, + "boiler_hw_setpoint_read_write": { + "name": "Warmwaterinstelwaarde ketel lezen/schrijven" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Overdracht warmwaterinstelwaarde ketel ingeschakeld" + }, + "central_heating_2_active": { + "name": "Centrale verwarming 2 actief" + }, + "central_heating_active": { + "name": "Centrale verwarming actief" + }, + "ch1_flow_enabled": { + "name": "CV1-aanvoer ingeschakeld" + }, + "ch2_flow_enabled": { + "name": "CV2-aanvoer ingeschakeld" + }, + "cooling_active": { + "name": "Koeling actief" + }, + "diagnostic_event": { + "name": "Diagnosegebeurtenis" + }, + "flame_active": { + "name": "Vlam actief" + }, + "hot_water_active": { + "name": "Warm water actief" + }, + "hw_enabled": { + "name": "Warm water ingeschakeld" + } + }, + "sensor": { + "signal": { + "name": "Signaal" + }, + "cloud": { + "name": "Cloud" + }, + "heating_operation_mode": { + "name": "Verwarmingsmodus" + }, + "heating": { + "name": "Verwarming", + "state": { + "off": "Uit", + "on": "Aan" + } + }, + "heating_channel": { + "name": "Verwarmingskanaal {channel}", + "state": { + "off": "Uit", + "on": "Aan" + } + }, + "hot_water": { + "name": "Warm water", + "state": { + "off": "Uit", + "on": "Aan" + } + }, + "hot_water_operation_mode": { + "name": "Warmwaterbedrijfsmodus" + }, + "target_temperature": { + "name": "Streeftemperatuur" + }, + "floor_temperature": { + "name": "Vloertemperatuur" + }, + "boiler_flow_temperature": { + "name": "Ketelaanvoertemperatuur" + }, + "boiler_return_temperature": { + "name": "Ketelretourtemperatuur" + }, + "flame_statistics": { + "name": "Vlamstatistieken" + }, + "boiler_ch_setpoint": { + "name": "CV-instelwaarde ketel" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Ondergrens CV-instelwaarde ketel" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Bovengrens CV-instelwaarde ketel" + }, + "boiler_exhaust_temperature": { + "name": "Rookgastemperatuur van de ketel" + }, + "boiler_hw_setpoint": { + "name": "Warmwaterinstelwaarde ketel" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Ketelwarmwaterinstelpunt ondergrens" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Ketelwarmwaterinstelpunt bovengrens" + }, + "ch1_flow_setpoint": { + "name": "CV1-aanvoerinstelwaarde" + }, + "ch2_flow_setpoint": { + "name": "CV2-aanvoerinstelwaarde" + }, + "ch_flow_active_lower_setpoint": { + "name": "Actieve onderste CV-aanvoerinstelwaarde" + }, + "ch_flow_active_upper_setpoint": { + "name": "Actieve bovenste CV-aanvoerinstelwaarde" + }, + "ch_pressure_bar": { + "name": "CV-druk" + }, + "connection_status": { + "name": "Verbindingsstatus" + }, + "coprocessor_update_status": { + "name": "Updatestatus OpenTherm-coprocessor" + }, + "coprocessor_version": { + "name": "OpenTherm coprocessorversie" + }, + "delta_t": { + "name": "Delta-T" + }, + "estimated_boiler_output": { + "name": "Geschat ketelvermogen" + }, + "hw_flow_rate": { + "name": "Warmwaterdebiet" + }, + "hw_flow_setpoint": { + "name": "Instelpunt warmwaterdebiet" + }, + "hw_temperature": { + "name": "Warmwatertemperatuur" + }, + "maximum_capacity_kw": { + "name": "Maximaal ketelvermogen" + }, + "minimum_modulation_level": { + "name": "Minimaal modulatieniveau" + }, + "operating_mode": { + "name": "Bedrijfsmodus" + }, + "relative_modulation_level": { + "name": "Relatief modulatieniveau" + }, + "room_setpoint": { + "name": "Kamerinstelwaarde" + }, + "room_temperature": { + "name": "Kamertemperatuur" + }, + "slave_status": { + "name": "OpenTherm slave-status" + }, + "tracked_room_id": { + "name": "ID van gevolgde kamer" + }, + "heating_demand": { + "name": "Verwarmingsvraag" + }, + "heating_demand_channel": { + "name": "Verwarmingsvraagkanaal {channel}" + }, + "hot_water_demand": { + "name": "Warmwatervraag" + }, + "total_energy": { + "name": "Totale energie" + }, + "energy_delivered": { + "name": "Geleverde energie" + }, + "energy_received": { + "name": "Ontvangen energie" + }, + "equipment_power": { + "name": "Apparatuur" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} begint met verwarmen", + "stopped_heating": "{entity_name} stopt met verwarmen", + "boosted": "{entity_name} wordt geboost", + "target_temperature_increased": "Doeltemperatuur van {entity_name} verhoogd", + "target_temperature_decreased": "Doeltemperatuur van {entity_name} verlaagd" + } + } +} diff --git a/custom_components/wiser/translations/pt.json b/custom_components/wiser/translations/pt.json new file mode 100644 index 00000000..28aad363 --- /dev/null +++ b/custom_components/wiser/translations/pt.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Configuração do Heat Hub Wiser", + "description": "Introduza o endereço IP e a chave secreta obtidos do hub.", + "data": { + "host": "Endereço IP ou nome de host", + "port": "Número da porta", + "password": "Chave secreta" + } + }, + "zeroconf_confirm": { + "title": "Configuração do Heat Hub Wiser ({name})", + "description": "Introduza a chave secreta obtida do hub.", + "data": { + "host": "Endereço IP ou nome de host", + "port": "Número da porta", + "password": "Chave secreta" + } + } + }, + "abort": { + "already_in_progress": "A configuração do Heat Hub Wiser já está em curso.", + "already_configured": "Este dispositivo já está configurado.", + "not_wiser_hub": "O dispositivo descoberto não é um Heat Hub Wiser." + }, + "error": { + "invalid_hot_water_presets": "Uma ou mais predefinições de água quente são inválidas.", + "auth_failure": "Não foi possível autenticar no hub Wiser. Verifique a chave secreta e tente novamente.", + "timeout_error": "O tempo limite foi excedido ao estabelecer ligação ao hub Wiser. Verifique o endereço IP e tente novamente.", + "timeout_error_discovery": "O tempo limite foi excedido ao estabelecer ligação ao hub Wiser. Verifique se o hub continua online e tente novamente.", + "unknown": "Ocorreu um erro desconhecido ao estabelecer ligação ao hub Wiser. Consulte os registos." + } + }, + "options": { + "step": { + "init": { + "title": "Opções da integração Wiser", + "description": "Selecione parâmetros para alterar", + "menu_options": { + "main_params": "Parâmetros principais", + "automation_params": "Parâmetros de automação", + "opentherm": "OpenTherm", + "equipment_sensors": "Sensores de equipamento", + "ui_options": "Opções da interface do utilizador" + } + }, + "ui_options": { + "title": "Opções da interface do utilizador", + "data": { + "legacy_naming": "Nomenclatura legada", + "show_wiser_sidebar": "Mostrar Wiser na barra lateral" + }, + "data_description": { + "legacy_naming": "Inclui os nomes das divisões nos nomes dos dispositivos Wiser. Se estiver ativado, ao utilizar o Home Assistant para regenerar os ID de entidade com o formato predefinido em Definições → Sistema → Formato do ID de entidade (Home Assistant 2026.8 e posterior), os nomes das áreas serão duplicados quando coincidirem com os nomes das divisões Wiser. Alterar esta opção não muda o nome dos ID de entidade existentes." + } + }, + "main_params": { + "title": "Opções da integração Wiser", + "description": "Parâmetros principais", + "data": { + "host": "Endereço IP ou nome do host", + "port": "Número da porta", + "scan_interval": "Intervalo de atualização (segundos)", + "heating_boost_temp": "Temperatura padrão do reforço de aquecimento (°C)", + "heating_boost_time": "Duração padrão do reforço de aquecimento (minutos)", + "hotwater_boost_time": "Duração padrão do reforço de água quente (minutos)", + "setpoint_mode": "Modo do valor de referência", + "restore_manual_temp_option": "Utilizar esta temperatura se não existir uma temperatura manual guardada" + } + }, + "automation_params": { + "title": "Opções da integração Wiser", + "description": "Habilitar automações integradas", + "sections": { + "automations_passive_mode": { + "name": "Modo passivo", + "description": "Ativar a funcionalidade TRV passiva. É necessário ativá-la individualmente em cada divisão.", + "data": { + "automations_passive_mode": "Modo passivo", + "passive_mode_temperature_increments": "Incrementos de temperatura no modo passivo" + } + }, + "hotwater_climate": { + "name": "Controle climático de água quente", + "description": "Ativa o controle climático de água quente. Veja o wiki para detalhes.", + "data": { + "hotwater_climate": "Ativar a entidade de climatização", + "experimental_mode": "Ativar o modo de funcionamento experimental", + "hotwater_auto_mode": "Modo automático de água quente", + "hotwater_heat_mode": "Modo de aquecimento de água quente", + "hotwater_boost_mode": "Modo de reforço de água quente", + "hotwater_sensor_entity_id": "Sensor de temperatura de água quente" + } + }, + "additional_sensors": { + "name": "Sensores adicionais", + "description": "Exponha sensores opcionais para os dados informados pelo Wiser.", + "data": { + "schedule_setpoint_rooms": "Divisões sem controle de climatização" + }, + "data_description": { + "schedule_setpoint_rooms": "Selecione as divisões Wiser onde deve ser criado um sensor de temperatura da programação atual." + } + } + } + }, + "equipment_sensors": { + "title": "Sensores de equipamento", + "description": "Configure entidades de dados de equipamento detalhadas para dispositivos compatíveis.", + "data": { + "equipment_sensors": "Habilitar sensores de dados do equipamento" + }, + "data_description": { + "equipment_sensors": "Disponibiliza um sensor adicional com atributos detalhados do equipamento para cada tomada inteligente, PowerTag e atuador de aquecimento compatível." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Configure as funcionalidades OpenTherm.", + "menu_options": { + "opentherm_hot_water_control": "Controle de água quente", + "opentherm_sensors": "Sensores" + } + }, + "opentherm_sensors": { + "title": "Sensores OpenTherm", + "description": "Escolha uma categoria de atributos OpenTherm detectados.", + "menu_options": { + "opentherm_readings": "Leituras", + "opentherm_central_heating": "Aquecimento central", + "opentherm_hot_water": "Água quente", + "opentherm_status": "Estado e diagnóstico" + } + }, + "opentherm_hot_water_control": { + "title": "Controle de água quente OpenTherm", + "description": "Controle opcionalmente o ponto de ajuste de água quente sanitária da caldeira através do Wiser. A integração cria uma programação OpenTherm Hot Water no painel Wiser Schedules. Desligado usa a temperatura mínima de água quente informada pelo OpenTherm.", + "data": { + "opentherm_hot_water_control": "Ativar o controle de água quente", + "opentherm_hot_water_manual_temperature": "Temperatura manual", + "opentherm_hot_water_sensor_entity_id": "Sensor de temperatura externo opcional.", + "opentherm_hot_water_presets": "Predefinições" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperatura usada pelo modo manual.", + "opentherm_hot_water_sensor_entity_id": "Sensor de temperatura opcional usado para a temperatura atual da água. O OpenTherm é usado quando o sensor não está disponível." + } + }, + "opentherm_readings": { + "title": "Leituras OpenTherm", + "description": "Escolha quais leituras OpenTherm detetadas são apresentadas como entidades de sensor.", + "data": { + "ch_flow_temperature": "Temperatura de fluxo da caldeira", + "ch_pressure_bar": "Pressão CH", + "ch_return_temperature": "Temperatura de retorno da caldeira", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Temperatura de exaustão da caldeira", + "estimated_boiler_output": "Potência estimada da caldeira", + "hw_flow_rate": "Caudal de água quente", + "hw_temperature": "Temperatura da água quente", + "maximum_capacity_kw": "Capacidade máxima da caldeira", + "minimum_modulation_level": "Nível mínimo de modulação", + "relative_modulation_level": "Nível de modulação relativa", + "room_temperature": "Temperatura ambiente" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm aquecimento central", + "description": "Escolha quais atributos de aquecimento central detectados são expostos como entidades.", + "data": { + "boiler_ch_max_setpoint_read_write": "Leitura/escrita do ponto de ajuste máximo do CH da caldeira", + "boiler_ch_max_setpoint_transfer_enable": "Transferência máxima do ponto de ajuste do CH da caldeira habilitada", + "boiler_ch_setpoint": "Ponto de ajuste do CH da caldeira", + "boiler_ch_setpoint_lower_bound": "Limite inferior do ponto de ajuste do CH da caldeira", + "boiler_ch_setpoint_upper_bound": "Limite superior do ponto de ajuste do CH da caldeira", + "ch_flow_active_lower_setpoint": "Ponto de ajuste inferior ativo de fluxo CH", + "ch_flow_active_upper_setpoint": "Ponto de ajuste superior ativo de fluxo CH", + "ch1_flow_enabled": "Fluxo CH1 habilitado", + "ch1_flow_setpoint": "Ponto de ajuste de fluxo CH1", + "ch2_flow_enabled": "Fluxo CH2 habilitado", + "ch2_flow_setpoint": "Valor de referência do fluxo CH2", + "room_setpoint": "Ponto de ajuste da sala" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm água quente", + "description": "Escolha quais atributos de água quente detectados são expostos como entidades.", + "data": { + "boiler_hw_setpoint": "Ponto de ajuste de água quente da caldeira", + "boiler_hw_setpoint_lower_bound": "Limite inferior do ponto de ajuste de água quente da caldeira", + "boiler_hw_setpoint_read_write": "Leitura/escrita do ponto de ajuste de água quente da caldeira", + "boiler_hw_setpoint_transfer_enable": "Transferência do ponto de ajuste de água quente da caldeira habilitada", + "boiler_hw_setpoint_upper_bound": "Limite superior do valor de referência de água quente da caldeira", + "hw_enabled": "Água quente habilitada", + "hw_flow_setpoint": "Ponto de ajuste do fluxo de água quente" + } + }, + "opentherm_status": { + "title": "OpenTherm status e diagnóstico", + "description": "Escolha quais status detectados e atributos de diagnóstico são expostos como entidades de sensor.", + "data": { + "boiler_fault": "Avaria na caldeira", + "central_heating_active": "Aquecimento central ativo", + "central_heating_2_active": "Aquecimento central 2 ativo", + "connection_status": "Estado da ligação", + "coprocessor_update_status": "Estado de atualização do coprocessador OpenTherm", + "coprocessor_version": "Versão do coprocessador OpenTherm", + "cooling_active": "Arrefecimento ativo", + "diagnostic_event": "Evento de diagnóstico", + "flame_active": "Chama ativa", + "flame_statistics": "Estatísticas de chama", + "hot_water_active": "Água quente ativa", + "operating_mode": "Modo de operação", + "slave_status": "Estado do dispositivo secundário OpenTherm", + "tracked_room_id": "ID da divisão monitorizada" + } + } + }, + "abort": { + "opentherm_not_available": "Os dados OpenTherm não estão disponíveis para este hub Wiser.", + "equipment_not_available": "Os dados do equipamento não estão disponíveis para este hub Wiser." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "Definir parâmetro OpenTherm", + "description": "Altera um parâmetro de temperatura OpenTherm compatível no hub Wiser.", + "fields": { + "parameter": { + "name": "Parâmetro", + "description": "Parâmetro de temperatura OpenTherm a alterar. O endpoint da API necessário é selecionado automaticamente." + }, + "temperature": { + "name": "Temperatura", + "description": "Utilize uma temperatura dentro dos limites informados pela sua caldeira." + }, + "hub": { + "name": "Hub Wiser", + "description": "Selecione um hub somente quando você tiver vários hubs Wiser." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Ponto de ajuste inferior de aquecimento central ativo", + "chFlowActiveUpperSetpoint": "Ponto de ajuste superior de aquecimento central ativo", + "dhwFlowSetpoint": "Ponto de ajuste de fluxo de água quente do hub", + "dhwSetpoint": "Ponto de ajuste de água quente da caldeira", + "maxChSetpoint": "Ponto de ajuste máximo de aquecimento central da caldeira" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Forneça uma temperatura OpenTherm." + }, + "opentherm_hub_not_found": { + "message": "O hub Wiser especificado não foi localizado." + }, + "opentherm_hub_not_loaded": { + "message": "O hub Wiser especificado não está carregado." + }, + "opentherm_hub_required": { + "message": "Selecione um hub Wiser quando mais de um hub estiver configurado." + }, + "opentherm_not_supported": { + "message": "Este hub não possui a funcionalidade OpenTherm." + }, + "opentherm_invalid_parameter": { + "message": "O parâmetro ou valor OpenTherm é inválido: {error}" + }, + "opentherm_write_failed": { + "message": "Não é possível definir o parâmetro OpenTherm {parameter}: {error}" + }, + "opentherm_confirmation_failed": { + "message": "O parâmetro OpenTherm {parameter} não permaneceu em {requested} °C após uma nova tentativa e o período de confirmação de {window} segundos. O Wiser indica {reported} °C. Não serão efetuadas mais tentativas." + }, + "opentherm_retry_failed": { + "message": "O parâmetro OpenTherm {parameter} não permaneceu em {requested} °C e não foi possível enviar a nova tentativa: {error}. O Wiser indica {reported} °C. Não serão efetuadas mais tentativas." + }, + "opentherm_notification_title": { + "message": "Comando OpenTherm do Wiser" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Aquecimento" + }, + "floor_temperature": { + "name": "Temperatura do piso" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Temperatura alvo do modo ausente" + }, + "floor_temperature_offset": { + "name": "Compensação da temperatura do piso" + }, + "indoor_discomfort_temperature": { + "name": "Temperatura de desconforto interno" + }, + "outdoor_discomfort_temperature": { + "name": "Temperatura de desconforto externo" + } + }, + "select": { + "mode": { + "name": "Modo" + }, + "power_on_behavior": { + "name": "Comportamento de inicialização" + }, + "led_indicator": { + "name": "Indicador LED" + } + }, + "button": { + "boost_all_heating": { + "name": "Reforçar todo o aquecimento" + }, + "cancel_all_heating_overrides": { + "name": "Cancelar todas as substituições de aquecimento" + }, + "boost_hot_water": { + "name": "Reforçar a água quente" + }, + "cancel_hot_water_overrides": { + "name": "Cancelar substituições de água quente" + }, + "toggle_hot_water": { + "name": "Alternar água quente" + }, + "moment": { + "name": "Momento: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Proteção da válvula" + }, + "eco_mode": { + "name": "Modo Eco" + }, + "away_mode_affects_hot_water": { + "name": "O modo ausente afeta a água quente" + }, + "comfort_mode": { + "name": "Modo conforto" + }, + "away_mode": { + "name": "Modo ausente" + }, + "daylight_saving": { + "name": "Horário de verão" + }, + "summer_comfort_enabled": { + "name": "Conforto de verão habilitado" + }, + "window_detection": { + "name": "Detecção de janela" + }, + "include_in_summer_comfort": { + "name": "Incluir no conforto do verão" + }, + "device_lock": { + "name": "Bloqueio de dispositivo" + }, + "identify": { + "name": "Identificar" + }, + "outlet": { + "name": "Tomada" + }, + "away_mode_turns_off": { + "name": "Desliga no modo ausente" + }, + "away_mode_closes": { + "name": "Fecha no modo ausente" + }, + "passive_mode": { + "name": "Modo passivo" + }, + "respect_summer_comfort": { + "name": "Respeitar o conforto de verão" + }, + "interacts_with_room_climate": { + "name": "Interage com o clima ambiente" + }, + "humidity_interacts_with_room_climate": { + "name": "A umidade interage com o clima ambiente" + }, + "temperature_interacts_with_room_climate": { + "name": "A temperatura interage com o clima ambiente" + }, + "illuminance_interacts_with_room_climate": { + "name": "A iluminação interage com o clima ambiente" + }, + "sensor_interacts_with_room_climate": { + "name": "{sensor} interage com o clima ambiente" + }, + "manual_heat": { + "name": "Aquecimento manual" + }, + "hot_water": { + "name": "Água quente" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Prevenção de desconforto no verão" + }, + "summer_comfort_available": { + "name": "Conforto de verão disponível" + }, + "pcm_device_limit_reached": { + "name": "Limite do dispositivo PCM atingido" + }, + "smoke_alarm": { + "name": "Alarme de fumaça" + }, + "heat_alarm": { + "name": "Alarme de calor" + }, + "tamper_alarm": { + "name": "Alarme de violação" + }, + "fault_warning": { + "name": "Aviso de falha" + }, + "remote_alarm": { + "name": "Alarme remoto" + }, + "battery_defect": { + "name": "Defeito na bateria" + }, + "controllable": { + "name": "Controlável" + }, + "pcm_mode": { + "name": "Modo PCM" + }, + "window_detection_active": { + "name": "Detecção de janela ativa" + }, + "is_dimmable": { + "name": "Regulável" + }, + "is_led_indicator_supported": { + "name": "Indicador LED compatível" + }, + "is_output_mode_supported": { + "name": "Modo de saída compatível" + }, + "is_power_on_behaviour_supported": { + "name": "Comportamento de inicialização suportado" + }, + "is_tilt_supported": { + "name": "Inclinação suportada" + }, + "is_open": { + "name": "Aberto" + }, + "is_closed": { + "name": "Fechado" + }, + "active": { + "name": "Ativo" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Leitura/gravação do ponto de ajuste máximo do CH da caldeira" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Transferência máxima do ponto de ajuste do CH da caldeira habilitada" + }, + "boiler_fault": { + "name": "Falha da caldeira" + }, + "boiler_hw_setpoint_read_write": { + "name": "Leitura/escrita do ponto de ajuste de água quente da caldeira" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Transferência do ponto de ajuste de água quente da caldeira habilitada" + }, + "central_heating_2_active": { + "name": "Aquecimento central 2 ativo" + }, + "central_heating_active": { + "name": "Aquecimento central ativo" + }, + "ch1_flow_enabled": { + "name": "Fluxo CH1 habilitado" + }, + "ch2_flow_enabled": { + "name": "Fluxo CH2 habilitado" + }, + "cooling_active": { + "name": "Resfriamento ativo" + }, + "diagnostic_event": { + "name": "Evento de diagnóstico" + }, + "flame_active": { + "name": "Chama ativa" + }, + "hot_water_active": { + "name": "Água quente ativa" + }, + "hw_enabled": { + "name": "Água quente ativada" + } + }, + "sensor": { + "signal": { + "name": "Sinal" + }, + "cloud": { + "name": "Nuvem" + }, + "heating_operation_mode": { + "name": "Modo de operação de aquecimento" + }, + "heating": { + "name": "Aquecimento", + "state": { + "off": "Desligado", + "on": "Ligado" + } + }, + "heating_channel": { + "name": "Canal de aquecimento {channel}", + "state": { + "off": "Desligado", + "on": "Ligado" + } + }, + "hot_water": { + "name": "Água quente", + "state": { + "off": "Desligado", + "on": "Ligado" + } + }, + "hot_water_operation_mode": { + "name": "Modo de operação de água quente" + }, + "target_temperature": { + "name": "Temperatura alvo" + }, + "floor_temperature": { + "name": "Temperatura do piso" + }, + "boiler_flow_temperature": { + "name": "Temperatura de fluxo da caldeira" + }, + "boiler_return_temperature": { + "name": "Temperatura de retorno da caldeira" + }, + "flame_statistics": { + "name": "Estatísticas de chama" + }, + "boiler_ch_setpoint": { + "name": "Ponto de ajuste do CH da caldeira" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Limite inferior do ponto de ajuste do CH da caldeira" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Limite superior do ponto de ajuste do CH da caldeira" + }, + "boiler_exhaust_temperature": { + "name": "Temperatura de exaustão da caldeira" + }, + "boiler_hw_setpoint": { + "name": "Ponto de ajuste de água quente da caldeira" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Limite inferior do ponto de ajuste de água quente da caldeira" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Ponto de ajuste de água quente da caldeira limite superior" + }, + "ch1_flow_setpoint": { + "name": "Ponto de ajuste de fluxo CH1" + }, + "ch2_flow_setpoint": { + "name": "Ponto de ajuste de fluxo CH2" + }, + "ch_flow_active_lower_setpoint": { + "name": "Ponto de ajuste inferior ativo de fluxo CH" + }, + "ch_flow_active_upper_setpoint": { + "name": "Ponto de ajuste superior ativo de fluxo CH" + }, + "ch_pressure_bar": { + "name": "Pressão CH" + }, + "connection_status": { + "name": "Status de conexão" + }, + "coprocessor_update_status": { + "name": "Estado de atualização do coprocessador OpenTherm" + }, + "coprocessor_version": { + "name": "Versão do coprocessador OpenTherm" + }, + "delta_t": { + "name": "Delta-T" + }, + "estimated_boiler_output": { + "name": "Saída estimada da caldeira" + }, + "hw_flow_rate": { + "name": "Caudal de água quente" + }, + "hw_flow_setpoint": { + "name": "Ponto de ajuste do fluxo de água quente" + }, + "hw_temperature": { + "name": "Temperatura da água quente" + }, + "maximum_capacity_kw": { + "name": "Capacidade máxima da caldeira" + }, + "minimum_modulation_level": { + "name": "Nível mínimo de modulação" + }, + "operating_mode": { + "name": "Modo de operação" + }, + "relative_modulation_level": { + "name": "Nível de modulação relativo" + }, + "room_setpoint": { + "name": "Ponto de ajuste da sala" + }, + "room_temperature": { + "name": "Temperatura ambiente" + }, + "slave_status": { + "name": "Estado do dispositivo secundário OpenTherm" + }, + "tracked_room_id": { + "name": "ID da divisão monitorizada" + }, + "heating_demand": { + "name": "Demanda de aquecimento" + }, + "heating_demand_channel": { + "name": "Canal de demanda de aquecimento {channel}" + }, + "hot_water_demand": { + "name": "Demanda de água quente" + }, + "total_energy": { + "name": "Energia total" + }, + "energy_delivered": { + "name": "Energia fornecida" + }, + "energy_received": { + "name": "Energia recebida" + }, + "equipment_power": { + "name": "Equipamento" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} inicia o aquecimento", + "stopped_heating": "{entity_name} para de aquecer", + "boosted": "{entity_name} é reforçado", + "target_temperature_increased": "A temperatura alvo de {entity_name} aumentou", + "target_temperature_decreased": "A temperatura alvo de {entity_name} diminuiu" + } + } +} diff --git a/custom_components/wiser/translations/sv.json b/custom_components/wiser/translations/sv.json new file mode 100644 index 00000000..66743fb3 --- /dev/null +++ b/custom_components/wiser/translations/sv.json @@ -0,0 +1,684 @@ +{ + "config": { + "flow_title": "{name}", + "step": { + "user": { + "title": "Konfiguration av Wiser Heat Hub", + "description": "Ange IP-adressen och den hemliga nyckeln som hämtats från hubben.", + "data": { + "host": "IP-adress eller värdnamn", + "port": "Portnummer", + "password": "Hemlig nyckel" + } + }, + "zeroconf_confirm": { + "title": "Konfiguration av Wiser Heat Hub ({name})", + "description": "Ange den hemliga nyckeln som hämtats från hubben.", + "data": { + "host": "IP-adress eller värdnamn", + "port": "Portnummer", + "password": "Hemlig nyckel" + } + } + }, + "abort": { + "already_in_progress": "Konfiguration av Wiser Heat Hub pågår redan.", + "already_configured": "Den här enheten är redan konfigurerad.", + "not_wiser_hub": "Den identifierade enheten är inte en Wiser Heat Hub." + }, + "error": { + "invalid_hot_water_presets": "En eller flera förinställningar för varmvatten är ogiltiga.", + "auth_failure": "Det gick inte att autentisera mot Wiser-hubben. Kontrollera den hemliga nyckeln och försök igen.", + "timeout_error": "Tidsgränsen överskreds vid anslutning till Wiser-hubben. Kontrollera IP-adressen och försök igen.", + "timeout_error_discovery": "Tidsgränsen överskreds vid anslutning till Wiser-hubben. Kontrollera att hubben fortfarande är online och försök igen.", + "unknown": "Okänt fel vid anslutning till Wiser-hubben. Kontrollera loggarna." + } + }, + "options": { + "step": { + "init": { + "title": "Alternativ för Wiser-integrationen", + "description": "Välj parametrar som ska ändras", + "menu_options": { + "main_params": "Huvudparametrar", + "automation_params": "Automatiseringsparametrar", + "opentherm": "OpenTherm", + "equipment_sensors": "Utrustningssensorer", + "ui_options": "Gränssnittsalternativ" + } + }, + "ui_options": { + "title": "Gränssnittsalternativ", + "data": { + "legacy_naming": "Äldre namngivning", + "show_wiser_sidebar": "Visa Wiser i sidopanelen" + }, + "data_description": { + "legacy_naming": "Inkludera rumsnamn i namn på Wiser-enheter. Om detta är aktiverat kommer återskapande av entitets-ID:n i Home Assistant med standardformatet under Inställningar → System → Format för entitets-ID (Home Assistant 2026.8 och senare) att duplicera områdesnamn när området och Wiser-rummet har samma namn. Ändring av detta alternativ byter inte namn på befintliga entitets-ID:n." + } + }, + "main_params": { + "title": "Alternativ för Wiser-integrationen", + "description": "Huvudparametrar", + "data": { + "host": "IP-adress eller värdnamn", + "port": "Portnummer", + "scan_interval": "Skanningsintervall (sek.)", + "heating_boost_temp": "Standardtemperatur för värmeboost (°C)", + "heating_boost_time": "Standardtid för värmeboost (min.)", + "hotwater_boost_time": "Standardtid för varmvattenboost (min.)", + "setpoint_mode": "Börvärdesläge", + "restore_manual_temp_option": "Använd denna temperatur om ingen manuell temperatur har sparats" + } + }, + "automation_params": { + "title": "Alternativ för Wiser-integrationen", + "description": "Aktivera inbyggda automatiseringar", + "sections": { + "automations_passive_mode": { + "name": "Passivt läge", + "description": "Aktivera passiv TRV-funktion. Varje rum måste aktiveras för att funktionen ska fungera.", + "data": { + "automations_passive_mode": "Passivt läge", + "passive_mode_temperature_increments": "Temperatursteg för passivt läge" + } + }, + "hotwater_climate": { + "name": "Klimatstyrning av varmvatten", + "description": "Aktivera klimatstyrning av varmvatten. Se wikin för mer information.", + "data": { + "hotwater_climate": "Aktivera klimatentiteten", + "experimental_mode": "Aktivera experimentellt driftläge", + "hotwater_auto_mode": "Automatiskt varmvattenläge", + "hotwater_heat_mode": "Varmvattenvärmeläge", + "hotwater_boost_mode": "Varmvattenboostläge", + "hotwater_sensor_entity_id": "Varmvattentemperatursensor" + } + }, + "additional_sensors": { + "name": "Ytterligare sensorer", + "description": "Visa valfria sensorer för data som rapporteras av Wiser.", + "data": { + "schedule_setpoint_rooms": "Rum utan klimatstyrning" + }, + "data_description": { + "schedule_setpoint_rooms": "Välj de Wiser-rum där en sensor för aktuell schematemperatur ska skapas." + } + } + } + }, + "equipment_sensors": { + "title": "Utrustningssensorer", + "description": "Konfigurera entiteter med detaljerade utrustningsdata för kompatibla enheter.", + "data": { + "equipment_sensors": "Aktivera sensorer för utrustningsdata" + }, + "data_description": { + "equipment_sensors": "Visa en extra sensor med detaljerade utrustningsattribut för varje kompatibelt smartuttag, PowerTag och värmeställdon." + } + }, + "opentherm": { + "title": "OpenTherm", + "description": "Konfigurera OpenTherm-funktioner.", + "menu_options": { + "opentherm_hot_water_control": "Varmvattenstyrning", + "opentherm_sensors": "Sensorer" + } + }, + "opentherm_sensors": { + "title": "OpenTherm-sensorer", + "description": "Välj en kategori med identifierade OpenTherm-attribut.", + "menu_options": { + "opentherm_readings": "Avläsningar", + "opentherm_central_heating": "Centralvärme", + "opentherm_hot_water": "Varmvatten", + "opentherm_status": "Status och diagnostik" + } + }, + "opentherm_hot_water_control": { + "title": "OpenTherm-varmvattenstyrning", + "description": "Styr valfritt pannans börvärde för tappvarmvatten via Wiser. Integrationen skapar ett OpenTherm Hot Water-schema i Wiser Schedules-panelen. Av använder den lägsta varmvattentemperatur som OpenTherm rapporterar.", + "data": { + "opentherm_hot_water_control": "Aktivera varmvattenstyrning", + "opentherm_hot_water_manual_temperature": "Manuell temperatur", + "opentherm_hot_water_sensor_entity_id": "Valfri extern temperatursensor.", + "opentherm_hot_water_presets": "Förinställningar" + }, + "data_description": { + "opentherm_hot_water_manual_temperature": "Temperatur som används i manuellt läge.", + "opentherm_hot_water_sensor_entity_id": "Valfri temperatursensor som används för aktuell vattentemperatur. OpenTherm används när sensorn inte är tillgänglig." + } + }, + "opentherm_readings": { + "title": "OpenTherm-avläsningar", + "description": "Välj vilka identifierade OpenTherm-avläsningar som ska visas som sensorentiteter.", + "data": { + "ch_flow_temperature": "Pannans framledningstemperatur", + "ch_pressure_bar": "Centralvärmetryck", + "ch_return_temperature": "Pannans returtemperatur", + "delta_t": "Delta-T", + "boiler_exhaust_temperature": "Pannans avgastemperatur", + "estimated_boiler_output": "Beräknad panneffekt", + "hw_flow_rate": "Varmvattenflöde", + "hw_temperature": "Varmvattentemperatur", + "maximum_capacity_kw": "Maximal pannkapacitet", + "minimum_modulation_level": "Lägsta modulationsnivå", + "relative_modulation_level": "Relativ modulationsnivå", + "room_temperature": "Rumstemperatur" + } + }, + "opentherm_central_heating": { + "title": "OpenTherm-centralvärme", + "description": "Välj vilka identifierade centralvärmeattribut som ska visas som entiteter.", + "data": { + "boiler_ch_max_setpoint_read_write": "Läsning/skrivning av pannans maximala centralvärmebörvärde", + "boiler_ch_max_setpoint_transfer_enable": "Överföring av pannans maximala centralvärmebörvärde aktiverad", + "boiler_ch_setpoint": "Pannans centralvärmebörvärde", + "boiler_ch_setpoint_lower_bound": "Nedre gräns för pannans centralvärmebörvärde", + "boiler_ch_setpoint_upper_bound": "Övre gräns för pannans centralvärmebörvärde", + "ch_flow_active_lower_setpoint": "Aktivt nedre börvärde för centralvärmeframledning", + "ch_flow_active_upper_setpoint": "Aktivt övre börvärde för centralvärmeframledning", + "ch1_flow_enabled": "Centralvärmekrets 1 framledning aktiverad", + "ch1_flow_setpoint": "Börvärde för centralvärmekrets 1 framledning", + "ch2_flow_enabled": "Centralvärmekrets 2 framledning aktiverad", + "ch2_flow_setpoint": "Börvärde för centralvärmekrets 2 framledning", + "room_setpoint": "Rumsbörvärde" + } + }, + "opentherm_hot_water": { + "title": "OpenTherm-varmvatten", + "description": "Välj vilka identifierade varmvattenattribut som ska visas som entiteter.", + "data": { + "boiler_hw_setpoint": "Pannans varmvattenbörvärde", + "boiler_hw_setpoint_lower_bound": "Nedre gräns för pannans varmvattenbörvärde", + "boiler_hw_setpoint_read_write": "Läsning/skrivning av pannans varmvattenbörvärde", + "boiler_hw_setpoint_transfer_enable": "Överföring av pannans varmvattenbörvärde aktiverad", + "boiler_hw_setpoint_upper_bound": "Övre gräns för pannans varmvattenbörvärde", + "hw_enabled": "Varmvatten aktiverat", + "hw_flow_setpoint": "Börvärde för varmvattenframledning" + } + }, + "opentherm_status": { + "title": "OpenTherm-status och diagnostik", + "description": "Välj vilka identifierade status- och diagnostikattribut som ska visas som sensorentiteter.", + "data": { + "boiler_fault": "Pannfel", + "central_heating_active": "Centralvärme aktiv", + "central_heating_2_active": "Centralvärme 2 aktiv", + "connection_status": "Anslutningsstatus", + "coprocessor_update_status": "Uppdateringsstatus för OpenTherm-hjälpprocessor", + "coprocessor_version": "Version av OpenTherm-hjälpprocessor", + "cooling_active": "Kylning aktiv", + "diagnostic_event": "Diagnostikhändelse", + "flame_active": "Låga aktiv", + "flame_statistics": "Lågstatistik", + "hot_water_active": "Varmvatten aktivt", + "operating_mode": "Driftläge", + "slave_status": "OpenTherm-slavstatus", + "tracked_room_id": "ID för övervakat rum" + } + } + }, + "abort": { + "opentherm_not_available": "OpenTherm-data är inte tillgängliga för denna Wiser-hubb.", + "equipment_not_available": "Utrustningsdata är inte tillgängliga för denna Wiser-hubb." + } + }, + "services": { + "set_opentherm_parameter": { + "name": "Ställ in OpenTherm-parameter", + "description": "Ändrar en OpenTherm-temperaturparameter som stöds på Wiser-hubben.", + "fields": { + "parameter": { + "name": "Parameter", + "description": "OpenTherm-temperaturparametern som ska ändras. Nödvändig API-slutpunkt väljs automatiskt." + }, + "temperature": { + "name": "Temperatur", + "description": "Använd en temperatur inom de gränser som rapporteras av pannan." + }, + "hub": { + "name": "Wiser-hubb", + "description": "Välj endast en hubb om du har flera Wiser-hubbar." + } + } + } + }, + "selector": { + "opentherm_parameter": { + "options": { + "chFlowActiveLowerSetpoint": "Aktivt nedre centralvärmebörvärde", + "chFlowActiveUpperSetpoint": "Aktivt övre centralvärmebörvärde", + "dhwFlowSetpoint": "Hubbens börvärde för varmvattenframledning", + "dhwSetpoint": "Pannans varmvattenbörvärde", + "maxChSetpoint": "Pannans maximala centralvärmebörvärde" + } + } + }, + "exceptions": { + "opentherm_temperature_required": { + "message": "Ange en OpenTherm-temperatur." + }, + "opentherm_hub_not_found": { + "message": "Den angivna Wiser-hubben hittades inte." + }, + "opentherm_hub_not_loaded": { + "message": "Den angivna Wiser-hubben är inte inläst." + }, + "opentherm_hub_required": { + "message": "Välj en Wiser-hubb när fler än en hubb är konfigurerad." + }, + "opentherm_not_supported": { + "message": "Den här hubben har inte OpenTherm-funktionalitet." + }, + "opentherm_invalid_parameter": { + "message": "OpenTherm-parametern eller värdet är ogiltigt: {error}" + }, + "opentherm_write_failed": { + "message": "Det gick inte att ställa in OpenTherm-parametern {parameter}: {error}" + }, + "opentherm_confirmation_failed": { + "message": "OpenTherm-parametern {parameter} låg inte kvar på {requested} °C efter ett nytt försök och bekräftelseperioden på {window} sekunder. Wiser rapporterar {reported} °C. Inga fler försök skickas." + }, + "opentherm_retry_failed": { + "message": "OpenTherm-parametern {parameter} låg inte kvar på {requested} °C och ett nytt försök kunde inte skickas: {error}. Wiser rapporterar {reported} °C. Inga fler försök skickas." + }, + "opentherm_notification_title": { + "message": "Wiser OpenTherm-kommando" + } + }, + "entity": { + "climate": { + "heating": { + "name": "Uppvärmning" + }, + "floor_temperature": { + "name": "Golvtemperatur" + } + }, + "number": { + "away_mode_target_temperature": { + "name": "Måltemperatur för bortaläge" + }, + "floor_temperature_offset": { + "name": "Förskjutning av golvtemperatur" + }, + "indoor_discomfort_temperature": { + "name": "Obehagstemperatur inomhus" + }, + "outdoor_discomfort_temperature": { + "name": "Obehagstemperatur utomhus" + } + }, + "select": { + "mode": { + "name": "Läge" + }, + "power_on_behavior": { + "name": "Startbeteende" + }, + "led_indicator": { + "name": "LED-indikator" + } + }, + "button": { + "boost_all_heating": { + "name": "Boosta all uppvärmning" + }, + "cancel_all_heating_overrides": { + "name": "Avbryt alla åsidosättningar av uppvärmning" + }, + "boost_hot_water": { + "name": "Boosta varmvatten" + }, + "cancel_hot_water_overrides": { + "name": "Avbryt alla åsidosättningar av varmvatten" + }, + "toggle_hot_water": { + "name": "Växla varmvatten" + }, + "moment": { + "name": "Moment: {name}" + } + }, + "switch": { + "valve_protection": { + "name": "Ventilskydd" + }, + "eco_mode": { + "name": "Ekoläge" + }, + "away_mode_affects_hot_water": { + "name": "Bortaläge påverkar varmvatten" + }, + "comfort_mode": { + "name": "Komfortläge" + }, + "away_mode": { + "name": "Bortaläge" + }, + "daylight_saving": { + "name": "Sommartid" + }, + "summer_comfort_enabled": { + "name": "Sommarkomfort aktiverad" + }, + "window_detection": { + "name": "Fönsterdetektering" + }, + "include_in_summer_comfort": { + "name": "Inkludera i sommarkomfort" + }, + "device_lock": { + "name": "Enhetslås" + }, + "identify": { + "name": "Identifiera" + }, + "outlet": { + "name": "Eluttag" + }, + "away_mode_turns_off": { + "name": "Bortaläge stänger av" + }, + "away_mode_closes": { + "name": "Bortaläge stänger" + }, + "passive_mode": { + "name": "Passivt läge" + }, + "respect_summer_comfort": { + "name": "Respektera sommarkomfort" + }, + "interacts_with_room_climate": { + "name": "Samverkar med rumsklimatet" + }, + "humidity_interacts_with_room_climate": { + "name": "Luftfuktighet samverkar med rumsklimatet" + }, + "temperature_interacts_with_room_climate": { + "name": "Temperatur samverkar med rumsklimatet" + }, + "illuminance_interacts_with_room_climate": { + "name": "Ljusstyrka samverkar med rumsklimatet" + }, + "sensor_interacts_with_room_climate": { + "name": "{sensor} samverkar med rumsklimatet" + }, + "manual_heat": { + "name": "Manuell uppvärmning" + }, + "hot_water": { + "name": "Varmvatten" + } + }, + "binary_sensor": { + "summer_discomfort_prevention": { + "name": "Förebyggande av sommarobehag" + }, + "summer_comfort_available": { + "name": "Sommarkomfort tillgänglig" + }, + "pcm_device_limit_reached": { + "name": "Gränsen för PCM-enheter har nåtts" + }, + "smoke_alarm": { + "name": "Röklarm" + }, + "heat_alarm": { + "name": "Värmelarm" + }, + "tamper_alarm": { + "name": "Sabotagelarm" + }, + "fault_warning": { + "name": "Felvarning" + }, + "remote_alarm": { + "name": "Fjärrlarm" + }, + "battery_defect": { + "name": "Batterifel" + }, + "controllable": { + "name": "Styrbar" + }, + "pcm_mode": { + "name": "PCM-läge" + }, + "window_detection_active": { + "name": "Fönsterdetektering aktiv" + }, + "is_dimmable": { + "name": "Dimbar" + }, + "is_led_indicator_supported": { + "name": "LED-indikator stöds" + }, + "is_output_mode_supported": { + "name": "Utgångsläge stöds" + }, + "is_power_on_behaviour_supported": { + "name": "Startbeteende stöds" + }, + "is_tilt_supported": { + "name": "Lutning stöds" + }, + "is_open": { + "name": "Öppen" + }, + "is_closed": { + "name": "Stängd" + }, + "active": { + "name": "Aktiv" + }, + "boiler_ch_max_setpoint_read_write": { + "name": "Läsning/skrivning av pannans maximala centralvärmebörvärde" + }, + "boiler_ch_max_setpoint_transfer_enable": { + "name": "Överföring av pannans maximala centralvärmebörvärde aktiverad" + }, + "boiler_fault": { + "name": "Pannfel" + }, + "boiler_hw_setpoint_read_write": { + "name": "Läsning/skrivning av pannans varmvattenbörvärde" + }, + "boiler_hw_setpoint_transfer_enable": { + "name": "Överföring av pannans varmvattenbörvärde aktiverad" + }, + "central_heating_2_active": { + "name": "Centralvärme 2 aktiv" + }, + "central_heating_active": { + "name": "Centralvärme aktiv" + }, + "ch1_flow_enabled": { + "name": "Centralvärmekrets 1 framledning aktiverad" + }, + "ch2_flow_enabled": { + "name": "Centralvärmekrets 2 framledning aktiverad" + }, + "cooling_active": { + "name": "Kylning aktiv" + }, + "diagnostic_event": { + "name": "Diagnostikhändelse" + }, + "flame_active": { + "name": "Låga aktiv" + }, + "hot_water_active": { + "name": "Varmvatten aktivt" + }, + "hw_enabled": { + "name": "Varmvatten aktiverat" + } + }, + "sensor": { + "signal": { + "name": "Signal" + }, + "cloud": { + "name": "Moln" + }, + "heating_operation_mode": { + "name": "Driftläge för uppvärmning" + }, + "heating": { + "name": "Uppvärmning", + "state": { + "off": "Av", + "on": "På" + } + }, + "heating_channel": { + "name": "Värmekanal {channel}", + "state": { + "off": "Av", + "on": "På" + } + }, + "hot_water": { + "name": "Varmvatten", + "state": { + "off": "Av", + "on": "På" + } + }, + "hot_water_operation_mode": { + "name": "Driftläge för varmvatten" + }, + "target_temperature": { + "name": "Måltemperatur" + }, + "floor_temperature": { + "name": "Golvtemperatur" + }, + "boiler_flow_temperature": { + "name": "Pannans framledningstemperatur" + }, + "boiler_return_temperature": { + "name": "Pannans returtemperatur" + }, + "flame_statistics": { + "name": "Lågstatistik" + }, + "boiler_ch_setpoint": { + "name": "Pannans centralvärmebörvärde" + }, + "boiler_ch_setpoint_lower_bound": { + "name": "Nedre gräns för pannans centralvärmebörvärde" + }, + "boiler_ch_setpoint_upper_bound": { + "name": "Övre gräns för pannans centralvärmebörvärde" + }, + "boiler_exhaust_temperature": { + "name": "Pannans avgastemperatur" + }, + "boiler_hw_setpoint": { + "name": "Pannans varmvattenbörvärde" + }, + "boiler_hw_setpoint_lower_bound": { + "name": "Nedre gräns för pannans varmvattenbörvärde" + }, + "boiler_hw_setpoint_upper_bound": { + "name": "Övre gräns för pannans varmvattenbörvärde" + }, + "ch1_flow_setpoint": { + "name": "Börvärde för centralvärmekrets 1 framledning" + }, + "ch2_flow_setpoint": { + "name": "Börvärde för centralvärmekrets 2 framledning" + }, + "ch_flow_active_lower_setpoint": { + "name": "Aktivt nedre börvärde för centralvärmeframledning" + }, + "ch_flow_active_upper_setpoint": { + "name": "Aktivt övre börvärde för centralvärmeframledning" + }, + "ch_pressure_bar": { + "name": "Centralvärmetryck" + }, + "connection_status": { + "name": "Anslutningsstatus" + }, + "coprocessor_update_status": { + "name": "Uppdateringsstatus för OpenTherm-hjälpprocessor" + }, + "coprocessor_version": { + "name": "Version av OpenTherm-hjälpprocessor" + }, + "delta_t": { + "name": "Delta-T" + }, + "estimated_boiler_output": { + "name": "Beräknad panneffekt" + }, + "hw_flow_rate": { + "name": "Varmvattenflöde" + }, + "hw_flow_setpoint": { + "name": "Börvärde för varmvattenframledning" + }, + "hw_temperature": { + "name": "Varmvattentemperatur" + }, + "maximum_capacity_kw": { + "name": "Maximal pannkapacitet" + }, + "minimum_modulation_level": { + "name": "Lägsta modulationsnivå" + }, + "operating_mode": { + "name": "Driftläge" + }, + "relative_modulation_level": { + "name": "Relativ modulationsnivå" + }, + "room_setpoint": { + "name": "Rumsbörvärde" + }, + "room_temperature": { + "name": "Rumstemperatur" + }, + "slave_status": { + "name": "OpenTherm-slavstatus" + }, + "tracked_room_id": { + "name": "ID för övervakat rum" + }, + "heating_demand": { + "name": "Värmebehov" + }, + "heating_demand_channel": { + "name": "Värmebehovskanal {channel}" + }, + "hot_water_demand": { + "name": "Varmvattenbehov" + }, + "total_energy": { + "name": "Total energi" + }, + "energy_delivered": { + "name": "Levererad energi" + }, + "energy_received": { + "name": "Mottagen energi" + }, + "equipment_power": { + "name": "Utrustning" + } + } + }, + "device_automation": { + "trigger_type": { + "started_heating": "{entity_name} börjar värma", + "stopped_heating": "{entity_name} slutar värma", + "boosted": "{entity_name} är boostad", + "target_temperature_increased": "Måltemperaturen för {entity_name} höjdes", + "target_temperature_decreased": "Måltemperaturen för {entity_name} sänktes" + } + } +} diff --git a/custom_components/wiser/update.py b/custom_components/wiser/update.py new file mode 100644 index 00000000..69425ad4 --- /dev/null +++ b/custom_components/wiser/update.py @@ -0,0 +1,351 @@ +"""Offer independent, manually installed Wiser frontend updates.""" + +from __future__ import annotations + +import asyncio +from datetime import timedelta +from hashlib import sha256 +import logging +import re +from urllib.parse import urlsplit + +import aiohttp +from homeassistant.components.update import UpdateEntity, UpdateEntityFeature +from homeassistant.const import EntityCategory +from homeassistant.exceptions import HomeAssistantError +from homeassistant.helpers import entity_registry as er +from homeassistant.helpers.aiohttp_client import async_get_clientsession +from homeassistant.helpers.update_coordinator import CoordinatorEntity, DataUpdateCoordinator + +from .const import CARD_MANIFEST, DOMAIN +from .frontend import JSModuleRegistration, async_card_resource +from .frontend.card_files import newer_version, prune_card_cache, store_card +from .frontend.registry import async_load_registry +from .frontend.wiser_sidebar import async_update_wiser_panel + +_LOGGER = logging.getLogger(__name__) +_MANAGER = "wiser_card_updates" +_MAX_DOWNLOAD = 20 * 1024 * 1024 +_HACS_REPOSITORY_ID = "159080189" + + +def _release_version(tag, allow_prerelease=False): + """Accept semantic card versions allowed by the selected release channel.""" + match = re.fullmatch( + r"v?(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?)", + tag, + ) + if not match: + raise ValueError(f"Unsupported card release version: {tag}") + if "-" in match[1].split("+", 1)[0] and not allow_prerelease: + raise ValueError(f"Unsupported stable card release version: {tag}") + return match[1] + + +def _validate_release(definition, release, allow_prerelease=False): + """Validate release metadata before exposing an installable update.""" + repository, filename = definition["repository"], definition["filename"] + if ( + release.get("draft") + or (release.get("prerelease") and not allow_prerelease) + or not release.get("published_at") + ): + raise ValueError("Expected a published stable card release") + version = _release_version(release["tag_name"], allow_prerelease) + assets = [ + asset for asset in release["assets"] + if asset["name"] == filename and asset.get("state") == "uploaded" + ] + if len(assets) != 1: + raise ValueError(f"Release must include exactly one {filename}") + asset = assets[0] + url = urlsplit(asset["browser_download_url"]) + if ( + url.scheme != "https" + or url.netloc != "github.com" + or not url.path.startswith(f"/{repository}/releases/download/") + ): + raise ValueError("Invalid card release download URL") + if not 100 <= asset["size"] <= _MAX_DOWNLOAD: + raise ValueError("Invalid card release asset size") + return { + "version": version, + "asset": asset, + "release_url": f"https://github.com/{repository}/releases/tag/{release['tag_name']}", + } + + +def _prereleases_enabled(hass): + """Follow HACS' prerelease preference for this integration when available.""" + registry = er.async_get(hass) + entity_id = registry.async_get_entity_id( + "switch", "hacs", _HACS_REPOSITORY_ID + ) + states = getattr(hass, "states", None) + return bool(entity_id and states and states.is_state(entity_id, "on")) + + +def _select_release(definition, releases, allow_prerelease): + """Select the highest valid published release allowed by the channel.""" + selected = None + for metadata in releases: + try: + release = _validate_release(definition, metadata, allow_prerelease) + except (ValueError, KeyError, TypeError): + continue + if selected is None or newer_version(release["version"], selected["version"]): + selected = release + if selected is None: + raise ValueError("No verified card release is available") + return selected + + +def _validate_download(definition, release, contents): + """Verify the file before changing the currently installed card.""" + filename, component = definition["filename"], definition["component"] + asset = release["asset"] + if len(contents) != asset["size"]: + raise ValueError("Card download size does not match release metadata") + digest = "sha256:" + sha256(contents).hexdigest() + if asset.get("digest") and asset["digest"] != digest: + raise ValueError("Card download checksum does not match release metadata") + if contents.lstrip().lower().startswith((b" _MAX_DOWNLOAD: + raise ValueError("Card download exceeds size limit") + chunks.append(chunk) + contents = b"".join(chunks) + await self.hass.async_add_executor_job( + _validate_download, definition, release, contents + ) + await self.hass.async_add_executor_job( + store_card, self.hass.config.path(), filename, + release["version"], contents, + ) + # Keep offering the update until every frontend registration succeeds. + # Retain the previous asset while a failed refresh can still reference it. + self.pending_refresh.setdefault(card, (installed, active_path)) + registration = JSModuleRegistration(self.hass) + await registration.async_register() + await async_update_wiser_panel(self.hass) + active_path, _, installed = await async_card_resource(self.hass, filename) + _, previous_path = self.pending_refresh.pop(card) + state["installed"] = installed + await self.hass.async_add_executor_job( + prune_card_cache, self.hass.config.path(), filename, + {active_path, previous_path}, + ) + except ( + aiohttp.ClientError, TimeoutError, OSError, ValueError, + RuntimeError, HomeAssistantError, + ) as err: + raise HomeAssistantError(f"Unable to install card update: {err}") from err + finally: + self.installing.discard(card) + self.async_update_listeners() + +class WiserCardUpdate(CoordinatorEntity, UpdateEntity): + """A user-installed frontend card update, separate from hub firmware.""" + + _attr_supported_features = UpdateEntityFeature.INSTALL + _attr_entity_category = EntityCategory.CONFIG + _attr_has_entity_name = True + _attr_release_summary = "Refresh your browser after installation to load the updated card." + + def __init__(self, coordinator, card): + super().__init__(coordinator) + self.card = card + self._attr_unique_id = f"wiser_{card}_card_update" + + @property + def name(self): + """Follow registry name changes without recreating the update entity.""" + return self.coordinator.cards[self.card]["name"] + + @property + def available(self): + return super().available and self.coordinator.data.get(self.card, {}).get("release") is not None + + @property + def installed_version(self): + return self.coordinator.data.get(self.card, {}).get("installed") + + @property + def latest_version(self): + release = self.coordinator.data.get(self.card, {}).get("release") + return release["version"] if release else None + + @property + def release_url(self): + release = self.coordinator.data.get(self.card, {}).get("release") + return release["release_url"] if release else None + + @property + def in_progress(self): + return self.card in self.coordinator.installing + + def version_is_newer(self, latest_version, installed_version): + return newer_version(latest_version, installed_version) + + async def async_install(self, version, backup, **kwargs): + """Install only after an explicit Home Assistant update action.""" + await self.coordinator.install(self.card) diff --git a/custom_components/wiser/water_heater.py b/custom_components/wiser/water_heater.py new file mode 100644 index 00000000..181953e1 --- /dev/null +++ b/custom_components/wiser/water_heater.py @@ -0,0 +1,5 @@ +"""Water-heater entities for Wiser.""" + +from .opentherm_hot_water import async_setup_entry + +__all__ = ["async_setup_entry"] diff --git a/custom_components/wiser/websockets.py b/custom_components/wiser/websockets.py index cd06ea46..d8b0e3c6 100644 --- a/custom_components/wiser/websockets.py +++ b/custom_components/wiser/websockets.py @@ -9,7 +9,15 @@ from aioWiserHeatAPI.exceptions import WiserScheduleError from aioWiserHeatAPI.schedule import WiserScheduleTypeEnum from homeassistant.helpers.dispatcher import async_dispatcher_connect -from .const import DATA, DOMAIN +from .const import DATA, DOMAIN, OPENTHERM_HOT_WATER_SCHEDULE +from .local_schedule import ( + LOCAL_HOT_WATER_SCHEDULE_ID, + LOCAL_HOT_WATER_SCHEDULE_TYPE, +) +from .frontend.wiser_sidebar import ( + save_panel_config, + save_wiser_panel_tabs, +) _LOGGER = logging.getLogger(__name__) @@ -51,6 +59,13 @@ def unsubscribe_listeners(): async def async_register_websockets(hass, data): + def get_entry_for_hub(hub: str): + """Return the integration runtime data for the requested hub.""" + for entry_id, entry_data in hass.data[DOMAIN].items(): + if not hub or entry_data[DATA].wiserhub.system.name == hub: + return entry_data + return None + def get_hub_name(config_entry_id): try: api = hass.data[DOMAIN][config_entry_id][DATA] @@ -59,14 +74,13 @@ def get_hub_name(config_entry_id): return None def get_api_for_hub(hub: str): - if hub: - for entry_id in hass.data[DOMAIN]: - if hass.data[DOMAIN][entry_id][DATA].wiserhub.system.name == hub: - return hass.data[DOMAIN][entry_id][DATA] - return None - else: - for entry_id in hass.data[DOMAIN]: - return hass.data[DOMAIN][entry_id][DATA] + entry_data = get_entry_for_hub(hub) + return entry_data[DATA] if entry_data else None + + def get_local_hot_water_schedule(hub: str): + """Return this hub's integration-owned hot-water schedule, if enabled.""" + entry_data = get_entry_for_hub(hub) + return entry_data.get(OPENTHERM_HOT_WATER_SCHEDULE) if entry_data else None def get_entity_from_entity_id(entity: str): """Get wiser entity from entity_id""" @@ -76,6 +90,42 @@ def get_entity_from_entity_id(entity: str): return entity_comp.get_entity(entity) return None + @callback + @websocket_api.websocket_command({ + vol.Required("type"): "wiser/panel/configure", + vol.Required("panel_id"): str, + vol.Required("configs"): {str: dict}, + }) + @websocket_api.require_admin + def websocket_configure_panel(hass, connection, msg): + """Save settings for any panel supplied by the frontend registry.""" + try: + save_panel_config(hass, msg["panel_id"], msg["configs"]) + except ValueError as err: + connection.send_error(msg["id"], "invalid_config", str(err)) + return + connection.send_result(msg["id"]) + + @callback + @websocket_api.websocket_command({ + vol.Required("type"): "wiser/panel/configure_tabs", + vol.Required("tabs"): [ + { + vol.Required("id"): str, + vol.Required("title"): str, + } + ], + }) + @websocket_api.require_admin + def websocket_configure_wiser_panel_tabs(hass, connection, msg): + """Save the unified Wiser panel tab order and custom titles.""" + try: + save_wiser_panel_tabs(hass, msg["tabs"]) + except ValueError as err: + connection.send_error(msg["id"], "invalid_config", str(err)) + return + connection.send_result(msg["id"]) + # Get Hubs @websocket_api.websocket_command( { @@ -147,6 +197,21 @@ async def websocket_get_schedules( } ) + local_schedule = get_local_hot_water_schedule(msg.get("hub")) + if local_schedule and ( + not schedule_type + or schedule_type.lower() + == LOCAL_HOT_WATER_SCHEDULE_TYPE.lower() + ): + output.append( + { + "Id": LOCAL_HOT_WATER_SCHEDULE_ID, + "Type": LOCAL_HOT_WATER_SCHEDULE_TYPE, + "Name": local_schedule.name, + "Assignments": 1, + } + ) + connection.send_result(msg["id"], sorted(output, key=lambda n: n["Name"])) else: connection.send_error(msg["id"], "wiser error", "hub not recognised") @@ -196,6 +261,16 @@ async def websocket_get_schedule_by_id( schedule_id = msg.get("schedule_id") d = get_api_for_hub(msg.get("hub")) if d: + local_schedule = get_local_hot_water_schedule(msg.get("hub")) + if ( + schedule_id == LOCAL_HOT_WATER_SCHEDULE_ID + and schedule_type == LOCAL_HOT_WATER_SCHEDULE_TYPE.lower() + and local_schedule + ): + connection.send_result( + msg["id"], local_schedule.ws_schedule_data + ) + return schedule_type_enum = WiserScheduleTypeEnum[schedule_type] schedule = d.wiserhub.schedules.get_by_id(schedule_type_enum, schedule_id) if schedule: @@ -294,6 +369,13 @@ async def websocket_assign_schedule( schedule_id = msg.get("schedule_id") d = get_api_for_hub(msg.get("hub")) if d: + if schedule_id == LOCAL_HOT_WATER_SCHEDULE_ID: + connection.send_error( + msg["id"], + "wiser error", + "The integration-owned OpenTherm hot-water schedule cannot be assigned", + ) + return schedule_type_enum = WiserScheduleTypeEnum[schedule_type] schedule = d.wiserhub.schedules.get_by_id(schedule_type_enum, schedule_id) if schedule: @@ -350,6 +432,11 @@ async def websocket_rename_schedule( name = msg["schedule_name"] d = get_api_for_hub(msg.get("hub")) if d: + local_schedule = get_local_hot_water_schedule(msg.get("hub")) + if schedule_id == LOCAL_HOT_WATER_SCHEDULE_ID and local_schedule: + await local_schedule.async_set_name(name) + connection.send_result(msg["id"], "success") + return schedule_type_enum = WiserScheduleTypeEnum[schedule_type] schedule = d.wiserhub.schedules.get_by_id(schedule_type_enum, schedule_id) if schedule: @@ -383,6 +470,13 @@ async def websocket_delete_schedule( schedule_id = msg["schedule_id"] d = get_api_for_hub(msg.get("hub")) if d: + if schedule_id == LOCAL_HOT_WATER_SCHEDULE_ID: + connection.send_error( + msg["id"], + "wiser error", + "The integration-owned OpenTherm hot-water schedule cannot be deleted", + ) + return schedule_type_enum = WiserScheduleTypeEnum[schedule_type] schedule = d.wiserhub.schedules.get_by_id(schedule_type_enum, schedule_id) if schedule: @@ -435,6 +529,17 @@ async def websocket_save_schedule( new_schedule = msg["schedule"] d = get_api_for_hub(msg.get("hub")) if d: + local_schedule = get_local_hot_water_schedule(msg.get("hub")) + if schedule_id == LOCAL_HOT_WATER_SCHEDULE_ID and local_schedule: + try: + await local_schedule.async_save(new_schedule) + except (TypeError, ValueError) as err: + connection.send_error( + msg["id"], "invalid_schedule", str(err) + ) + return + connection.send_result(msg["id"], "success") + return schedule_type_enum = WiserScheduleTypeEnum[schedule_type] schedule = d.wiserhub.schedules.get_by_id(schedule_type_enum, schedule_id) if schedule: @@ -469,6 +574,13 @@ async def websocket_copy_schedule( to_schedule_id = msg["to_schedule_id"] d = get_api_for_hub(msg.get("hub")) if d: + if LOCAL_HOT_WATER_SCHEDULE_ID in (schedule_id, to_schedule_id): + connection.send_error( + msg["id"], + "wiser error", + "The integration-owned OpenTherm hot-water schedule cannot be copied", + ) + return schedule_type_enum = WiserScheduleTypeEnum[schedule_type] schedule = d.wiserhub.schedules.get_by_id(schedule_type_enum, schedule_id) if schedule: @@ -540,6 +652,8 @@ async def websocket_get_zigbee_data( else: connection.send_error(msg["id"], "wiser error", "hub not recognised") + async_register_command(hass, websocket_configure_panel) + async_register_command(hass, websocket_configure_wiser_panel_tabs) async_register_command(hass, websocket_get_hubs) async_register_command(hass, websocket_get_suntimes) async_register_command(hass, websocket_get_schedules) diff --git a/docs/frontend-registry.md b/docs/frontend-registry.md new file mode 100644 index 00000000..228335d7 --- /dev/null +++ b/docs/frontend-registry.md @@ -0,0 +1,152 @@ +# Adding Wiser frontend cards and panels + +Wiser cards and panels are developed and released from their own repositories. The integration keeps only their definitions in `custom_components/wiser/frontend/cards.json`; compiled JavaScript bundles must not be committed to this repository. + +Adding a new definition requires an integration pull request and release. After that initial registration, new versions of the card or panel are released and updated independently through its Home Assistant update entity. + +## Choose the frontend type + +The registry supports three types of frontend bundle: + +| Type | `component` | `panel` | `card` | +| --- | --- | --- | --- | +| Lovelace card only | Card custom element | `null` | Omit or `true` | +| Sidebar panel only | Panel custom element | Panel custom element | `false` | +| Card and sidebar panel | Card custom element | Panel custom element | Omit or `true` | + +A panel-only bundle is not added to the Lovelace resources list. The integration loads it only for its Wiser sidebar tab. + +## Prepare the frontend repository + +The repository must publish a GitHub release containing one compiled JavaScript asset whose name exactly matches the registry `filename`. The asset must: + +- Be at least 100 bytes and contain JavaScript rather than an HTML error page. +- Define the custom element named by `component`. +- Define the custom element named by `panel` when one is configured. +- Use the shared `wiser/panel/configure` API instead of a panel-specific settings endpoint. +- Begin with a version marker matching the asset filename and release version. + +For `wiser-energy-card.js` version `1.0.0`, the marker is: + +```js +/*! WISER-CARD-VERSION wiser-energy-card 1.0.0 */ +``` + +Use semantic release tags such as `v1.0.0` or `v1.1.0-beta.1`. Stable update checks ignore prereleases. When the HACS **Pre-release** switch is enabled for the integration, update checks consider stable and prerelease versions. + +## Add the registry definition + +Add an entry to the `cards` array in `custom_components/wiser/frontend/cards.json`: + +```json +{ + "id": "energy", + "name": "Wiser Energy Card", + "filename": "wiser-energy-card.js", + "repository": "andyblac/wiser-energy-card", + "component": "wiser-energy-card", + "panel": "wiser-energy-panel" +} +``` + +The fields are: + +| Field | Purpose | +| --- | --- | +| `id` | Stable internal identifier used for settings and the update entity. | +| `name` | Name displayed by the Home Assistant update entity. | +| `filename` | Exact JavaScript filename attached to every GitHub release. | +| `repository` | GitHub repository in `owner/repository` form. | +| `component` | Custom element defined by the JavaScript bundle. | +| `panel` | Sidebar panel custom element, or `null` for a card without a panel. | +| `card` | Set to `false` for a panel-only bundle; otherwise omit it. | +| `legacy_filenames` | Previous filenames retained only when migrating a renamed card. | + +A card without a sidebar panel uses `"panel": null`. A panel-only definition uses the panel custom element for both `component` and `panel`: + +```json +{ + "id": "hub", + "name": "Wiser Hub Panel", + "filename": "wiser-hub-panel.js", + "repository": "andyblac/wiser-hub-panel", + "component": "wiser-hub-panel", + "panel": "wiser-hub-panel", + "card": false +} +``` + +IDs, filenames, component names and non-null panel names must be unique. Treat them as permanent identities after release. Renaming one requires an explicit migration so existing resources, settings and entity history are preserved. The versioned registry document supports up to 100 definitions: + +```json +{ + "schema_version": 1, + "cards": [] +} +``` + +## Test registration locally + +For the normal development layout, place the frontend repository beside this integration repository and build its `dist/` output. A development integration build automatically prefers that local bundle: + +```text +GitHub/ +├── wiserHomeAssistantPlatform/ +└── wiser-energy-card/ + └── dist/ + └── wiser-energy-card.js +``` + +```sh +python3 scripts/build.py --channel dev +``` + +To test installation through Home Assistant instead, edit the installed `custom_components/wiser/frontend/cards.json` and run **Check for updates** on any Wiser frontend update entity. The shared coordinator reloads and validates the file, creates an update entity for the new definition, and offers it as installed version `0.0.0`. This does not require an integration rebuild or Home Assistant restart. + +To build a package containing the definitions but none of the JavaScript bundles, run: + +```sh +python3 scripts/build.py --channel dev --without-frontend +``` + +This is useful for testing initial installation through all frontend update entities. The build does not contact any card or panel repository. + +You can override a repository during development without changing the registry: + +```sh +python3 scripts/build.py --channel dev \ + --repository energy=example/wiser-energy-card +``` + +## Submit the integration pull request + +The pull request for a new card or panel should contain: + +- Its entry in `custom_components/wiser/frontend/cards.json`. +- Any integration backend, WebSocket or shared-shell changes it requires. +- Tests for new integration behavior. +- A changelog entry when required by the integration contribution guidelines. + +Publish a usable frontend release before the integration release is built. If the frontend repository or release asset is unavailable during packaging, the integration build continues and omits that bundle. Its definition remains in `cards.json`, so Home Assistant can install it later through the update entity. + +After the definition has shipped, normal frontend releases require no further integration pull request. Publish the correctly named and versioned JavaScript asset in the frontend repository; Home Assistant will offer it through the existing update entity. + +## Sidebar panel contract + +Each registered panel receives `hass` and `panel.config`. The configuration includes `panel_id`, `hubs`, `hub_ids`, per-hub `card_configs`, and `card_url`. Use the supplied `panel_id` when saving settings. + +Panel preferences use the administrator-only shared WebSocket command: + +```js +await hass.callWS({ + type: "wiser/panel/configure", + panel_id: this._config.panel_id, + configs: { + [hubName]: { columns: 3 } + } +}); +``` + +The integration stores this example under `wiser_panel_config.energy` in each selected hub's options and refreshes the panel without reloading the hub. Tab ordering and custom titles continue to use `wiser/panel/configure_tabs`. + +The shared panel shell and backend APIs ship with the integration. A panel that needs new backend commands, hub data or shared-shell behavior must include those changes in its integration pull request. diff --git a/scripts/build.py b/scripts/build.py new file mode 100644 index 00000000..7668050d --- /dev/null +++ b/scripts/build.py @@ -0,0 +1,101 @@ +"""Package Wiser with local development cards or published release assets.""" + +import argparse +import json +from pathlib import Path +import shutil +import tempfile +from zipfile import ZIP_DEFLATED, ZipFile + +from fetch_card_releases import ( + fetch_cards, + repository_overrides, + select_repositories, + validate_registry, +) + +ROOT = Path(__file__).resolve().parents[1] + + +def build( + source, + output, + local_root, + channel, + repositories=None, + release=False, + without_frontend=False, +): + """Stage a fresh integration and replace the ZIP only after a successful build.""" + # Stable builds and explicit releases must never package local card builds. + if release or channel == "stable": + local_root = None + output.parent.mkdir(parents=True, exist_ok=True) + with tempfile.TemporaryDirectory(dir=output.parent) as temporary: + staging = Path(temporary) / "wiser" + shutil.copytree( + source, staging, + ignore=shutil.ignore_patterns("__pycache__", "*.pyc", ".DS_Store"), + ) + manifest = validate_registry( + json.loads((staging / "frontend/cards.json").read_text("utf-8")) + ) + definitions = {card["id"]: card for card in manifest} + selected_repositories = select_repositories(definitions, repositories or {}) + report = fetch_cards( + channel, + staging / "frontend", + selected_repositories, + local_root=local_root, + definitions=definitions, + skip_unavailable=True, + omit=without_frontend, + ) + archive_path = Path(temporary) / "wiser.zip" + with ZipFile(archive_path, "w", compression=ZIP_DEFLATED) as archive: + for path in sorted(staging.rglob("*")): + if path.is_file(): + archive.write(path, path.relative_to(staging)) + with ZipFile(archive_path) as archive: + if archive.testzip() is not None: + raise ValueError("Package integrity check failed") + archive_path.replace(output) + shutil.copyfile( + staging / "frontend/card-releases.json", + output.parent / "card-releases.json", + ) + (output.parent / "panel-config-release.json").unlink(missing_ok=True) + return report + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--channel", choices=["dev", "stable"], default="dev") + parser.add_argument("--release", action="store_true", + help="Use only GitHub release assets, including for the dev channel") + parser.add_argument("--local-root", type=Path, default=ROOT.parent, + help="Directory containing sibling card repositories") + parser.add_argument("--output", type=Path, default=ROOT / "dist/wiser.zip") + parser.add_argument("--repository", action="append", default=[], metavar="ID=OWNER/REPO") + parser.add_argument( + "--without-frontend", + action="store_true", + help="Package cards.json without any card or panel JavaScript bundles", + ) + args = parser.parse_args() + try: + report = build( + ROOT / "custom_components/wiser", args.output, args.local_root, + args.channel, + repository_overrides(args.repository), + release=args.release, + without_frontend=args.without_frontend, + ) + except Exception as error: + parser.exit(1, f"Build failed: {error}\n") + print(json.dumps(report, indent=2)) + print(f"Built {args.output.resolve()}") + + +if __name__ == "__main__": + main() diff --git a/scripts/fetch_card_releases.py b/scripts/fetch_card_releases.py new file mode 100644 index 00000000..be259255 --- /dev/null +++ b/scripts/fetch_card_releases.py @@ -0,0 +1,220 @@ +"""Fetch published card assets for integration packaging (never branch source).""" + +import argparse +from hashlib import sha256 +import importlib.util +import json +import os +from pathlib import Path +import re +import tempfile +from urllib.parse import urlparse +from urllib.request import Request, urlopen + +ROOT = Path(__file__).resolve().parents[1] +REGISTRY_PATH = ROOT / "custom_components/wiser/frontend/cards.json" + + +# Load the dependency-free schema without importing the Home Assistant package. +_SPEC = importlib.util.spec_from_file_location( + "wiser_frontend_manifest", ROOT / "custom_components/wiser/frontend/manifest.py" +) +if _SPEC is None or _SPEC.loader is None: + raise RuntimeError("Unable to load the frontend registry schema") +_MANIFEST = importlib.util.module_from_spec(_SPEC) +_SPEC.loader.exec_module(_MANIFEST) +validate_card_manifest = _MANIFEST.validate_manifest +validate_registry = _MANIFEST.validate_registry + + +def list_releases(repository): + if not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", repository): + raise ValueError(f"Invalid repository: {repository}") + releases = [] + for page in range(1, 101): + headers = {"Accept": "application/vnd.github+json", "User-Agent": "wiser-card-packager"} + if token := os.environ.get("GITHUB_TOKEN"): + headers["Authorization"] = f"Bearer {token}" + request = Request( + f"https://api.github.com/repos/{repository}/releases?per_page=100&page={page}", + headers=headers, + ) + with urlopen(request, timeout=30) as response: + batch = json.load(response) + releases.extend(batch) + if len(batch) < 100: + return releases + raise ValueError(f"Too many releases in {repository}") + + +def select_release(releases, channel): + """Select the most recently published release allowed by the channel.""" + published = [r for r in releases if not r.get("draft") and r.get("published_at")] + stable = [r for r in published if not r.get("prerelease")] + candidates = published if channel == "dev" else stable + if not candidates: + raise ValueError(f"No published {'prerelease or stable' if channel == 'dev' else 'stable'} release available") + return max(candidates, key=lambda r: r["published_at"]) + + +def select_asset(release, filename): + assets = [a for a in release.get("assets", []) if a["name"] == filename and a.get("state") == "uploaded"] + if len(assets) != 1: + raise ValueError(f"Release {release['tag_name']} must have one uploaded {filename} asset") + return assets[0] + + +def download_asset(repository, asset): + url = asset["browser_download_url"] + parsed = urlparse(url) + if parsed.scheme != "https" or parsed.netloc != "github.com" or not parsed.path.startswith(f"/{repository}/releases/download/"): + raise ValueError("Asset URL is not a GitHub release download for the selected repository") + # Public assets do not require forwarding the API token through redirects. + with urlopen(Request(url, headers={"User-Agent": "wiser-card-packager"}), timeout=60) as response: + contents = response.read() + if len(contents) != asset["size"] or len(contents) < 100: + raise ValueError(f"Incorrect size for {asset['name']}") + digest = "sha256:" + sha256(contents).hexdigest() + if asset.get("digest") and asset["digest"] != digest: + raise ValueError(f"Digest mismatch for {asset['name']}") + if contents.lstrip().lower().startswith((b"=3.11,<4 +awesomeversion>=24.6,<26 diff --git a/tests/test_build.py b/tests/test_build.py new file mode 100644 index 00000000..c8cc5bf8 --- /dev/null +++ b/tests/test_build.py @@ -0,0 +1,299 @@ +"""Test automatic card source selection and integration packaging.""" + +import importlib.util +import json +from pathlib import Path +import sys +from tempfile import TemporaryDirectory +import unittest +from unittest.mock import patch +from zipfile import ZipFile + +from test_card_releases import FETCH, release, CARD_MANIFEST, CARD_REPOSITORIES + +SPEC = importlib.util.spec_from_file_location( + "wiser_build", Path(__file__).resolve().parents[1] / "scripts/build.py" +) +BUILD = importlib.util.module_from_spec(SPEC) +with patch.dict(sys.modules, {"fetch_card_releases": FETCH}): + SPEC.loader.exec_module(BUILD) + + +class BuildTest(unittest.TestCase): + def setUp(self): + self.temporary = TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + self.root = Path(self.temporary.name) + self.source = self.root / "integration" + frontend = self.source / "frontend" + frontend.mkdir(parents=True) + self.output = self.root / "dist/wiser.zip" + self.output.parent.mkdir() + (self.output.parent / "panel-config-release.json").write_text("obsolete") + self.payloads = { + "schedule": b'customElements.define("wiser-schedule-card",class{});customElements.define("wiser-schedules-panel",class{})', + "zigbee": b'customElements.define("wiser-zigbee-card",class{});customElements.define("wiser-zigbee-panel",class{})', + "rooms": ( + b'customElements.define("wiser-controls-card",class{});' + b'customElements.define("wiser-controls-panel",class{})' + ), + "hub": b'customElements.define("wiser-hub-panel",class{})', + } + (frontend / "cards.json").write_text( + json.dumps({"schema_version": 1, "cards": CARD_MANIFEST}), + encoding="utf-8", + ) + for definition in CARD_MANIFEST: + (frontend / definition["filename"]).write_bytes(b"old tracked bundle") + (frontend / "__pycache__").mkdir() + (frontend / "__pycache__/old.pyc").touch() + + def local(self, card): + definition = next(item for item in CARD_MANIFEST if item["id"] == card) + path = ( + self.root + / definition["repository"].rsplit("/", 1)[-1] + / "dist" + / definition["filename"] + ) + path.parent.mkdir(parents=True) + path.write_bytes(self.payloads[card]) + return path + + def build(self): + return BUILD.build(self.source, self.output, self.root, "dev", CARD_REPOSITORIES) + + def filename(self, card): + return next( + item["filename"] for item in CARD_MANIFEST if item["id"] == card + ) + + def test_local_bundles_win_without_network_and_tracked_files_are_untouched(self): + for card in self.payloads: + self.local(card) + with patch.object(FETCH, "list_releases") as releases: + report = self.build() + releases.assert_not_called() + self.assertEqual([r["source"] for r in report], ["local"] * 4) + with ZipFile(self.output) as archive: + for card, payload in self.payloads.items(): + filename = self.filename(card) + self.assertEqual(archive.read(f"frontend/{filename}"), payload) + self.assertEqual( + (self.source / f"frontend/{filename}").read_bytes(), + b"old tracked bundle", + ) + self.assertEqual(json.loads(archive.read("frontend/card-releases.json")), report) + self.assertEqual( + json.loads(archive.read("frontend/cards.json")), + {"schema_version": 1, "cards": CARD_MANIFEST}, + ) + self.assertFalse(any("__pycache__" in name for name in archive.namelist())) + self.assertFalse((self.output.parent / "panel-config-release.json").exists()) + + def test_missing_local_bundle_falls_back_independently(self): + self.local("zigbee") + self.local("rooms") + self.local("hub") + with patch.object(FETCH, "list_releases", return_value=[release("v1", "2026", card="schedule")]) as releases, patch.object(FETCH, "download_asset", return_value=(self.payloads["schedule"], "sha256:test")): + report = self.build() + releases.assert_called_once_with(CARD_REPOSITORIES["schedule"]) + self.assertEqual( + [r["source"] for r in report], + ["release", "local", "local", "local"], + ) + + def test_without_frontend_keeps_registry_and_omits_every_bundle(self): + for card in self.payloads: + self.local(card) + (self.source / "frontend/wiser-rooms-card.js").write_bytes( + b"old legacy bundle" + ) + with patch.object(FETCH, "list_releases") as releases: + report = BUILD.build( + self.source, + self.output, + self.root, + "dev", + CARD_REPOSITORIES, + without_frontend=True, + ) + + releases.assert_not_called() + self.assertEqual([item["source"] for item in report], ["omitted"] * 4) + with ZipFile(self.output) as archive: + names = archive.namelist() + for definition in CARD_MANIFEST: + self.assertNotIn(f"frontend/{definition['filename']}", names) + self.assertNotIn("frontend/wiser-rooms-card.js", names) + self.assertEqual( + json.loads(archive.read("frontend/cards.json")), + {"schema_version": 1, "cards": CARD_MANIFEST}, + ) + + def test_no_local_bundles_downloads_all_releases(self): + with patch.object(FETCH, "list_releases", side_effect=[ + [release("v1", "2026", card="schedule")], + [release("v2", "2026", card="zigbee")], + [release("v3", "2026", card="rooms")], + [release("v4", "2026", card="hub")], + ]), patch.object(FETCH, "download_asset", side_effect=[ + (self.payloads["schedule"], "sha256:schedule"), + (self.payloads["zigbee"], "sha256:zigbee"), + (self.payloads["rooms"], "sha256:rooms"), + (self.payloads["hub"], "sha256:hub"), + ]): + report = self.build() + self.assertEqual([r["source"] for r in report], ["release"] * 4) + with ZipFile(self.output) as archive: + for card, payload in self.payloads.items(): + self.assertEqual( + archive.read(f"frontend/{self.filename(card)}"), payload + ) + + def test_bundled_registry_adds_an_unknown_card_and_repository(self): + for card in self.payloads: + self.local(card) + definition = { + "id": "metering", "name": "Electricity", + "filename": "wiser-electricity-card.js", "repository": "example/monitoring", + "component": "wiser-electricity-card", "panel": "wiser-electricity-panel", + } + manifest = [*CARD_MANIFEST, definition] + (self.source / "frontend/cards.json").write_text( + json.dumps({"schema_version": 1, "cards": manifest}) + ) + bundle = self.root / "monitoring/dist/wiser-electricity-card.js" + bundle.parent.mkdir(parents=True) + bundle.write_bytes(b"customElements.define('wiser-electricity-card',class{});customElements.define('wiser-electricity-panel',class{})") + with patch.object(FETCH, "list_releases") as releases: + report = BUILD.build(self.source, self.output, self.root, "dev") + releases.assert_not_called() + self.assertEqual(report[-1]["repository"], "example/monitoring") + with ZipFile(self.output) as archive: + self.assertEqual( + json.loads(archive.read("frontend/cards.json")), + {"schema_version": 1, "cards": manifest}, + ) + self.assertEqual(archive.read("frontend/wiser-electricity-card.js"), bundle.read_bytes()) + + def test_build_rejects_bundle_using_removed_settings_endpoint(self): + self.local("schedule").write_bytes(self.payloads["schedule"] + b';const api="wiser/anything_panel/configure";') + with self.assertRaisesRegex(ValueError, "generic settings API"): + self.build() + self.assertFalse(self.output.exists()) + + def test_failed_fallback_omits_only_the_unavailable_bundle(self): + self.local("schedule") + self.local("rooms") + self.local("hub") + self.output.write_bytes(b"previous package") + with patch.object(FETCH, "list_releases", side_effect=ValueError("unavailable")): + report = self.build() + self.assertEqual( + [record["source"] for record in report], + ["local", "unavailable", "local", "local"], + ) + self.assertEqual(report[1]["error"], "unavailable") + with ZipFile(self.output) as archive: + self.assertNotIn("frontend/wiser-zigbee-card.js", archive.namelist()) + self.assertEqual( + archive.read("frontend/wiser-schedule-card.js"), + self.payloads["schedule"], + ) + self.assertEqual( + archive.read("frontend/wiser-controls-card.js"), + self.payloads["rooms"], + ) + self.assertEqual( + json.loads(archive.read("frontend/cards.json")), + {"schema_version": 1, "cards": CARD_MANIFEST}, + ) + + def test_invalid_local_bundle_fails_instead_of_using_old_tracked_card(self): + self.local("schedule").write_bytes(b"wiser-schedule-card without panel") + with patch.object(FETCH, "list_releases") as releases: + with self.assertRaisesRegex(ValueError, "does not include the sidebar panel"): + self.build() + releases.assert_not_called() + self.assertFalse(self.output.exists()) + + def test_release_builds_ignore_local_bundles_for_both_channels(self): + for card in self.payloads: + self.local(card) + for channel, explicit_release in (("dev", True), ("stable", True), ("stable", False)): + with self.subTest(channel=channel, explicit_release=explicit_release): + with patch.object(FETCH, "list_releases", side_effect=[ + [release("v1", "2026", card="schedule")], + [release("v2", "2026", card="zigbee")], + [release("v3", "2026", card="rooms")], + [release("v4", "2026", card="hub")], + ]), patch.object(FETCH, "download_asset", side_effect=[ + (self.payloads["schedule"], "sha256:schedule"), + (self.payloads["zigbee"], "sha256:zigbee"), + ( + b'customElements.define("wiser-controls-card",class{});' + b'customElements.define("wiser-controls-panel",class{})', + "sha256:rooms", + ), + (self.payloads["hub"], "sha256:hub"), + ]) as download: + report = BUILD.build( + self.source, self.output, self.root, channel, + CARD_REPOSITORIES, release=explicit_release, + ) + self.assertEqual(download.call_count, 4) + self.assertEqual([r["source"] for r in report], ["release"] * 4) + with ZipFile(self.output) as archive: + self.assertEqual( + archive.read("frontend/wiser-zigbee-card.js"), + self.payloads["zigbee"], + ) + self.assertEqual( + json.loads(archive.read("frontend/cards.json")), + {"schema_version": 1, "cards": CARD_MANIFEST}, + ) + self.assertEqual( + archive.read("frontend/wiser-controls-card.js"), + self.payloads["rooms"], + ) + + def test_stable_build_omits_an_unavailable_published_bundle(self): + with patch.object(FETCH, "list_releases", side_effect=[ + [release("v1", "2026", card="schedule")], + ValueError("repository unavailable"), + [release("v3", "2026", card="rooms")], + [release("v4", "2026", card="hub")], + ]), patch.object(FETCH, "download_asset", side_effect=[ + (self.payloads["schedule"], "sha256:schedule"), + (self.payloads["rooms"], "sha256:rooms"), + (self.payloads["hub"], "sha256:hub"), + ]): + report = BUILD.build( + self.source, self.output, self.root, "stable", CARD_REPOSITORIES, + ) + + self.assertEqual( + [record["source"] for record in report], + ["release", "unavailable", "release", "release"], + ) + with ZipFile(self.output) as archive: + self.assertIn("frontend/wiser-schedule-card.js", archive.namelist()) + self.assertNotIn("frontend/wiser-zigbee-card.js", archive.namelist()) + self.assertIn("frontend/wiser-controls-card.js", archive.namelist()) + self.assertIn("frontend/wiser-hub-panel.js", archive.namelist()) + self.assertEqual( + json.loads(archive.read("frontend/cards.json")), + {"schema_version": 1, "cards": CARD_MANIFEST}, + ) + + def test_release_failure_omits_bundles_without_falling_back_to_local(self): + for card in self.payloads: + self.local(card) + with patch.object(FETCH, "list_releases", side_effect=ValueError("unavailable")): + report = BUILD.build( + self.source, self.output, self.root, "dev", + CARD_REPOSITORIES, release=True, + ) + self.assertTrue(self.output.exists()) + self.assertTrue(all(item["source"] == "unavailable" for item in report)) diff --git a/tests/test_card_releases.py b/tests/test_card_releases.py new file mode 100644 index 00000000..d6d25043 --- /dev/null +++ b/tests/test_card_releases.py @@ -0,0 +1,138 @@ +"""Test release-channel selection and safe staging of downloaded card assets.""" +import importlib.util +import json +from pathlib import Path +from tempfile import TemporaryDirectory +import unittest +from unittest.mock import patch + +SPEC = importlib.util.spec_from_file_location("fetch_card_releases", Path(__file__).resolve().parents[1] / "scripts/fetch_card_releases.py") +FETCH = importlib.util.module_from_spec(SPEC) +SPEC.loader.exec_module(FETCH) + + +CARD_MANIFEST = json.loads( + ( + Path(__file__).resolve().parents[1] + / "custom_components/wiser/frontend/cards.json" + ).read_text() +)["cards"] +CARD_REPOSITORIES = {card["id"]: card["repository"] for card in CARD_MANIFEST} +CARD_DEFINITIONS = {card["id"]: card for card in CARD_MANIFEST} + + +def release(tag, date, prerelease=False, card="zigbee", draft=False, asset=True): + definition = CARD_DEFINITIONS[card] + filename = definition["filename"] + repository = definition["repository"] + return { + "tag_name": tag, "published_at": date, "prerelease": prerelease, "draft": draft, + "assets": [{"id": 1, "name": filename, "state": "uploaded", "size": 200, + "browser_download_url": f"https://github.com/{repository}/releases/download/{tag}/{filename}"}] if asset else [], + } + + +class CardReleaseTest(unittest.TestCase): + def setUp(self): + self.beta = release("v2.0.0-beta.1", "2026-09-01T00:00:00Z", True) + self.stable = release("v2.0.1", "2026-09-02T00:00:00Z") + self.releases = [self.stable, self.beta, release("draft", "2026-09-03T00:00:00Z", True, draft=True)] + + def test_dev_selects_newer_stable_over_older_prerelease(self): + self.assertEqual(FETCH.select_release(self.releases, "dev"), self.stable) + + def test_dev_selects_newer_prerelease_over_older_stable(self): + newer_beta = release("v2.1.0-beta.1", "2026-09-04T00:00:00Z", True) + releases = [newer_beta, *self.releases] + self.assertEqual(FETCH.select_release(releases, "dev"), newer_beta) + self.assertEqual(FETCH.select_release(releases, "stable"), self.stable) + + def test_unpublished_releases_are_excluded(self): + unpublished = release("v4.0.0", None) + for channel in ("dev", "stable"): + with self.subTest(channel=channel): + self.assertEqual( + FETCH.select_release([unpublished, *self.releases], channel), + self.stable, + ) + + def test_stable_excludes_prereleases_and_drafts(self): + self.assertEqual(FETCH.select_release(self.releases, "stable"), self.stable) + + def test_dev_falls_back_to_stable(self): + self.assertEqual(FETCH.select_release([self.stable], "dev"), self.stable) + + def test_stable_does_not_fall_back_to_beta(self): + with self.assertRaisesRegex(ValueError, "No published stable"): + FETCH.select_release([self.beta], "stable") + + def test_latest_is_selected_by_publication_date(self): + older = release("old", "2026-08-01T00:00:00Z", True) + self.assertEqual(FETCH.select_release([older, self.beta], "dev"), self.beta) + + def test_missing_release_or_asset_fails(self): + with self.assertRaises(ValueError): + FETCH.select_release([], "dev") + with self.assertRaisesRegex(ValueError, "must have one"): + FETCH.select_asset(release("missing", "2026", asset=False), "wiser-zigbee-card.js") + + def test_registry_accepts_panel_only_bundle_definition(self): + definition = { + "id": "hub", + "name": "Wiser Hub Panel", + "filename": "wiser-hub-panel.js", + "repository": "andyblac/wiser-hub-panel", + "component": "wiser-hub-panel", + "panel": "wiser-hub-panel", + "card": False, + } + self.assertEqual(FETCH.validate_card_manifest([definition]), [definition]) + + def test_registry_rejects_invalid_card_flag(self): + definition = CARD_MANIFEST[0] | {"card": "false"} + with self.assertRaisesRegex(ValueError, "card flag"): + FETCH.validate_card_manifest([definition]) + + def test_plan_does_not_download_or_write(self): + with TemporaryDirectory() as directory: + out = Path(directory) / "not-created" + with patch.object(FETCH, "list_releases", return_value=self.releases), patch.object(FETCH, "download_asset") as download: + report = FETCH.fetch_cards("dev", out, {"zigbee": "andyblac/wiser-zigbee-card"}, definitions=CARD_DEFINITIONS, plan=True) + download.assert_not_called() + self.assertFalse(out.exists()) + self.assertEqual(report[0]["tag"], "v2.0.1") + + def test_download_failure_does_not_replace_either_card(self): + with TemporaryDirectory() as directory: + out = Path(directory) + (out / "wiser-schedule-card.js").write_bytes(b"old") + releases = [[release("schedule", "2026", True, card="schedule")], [self.beta]] + schedule = ( + b'customElements.define("wiser-schedule-card",class{});' + b'customElements.define("wiser-schedules-panel",class{})' + ) + with patch.object(FETCH, "list_releases", side_effect=releases), patch.object(FETCH, "download_asset", side_effect=[(schedule, "sha256:test"), ValueError("download failed")]): + with self.assertRaises(ValueError): + FETCH.fetch_cards("dev", out, CARD_REPOSITORIES, CARD_DEFINITIONS) + self.assertEqual((out / "wiser-schedule-card.js").read_bytes(), b"old") + self.assertFalse((out / "card-releases.json").exists()) + + def test_download_written_to_correct_filename_with_provenance(self): + with TemporaryDirectory() as directory: + out = Path(directory) + card = ( + b'customElements.define("wiser-zigbee-card",class{});' + b'customElements.define("wiser-zigbee-panel",class{})' + ) + with patch.object(FETCH, "list_releases", return_value=self.releases), patch.object(FETCH, "download_asset", return_value=(card, "sha256:test")): + FETCH.fetch_cards("dev", out, {"zigbee": "andyblac/wiser-zigbee-card"}, CARD_DEFINITIONS) + self.assertEqual((out / "wiser-zigbee-card.js").read_bytes(), card) + self.assertEqual(json.loads((out / "card-releases.json").read_text())[0]["digest"], "sha256:test") + + def test_panel_card_rejects_bundle_without_panel_component(self): + with TemporaryDirectory() as directory: + out = Path(directory) + old_card = b'customElements.define("wiser-schedule-card",class{})' + with patch.object(FETCH, "list_releases", return_value=[release("old", "2026", card="schedule")]), patch.object(FETCH, "download_asset", return_value=(old_card, "sha256:test")): + with self.assertRaisesRegex(ValueError, "does not include the sidebar panel"): + FETCH.fetch_cards("stable", out, {"schedule": "andyblac/wiser-schedule-card"}, CARD_DEFINITIONS) diff --git a/tests/test_card_updates.py b/tests/test_card_updates.py new file mode 100644 index 00000000..a6eb2698 --- /dev/null +++ b/tests/test_card_updates.py @@ -0,0 +1,569 @@ +"""Exercise persistent card installs and update entities without a running HA.""" + +import ast +from datetime import timedelta +from hashlib import sha256 +import importlib.util +import json +from pathlib import Path +import re +import sys +from tempfile import TemporaryDirectory +from types import ModuleType, SimpleNamespace +import unittest +from unittest.mock import AsyncMock, Mock, patch + +ROOT = Path(__file__).resolve().parents[1] / "custom_components/wiser" + + +def module(name, **attrs): + result = ModuleType(name) + result.__dict__.update(attrs) + return result + + +def load(name, path): + spec = importlib.util.spec_from_file_location(name, path) + result = importlib.util.module_from_spec(spec) + sys.modules[name] = result + spec.loader.exec_module(result) + return result + + +class Coordinator: + def __init__(self, hass, logger, **kwargs): + self.hass = hass + self.options = kwargs + self.data = {} + self.async_update_listeners = Mock() + self.async_shutdown = AsyncMock() + + async def async_refresh(self): + self.data = await self._async_update_data() + + +class CoordinatorEntity: + def __init__(self, coordinator): + self.coordinator = coordinator + + @property + def available(self): + return True + + +class CardUpdatesTest(unittest.IsolatedAsyncioTestCase): + def setUp(self): + self.temporary = TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + self.directory = Path(self.temporary.name) + self.registry = SimpleNamespace( + async_get_entity_id=Mock(return_value=None), + async_update_entity=Mock(), + async_remove=Mock(), + ) + self.session = Mock() + self.registration = SimpleNamespace(async_register=AsyncMock()) + frontend = module("card_update_test.frontend", __path__=[str(ROOT / "frontend")]) + self.modules = patch.dict(sys.modules, { + "card_update_test": module("card_update_test", __path__=[str(ROOT)]), + "card_update_test.const": module( + "card_update_test.const", URL_BASE="/wiser", DOMAIN="wiser", + CARD_MANIFEST=json.loads((ROOT / "frontend/cards.json").read_text())[ + "cards" + ], + ), + "card_update_test.frontend": frontend, + "homeassistant": module("homeassistant"), + "homeassistant.components": module("homeassistant.components"), + "homeassistant.components.update": module("homeassistant.components.update", UpdateEntity=type("UpdateEntity", (), {}), UpdateEntityFeature=SimpleNamespace(INSTALL=1)), + "homeassistant.const": module("homeassistant.const", EntityCategory=SimpleNamespace(CONFIG="config")), + "homeassistant.exceptions": module("homeassistant.exceptions", HomeAssistantError=RuntimeError), + "homeassistant.helpers": module("homeassistant.helpers"), + "homeassistant.helpers.entity_registry": module("homeassistant.helpers.entity_registry", async_get=lambda _: self.registry), + "homeassistant.helpers.aiohttp_client": module("homeassistant.helpers.aiohttp_client", async_get_clientsession=lambda _: self.session), + "homeassistant.helpers.update_coordinator": module("homeassistant.helpers.update_coordinator", CoordinatorEntity=CoordinatorEntity, DataUpdateCoordinator=Coordinator), + "card_update_test.frontend.wiser_sidebar": module( + "card_update_test.frontend.wiser_sidebar", + async_update_wiser_panel=AsyncMock(), + ), + }) + self.modules.start() + self.addCleanup(self.modules.stop) + self.files = load("card_update_test.frontend.card_files", ROOT / "frontend/card_files.py") + self.version_reader = Mock(return_value="1.0.0") + async def resource(hass, filename): + return self.files.resolve_card(self.directory, filename, self.version_reader) + frontend.async_card_resource = resource + frontend.JSModuleRegistration = Mock(return_value=self.registration) + self.updates = load("card_update_test.update", ROOT / "update.py") + self.hass = SimpleNamespace( + data={}, config=SimpleNamespace(path=lambda *args: str(self.directory.joinpath(*args))), + async_add_executor_job=AsyncMock(side_effect=lambda fn, *args: fn(*args)), + states=SimpleNamespace(is_state=Mock(return_value=False)), + ) + self.coordinator = self.updates.CardUpdateCoordinator(self.hass) + + def payload(self, card="zigbee", version="2.0.0"): + definition = self.coordinator.cards[card] + filename, component = definition["filename"], definition["component"] + panel = definition.get("panel") + panel_source = ( + f"customElements.define('{panel}', class extends HTMLElement {{}});" + if panel else "" + ) + return (f"/*! WISER-CARD-VERSION {filename[:-3]} {version} */\n" + f"customElements.define('{component}', class extends HTMLElement {{}});" + f"{panel_source}").encode() + + def metadata(self, card="zigbee", version="2.0.0", prerelease=False): + contents = self.payload(card, version) + definition = self.coordinator.cards[card] + repo, filename = definition["repository"], definition["filename"] + return { + "tag_name": f"v{version}", "published_at": "2026-09-20", "draft": False, "prerelease": prerelease, + "assets": [{"name": filename, "state": "uploaded", "size": len(contents), + "digest": "sha256:" + sha256(contents).hexdigest(), + "browser_download_url": f"https://github.com/{repo}/releases/download/v{version}/{filename}"}], + } + + def response(self, *, json_data=None, contents=b"", error=None): + response = SimpleNamespace(raise_for_status=Mock(side_effect=error), json=AsyncMock(return_value=json_data)) + async def chunks(size): + yield contents + response.content = SimpleNamespace(iter_chunked=chunks) + context = type("ResponseContext", (), { + "__aenter__": AsyncMock(return_value=response), "__aexit__": AsyncMock(return_value=False) + })() + return context + + async def test_update_check_reloads_local_registry_and_discovers_new_card(self): + definition = { + "id": "energy", + "name": "Wiser Energy Card", + "filename": "wiser-energy-card.js", + "repository": "andyblac/wiser-energy-card", + "component": "wiser-energy-card", + "panel": "wiser-energy-panel", + } + registry = sys.modules["card_update_test.frontend.registry"] + registry_path = self.directory / "cards.json" + registry_path.write_text( + json.dumps( + { + "schema_version": 1, + "cards": [*self.coordinator.cards.values(), definition], + } + ) + ) + self.coordinator._check_card = AsyncMock( + return_value={"installed": "0.0.0", "release": None, "error": None} + ) + add = Mock() + self.coordinator.entries["hub"] = add + self.coordinator.add_entities("hub") + add.reset_mock() + + with patch.object(registry, "REGISTRY_PATH", registry_path): + await self.coordinator.async_refresh() + + self.assertIn("energy", self.coordinator.cards) + self.assertEqual([entity.card for entity in add.call_args.args[0]], ["energy"]) + + async def test_invalid_local_registry_keeps_last_valid_definitions(self): + registry = sys.modules["card_update_test.frontend.registry"] + self.hass.data[registry.REGISTRY_DATA] = list(self.coordinator.cards.values()) + registry_path = self.directory / "cards.json" + registry_path.write_text('{"schema_version": 1, "cards": [{"id": "bad"}]}') + + with patch.object(registry, "REGISTRY_PATH", registry_path), self.assertLogs( + "card_update_test.frontend.registry", level="WARNING" + ): + manifest = await registry.async_load_registry(self.hass) + + self.assertEqual(manifest, list(self.coordinator.cards.values())) + + def test_rejects_bundle_using_removed_panel_specific_settings_api(self): + definition = self.coordinator.cards["zigbee"] + contents = self.payload() + b';const api="wiser/anything_panel/configure";' + metadata = self.metadata() + metadata["assets"][0].update(size=len(contents), digest="sha256:" + sha256(contents).hexdigest()) + release = self.updates._validate_release(definition, metadata) + with self.assertRaisesRegex(ValueError, "generic settings API"): + self.updates._validate_download(definition, release, contents) + + def test_rooms_release_is_validated_and_cached_independently(self): + release = self.updates._validate_release(self.coordinator.cards["rooms"], self.metadata("rooms")) + contents = self.payload("rooms") + self.updates._validate_download(self.coordinator.cards["rooms"], release, contents) + self.files.store_card(self.directory, "wiser-controls-card.js", "2.0.0", contents) + path, _, version = self.files.resolve_card(self.directory, "wiser-controls-card.js", self.version_reader) + self.assertEqual(path.read_bytes(), contents) + self.assertEqual(version, "2.0.0") + + def test_persistent_cache_survives_restart_and_newer_bundle_takes_precedence(self): + contents = self.payload() + self.files.store_card(self.directory, "wiser-zigbee-card.js", "2.0.0", contents) + path, url, version = self.files.resolve_card(self.directory, "wiser-zigbee-card.js", self.version_reader) + self.assertEqual(path.read_bytes(), contents) + self.assertIn("/wiser/cards/", url) + self.assertEqual(version, "2.0.0") + self.version_reader.return_value = "3.0.0" + path, url, version = self.files.resolve_card(self.directory, "wiser-zigbee-card.js", self.version_reader) + self.assertEqual(path, ROOT / "frontend/wiser-zigbee-card.js") + self.assertEqual(version, "3.0.0") + + def test_corrupt_cache_falls_back_to_bundled_card(self): + self.files.store_card(self.directory, "wiser-zigbee-card.js", "2.0.0", self.payload()) + path, _, _ = self.files.resolve_card(self.directory, "wiser-zigbee-card.js", self.version_reader) + path.write_bytes(b"corrupt") + _, url, version = self.files.resolve_card(self.directory, "wiser-zigbee-card.js", self.version_reader) + self.assertEqual(url, "/wiser/wiser-zigbee-card.js?v=1.0.0") + self.assertEqual(version, "1.0.0") + + def test_failed_metadata_write_keeps_previous_version(self): + self.files.store_card(self.directory, "wiser-zigbee-card.js", "2.0.0", self.payload()) + original = self.files._atomic_write + def write(path, contents): + if path.suffix == ".json": + raise OSError("disk full") + original(path, contents) + with patch.object(self.files, "_atomic_write", side_effect=write): + with self.assertRaises(OSError): + self.files.store_card(self.directory, "wiser-zigbee-card.js", "3.0.0", self.payload(version="3.0.0")) + self.assertEqual(self.files.resolve_card(self.directory, "wiser-zigbee-card.js", self.version_reader)[2], "2.0.0") + + def test_stable_checks_do_not_offer_downgrades_or_unknown_versions(self): + self.assertTrue(self.files.newer_version("2.0.0", "2.0.0-rc1")) + self.assertFalse(self.files.newer_version("2.0.0", "3.0.0-dev.84")) + self.assertFalse(self.files.newer_version("2.0.0", "2.0.0")) + self.assertFalse(self.files.newer_version("2.0.0", "sha256-abcd")) + + def test_rejects_prereleases_and_foreign_download_urls(self): + for change in ("prerelease", "draft", "url", "size"): + metadata = self.metadata() + if change in ("prerelease", "draft"): + metadata[change] = True + elif change == "url": + metadata["assets"][0]["browser_download_url"] = "https://example.com/card.js" + else: + metadata["assets"][0]["size"] = 21 * 1024 * 1024 + with self.subTest(change=change), self.assertRaises(ValueError): + self.updates._validate_release(self.coordinator.cards["zigbee"], metadata) + + def test_accepts_prerelease_only_when_enabled(self): + metadata = self.metadata(version="2.1.0-beta.1", prerelease=True) + with self.assertRaises(ValueError): + self.updates._validate_release(self.coordinator.cards["zigbee"], metadata) + release = self.updates._validate_release( + self.coordinator.cards["zigbee"], metadata, allow_prerelease=True + ) + self.assertEqual(release["version"], "2.1.0-beta.1") + + async def test_hacs_prerelease_switch_enables_card_prereleases(self): + self.registry.async_get_entity_id.return_value = "switch.wiser_pre_release" + self.hass.states.is_state.return_value = True + releases = [ + self.metadata(version="2.0.1"), + self.metadata(version="2.1.0-beta.1", prerelease=True), + ] + self.session.get.return_value = self.response(json_data=releases) + state = await self.coordinator._check_card("zigbee") + self.assertEqual(state["release"]["version"], "2.1.0-beta.1") + self.assertIn("/releases?per_page=100", self.session.get.call_args.args[0]) + + async def test_card_updates_remain_stable_without_hacs_prereleases(self): + self.session.get.return_value = self.response(json_data=self.metadata()) + state = await self.coordinator._check_card("zigbee") + self.assertEqual(state["release"]["version"], "2.0.0") + self.assertIn("/releases/latest", self.session.get.call_args.args[0]) + + def test_rejects_bad_checksum_and_wrong_version_or_panel(self): + release = self.updates._validate_release(self.coordinator.cards["zigbee"], self.metadata()) + with self.assertRaises(ValueError): + self.updates._validate_download(self.coordinator.cards["zigbee"], release, self.payload().replace(b"2.0.0", b"9.0.0")) + for contents in ( + self.payload(version="9.0.0"), + self.payload().replace(b"wiser-zigbee-card", b"other-widget-name"), + self.payload().replace(b"wiser-zigbee-panel", b"other-panel-name"), + ): + release["asset"]["digest"] = None + release["asset"]["size"] = len(contents) + with self.assertRaises(ValueError): + self.updates._validate_download(self.coordinator.cards["zigbee"], release, contents) + + async def test_network_failure_does_not_block_other_card_or_hub_setup(self): + import aiohttp + self.session.get.side_effect = [ + self.response(error=aiohttp.ClientError("offline")), + self.response(json_data=self.metadata()), + self.response(json_data=self.metadata("rooms")), + self.response(json_data=self.metadata("hub")), + ] + with self.assertLogs("card_update_test.update", level="WARNING"): + await self.coordinator.async_refresh() + self.assertIsNone(self.coordinator.data["schedule"]["release"]) + self.assertEqual(self.coordinator.data["zigbee"]["release"]["version"], "2.0.0") + self.assertEqual(self.coordinator.data["rooms"]["release"]["version"], "2.0.0") + self.assertEqual(self.coordinator.data["hub"]["release"]["version"], "2.0.0") + self.assertEqual(self.coordinator.options["update_interval"], timedelta(hours=24)) + + async def test_check_does_not_install_until_user_requests_it(self): + self.session.get.return_value = self.response(json_data=self.metadata()) + state = await self.coordinator._check_card("zigbee") + self.assertFalse((self.directory / self.files.CARD_CACHE).exists()) + self.coordinator.data["zigbee"] = state + self.session.get.return_value = self.response(contents=self.payload()) + await self.coordinator.install("zigbee") + self.assertEqual(self.coordinator.data["zigbee"]["installed"], "2.0.0") + self.assertEqual(self.files.resolve_card(self.directory, "wiser-zigbee-card.js", self.version_reader)[2], "2.0.0") + self.registration.async_register.assert_awaited_once() + self.updates.async_update_wiser_panel.assert_awaited_once() + self.assertFalse(self.coordinator.installing) + + async def test_failed_frontend_refresh_can_retry_without_downloading_again(self): + for target in ( + self.registration.async_register, + self.updates.async_update_wiser_panel, + ): + with self.subTest(target=target): + self.files.store_card( + self.directory, "wiser-zigbee-card.js", "1.5.0", + self.payload(version="1.5.0"), + ) + previous, _, _ = self.files.resolve_card( + self.directory, "wiser-zigbee-card.js", self.version_reader + ) + release = self.updates._validate_release(self.coordinator.cards["zigbee"], self.metadata()) + self.coordinator.data["zigbee"] = {"installed": "1.5.0", "release": release} + self.session.get.reset_mock() + self.session.get.return_value = self.response(contents=self.payload()) + target.side_effect = RuntimeError("frontend unavailable") + with self.assertRaisesRegex(RuntimeError, "Unable to install"): + await self.coordinator.install("zigbee") + self.assertEqual(self.coordinator.data["zigbee"]["installed"], "1.5.0") + self.assertTrue(previous.exists()) + self.assertFalse(self.coordinator.installing) + self.session.get.assert_called_once() + + # A scheduled release check must not hide the unfinished update. + self.session.get.return_value = self.response(json_data=self.metadata()) + self.coordinator.data["zigbee"] = await self.coordinator._check_card("zigbee") + self.assertEqual(self.coordinator.data["zigbee"]["installed"], "1.5.0") + self.session.get.reset_mock() + target.side_effect = None + await self.coordinator.install("zigbee") + self.session.get.assert_not_called() + self.assertEqual(self.coordinator.data["zigbee"]["installed"], "2.0.0") + self.assertFalse(self.coordinator.pending_refresh) + self.assertFalse(self.coordinator.installing) + self.assertTrue(previous.exists()) + + async def test_successful_updates_retain_only_current_and_previous_card_bundles(self): + filename = "wiser-zigbee-card.js" + cache = self.directory / self.files.CARD_CACHE + self.files.store_card(self.directory, "wiser-schedule-card.js", "2.0.0", self.payload("schedule")) + other_files = set(cache.iterdir()) + unrelated = cache / "wiser-zigbee-card-not-a-digest.js" + unrelated.write_text("unrelated") + for version in ("2.0.0", "3.0.0", "4.0.0"): + release = self.updates._validate_release(self.coordinator.cards["zigbee"], self.metadata(version=version)) + self.coordinator.data["zigbee"] = {"release": release} + self.session.get.return_value = self.response(contents=self.payload(version=version)) + await self.coordinator.install("zigbee") + bundles = [p for p in cache.glob("wiser-zigbee-card-*.js") if p != unrelated] + self.assertEqual(len(bundles), 2) + self.assertEqual( + {re.search(rb"card (\S+) ", p.read_bytes())[1].decode() for p in bundles}, + {"3.0.0", "4.0.0"}, + ) + self.assertTrue(all(p.exists() for p in other_files | {unrelated})) + self.assertEqual(self.files.resolve_card(self.directory, filename, self.version_reader)[2], "4.0.0") + + async def test_cache_cleanup_failure_does_not_fail_installation(self): + release = self.updates._validate_release(self.coordinator.cards["zigbee"], self.metadata()) + self.coordinator.data["zigbee"] = {"installed": "1.0.0", "release": release} + self.session.get.return_value = self.response(contents=self.payload()) + with patch.object(Path, "iterdir", side_effect=OSError("permission denied")): + with self.assertLogs("card_update_test.frontend.card_files", level="WARNING"): + await self.coordinator.install("zigbee") + self.assertEqual(self.coordinator.data["zigbee"]["installed"], "2.0.0") + self.assertFalse(self.coordinator.pending_refresh) + + async def test_invalid_download_keeps_existing_installation(self): + release = self.updates._validate_release(self.coordinator.cards["zigbee"], self.metadata()) + self.coordinator.data["zigbee"] = {"installed": "1.0.0", "release": release} + self.session.get.return_value = self.response(contents=b"broken") + with self.assertRaisesRegex(RuntimeError, "Unable to install"): + await self.coordinator.install("zigbee") + self.assertEqual(self.coordinator.data["zigbee"]["installed"], "1.0.0") + self.assertFalse((self.directory / self.files.CARD_CACHE).exists()) + self.assertFalse(self.coordinator.installing) + self.registration.async_register.assert_not_awaited() + + async def test_multiple_hubs_share_entities_and_transfer_when_owner_unloads(self): + self.hass.data["wiser_card_updates"] = self.coordinator + self.coordinator.async_refresh = AsyncMock() + first, second = Mock(), Mock() + await self.updates.async_setup_entry(self.hass, SimpleNamespace(entry_id="first"), first) + await self.updates.async_setup_entry(self.hass, SimpleNamespace(entry_id="second"), second) + self.coordinator.async_refresh.assert_awaited_once() + self.assertEqual(len(first.call_args.args[0]), 4) + second.assert_not_called() + self.registry.async_get_entity_id.side_effect = lambda domain, platform, unique_id: f"update.{unique_id}" + await self.updates.async_unload_card_updates(self.hass, SimpleNamespace(entry_id="first")) + self.assertEqual(self.coordinator.owner, "second") + self.assertEqual(len(second.call_args.args[0]), 4) + self.registry.async_remove.assert_called_with( + "update.wiser_frontend_registry_update" + ) + self.assertEqual(self.registry.async_update_entity.call_args.kwargs["config_entry_id"], "second") + await self.updates.async_unload_card_updates(self.hass, SimpleNamespace(entry_id="second")) + self.coordinator.async_shutdown.assert_awaited_once() + self.assertNotIn("wiser_card_updates", self.hass.data) + + async def test_update_entity_delegates_manual_install_and_exposes_release(self): + self.coordinator.data["zigbee"] = {"installed": "1.0.0", "release": self.updates._validate_release(self.coordinator.cards["zigbee"], self.metadata())} + entity = self.updates.WiserCardUpdate(self.coordinator, "zigbee") + self.assertTrue(entity.available) + self.assertEqual(entity.name, "Wiser Zigbee Card") + self.coordinator.cards["zigbee"] = { + **self.coordinator.cards["zigbee"], "name": "Renamed Zigbee Card" + } + self.assertEqual(entity.name, "Renamed Zigbee Card") + self.assertEqual(entity.installed_version, "1.0.0") + self.assertEqual(entity.latest_version, "2.0.0") + self.assertIn("/releases/tag/v2.0.0", entity.release_url) + self.coordinator.install = AsyncMock() + await entity.async_install(None, False) + self.coordinator.install.assert_awaited_once_with("zigbee") + + async def test_lovelace_resource_moves_to_cached_url_without_duplicate(self): + tree = ast.parse((ROOT / "frontend/__init__.py").read_text()) + cls = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == "JSModuleRegistration") + active = "/wiser/cards/wiser-zigbee-card-abc.js?v=2.0.0" + resource = {"id": "card", "url": "/wiser/wiser-zigbee-card.js?v=1.0.0"} + resources = SimpleNamespace( + async_items=lambda: [resource], async_update_item=AsyncMock(), + async_create_item=AsyncMock(), async_delete_item=AsyncMock(), + ) + self.hass.data["lovelace"] = SimpleNamespace(mode="storage", resources=resources) + namespace = { + "asyncio": __import__("asyncio"), + "HomeAssistant": object, "LovelaceData": object, "Path": Path, + "MAJOR_VERSION": 2025, "MINOR_VERSION": 5, + "MODE_STORAGE": "storage", "URL_BASE": "/wiser", + "CARD_CACHE_URL": "/wiser/cards", "_LOGGER": Mock(), + "get_manifest": lambda hass: [{"filename": "wiser-zigbee-card.js", "name": "Zigbee"}], + "async_card_resource": AsyncMock(return_value=(None, active, "2.0.0")), + } + exec(compile(ast.Module(body=[cls], type_ignores=[]), "frontend/__init__.py", "exec"), namespace) + registration = namespace["JSModuleRegistration"](self.hass) + registration.async_remove_gzip_files = AsyncMock() + await registration._async_register_modules() + resources.async_update_item.assert_awaited_once_with("card", {"res_type": "module", "url": active}) + resources.async_create_item.assert_not_awaited() + resource["url"] = active + resources.async_update_item.reset_mock() + await registration._async_register_modules() + resources.async_update_item.assert_not_awaited() + resources.async_create_item.assert_not_awaited() + await registration.async_unregister() + resources.async_delete_item.assert_awaited_once_with("card") + + async def test_panel_only_bundle_is_not_registered_as_lovelace_card(self): + tree = ast.parse((ROOT / "frontend/__init__.py").read_text()) + cls = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == "JSModuleRegistration") + resources = SimpleNamespace( + async_items=lambda: [], async_update_item=AsyncMock(), + async_create_item=AsyncMock(), async_delete_item=AsyncMock(), + ) + self.hass.data["lovelace"] = SimpleNamespace(mode="storage", resources=resources) + resolve = AsyncMock() + namespace = { + "asyncio": __import__("asyncio"), + "HomeAssistant": object, "LovelaceData": object, "Path": Path, + "MAJOR_VERSION": 2025, "MINOR_VERSION": 5, + "MODE_STORAGE": "storage", "URL_BASE": "/wiser", + "CARD_CACHE_URL": "/wiser/cards", "_LOGGER": Mock(), + "get_manifest": lambda hass: [{ + "filename": "wiser-hub-panel.js", "name": "Hub", "card": False, + }], + "async_card_resource": resolve, + } + exec(compile(ast.Module(body=[cls], type_ignores=[]), "frontend/__init__.py", "exec"), namespace) + registration = namespace["JSModuleRegistration"](self.hass) + await registration._async_register_modules() + resolve.assert_not_awaited() + resources.async_create_item.assert_not_awaited() + await registration.async_unregister() + resources.async_delete_item.assert_not_awaited() + + async def test_duplicate_lovelace_resources_are_removed(self): + tree = ast.parse((ROOT / "frontend/__init__.py").read_text()) + cls = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == "JSModuleRegistration") + active = "/wiser/cards/wiser-controls-card-abc.js?v=1.0.0" + resources = SimpleNamespace( + async_items=lambda: [ + {"id": "older", "url": active}, + {"id": "newer", "url": active}, + ], + async_update_item=AsyncMock(), async_create_item=AsyncMock(), + async_delete_item=AsyncMock(), + ) + self.hass.data["lovelace"] = SimpleNamespace(mode="storage", resources=resources) + namespace = { + "asyncio": __import__("asyncio"), + "HomeAssistant": object, "LovelaceData": object, "Path": Path, + "MAJOR_VERSION": 2025, "MINOR_VERSION": 5, + "MODE_STORAGE": "storage", "URL_BASE": "/wiser", + "CARD_CACHE_URL": "/wiser/cards", "_LOGGER": Mock(), + "get_manifest": lambda hass: [{ + "filename": "wiser-controls-card.js", "name": "Controls", + }], + "async_card_resource": AsyncMock(return_value=(None, active, "1.0.0")), + } + exec(compile(ast.Module(body=[cls], type_ignores=[]), "frontend/__init__.py", "exec"), namespace) + registration = namespace["JSModuleRegistration"](self.hass) + await registration._async_register_modules() + resources.async_delete_item.assert_awaited_once_with("older") + resources.async_update_item.assert_not_awaited() + resources.async_create_item.assert_not_awaited() + + async def test_renamed_card_reuses_legacy_lovelace_resource(self): + tree = ast.parse((ROOT / "frontend/__init__.py").read_text()) + cls = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == "JSModuleRegistration") + active = "/wiser/cards/wiser-controls-card-abc.js?v=1.0.0" + resource = {"id": "card", "url": "/wiser/cards/wiser-rooms-card-old.js?v=0.9.0"} + resources = SimpleNamespace( + async_items=lambda: [resource], async_update_item=AsyncMock(), + async_create_item=AsyncMock(), async_delete_item=AsyncMock(), + ) + self.hass.data["lovelace"] = SimpleNamespace(mode="storage", resources=resources) + namespace = { + "asyncio": __import__("asyncio"), + "HomeAssistant": object, "LovelaceData": object, "Path": Path, + "MAJOR_VERSION": 2025, "MINOR_VERSION": 5, + "MODE_STORAGE": "storage", "URL_BASE": "/wiser", + "CARD_CACHE_URL": "/wiser/cards", "_LOGGER": Mock(), + "get_manifest": lambda hass: [{ + "filename": "wiser-controls-card.js", + "legacy_filenames": ["wiser-rooms-card.js"], + "name": "Controls", + }], + "async_card_resource": AsyncMock(return_value=(None, active, "1.0.0")), + } + exec(compile(ast.Module(body=[cls], type_ignores=[]), "frontend/__init__.py", "exec"), namespace) + registration = namespace["JSModuleRegistration"](self.hass) + registration.async_remove_gzip_files = AsyncMock() + await registration._async_register_modules() + resources.async_update_item.assert_awaited_once_with( + "card", {"res_type": "module", "url": active} + ) + resources.async_create_item.assert_not_awaited() + + async def test_older_stable_release_is_not_installed_over_development_card(self): + self.version_reader.return_value = "3.0.0-dev.84" + self.coordinator.data["zigbee"] = {"release": self.updates._validate_release(self.coordinator.cards["zigbee"], self.metadata())} + await self.coordinator.install("zigbee") + self.session.get.assert_not_called() + self.assertFalse((self.directory / self.files.CARD_CACHE).exists()) diff --git a/tests/test_climate_attributes.py b/tests/test_climate_attributes.py new file mode 100644 index 00000000..edf2a636 --- /dev/null +++ b/tests/test_climate_attributes.py @@ -0,0 +1,62 @@ +"""Regression tests for room climate state attributes.""" + +import ast +from pathlib import Path +import unittest + + +COMPONENT_PATH = Path(__file__).parents[1] / "custom_components/wiser" + + +def attribute_assignments(nodes): + """Return the attrs keys assigned by the given nodes.""" + return [ + target.slice.value + for node in nodes + if isinstance(node, ast.Assign) + for target in node.targets + if isinstance(target, ast.Subscript) + and isinstance(target.value, ast.Name) + and target.value.id == "attrs" + and isinstance(target.slice, ast.Constant) + ] + + +class RoomClimateAttributesTest(unittest.TestCase): + """Ensure only OpenTherm-specific room attributes need OpenTherm.""" + + def setUp(self) -> None: + tree = ast.parse((COMPONENT_PATH / "climate.py").read_text()) + wiser_room = next( + node + for node in tree.body + if isinstance(node, ast.ClassDef) and node.name == "WiserRoom" + ) + self.method = next( + node + for node in wiser_room.body + if isinstance(node, ast.FunctionDef) + and node.name == "extra_state_attributes" + ) + self.opentherm_guard = next( + node + for node in self.method.body + if isinstance(node, ast.If) and "opentherm" in ast.unparse(node.test) + ) + + def test_percentage_demand_is_reported_without_opentherm(self) -> None: + self.assertIn("percentage_demand", attribute_assignments(self.method.body)) + self.assertNotIn( + "percentage_demand", + attribute_assignments(ast.walk(self.opentherm_guard)), + ) + + def test_demand_type_is_reported_only_with_opentherm(self) -> None: + guarded = set(ast.walk(self.opentherm_guard)) + unguarded = [node for node in ast.walk(self.method) if node not in guarded] + self.assertIn("demand_type", attribute_assignments(guarded)) + self.assertNotIn("demand_type", attribute_assignments(unguarded)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_device.py b/tests/test_device.py index b137c478..55ceba19 100644 --- a/tests/test_device.py +++ b/tests/test_device.py @@ -3,6 +3,7 @@ from __future__ import annotations from dataclasses import dataclass, field +from datetime import datetime, timezone from importlib.util import module_from_spec, spec_from_file_location from pathlib import Path import unittest @@ -23,6 +24,9 @@ class DeviceEntry: area_id: str | None = None labels: set = field(default_factory=set) name_by_user: str | None = None + created_at: datetime | None = None + identifiers: set = field(default_factory=set) + connections: set = field(default_factory=set) @dataclass @@ -31,6 +35,46 @@ class EntityEntry: entity_id: str device_id: str | None + domain: str = "sensor" + platform: str = "wiser" + unique_id: str = "" + created_at: datetime | None = None + translation_key: str | None = None + device_class: str | None = None + original_device_class: str | None = None + + +@dataclass +class AreaEntry: + """Minimal area-registry entry used by the tests.""" + + id: str + name: str = "" + + +class AreaRegistry: + """Minimal Home Assistant area registry.""" + + def __init__(self) -> None: + self.requested = [] + self.areas = {} + + def async_get_or_create(self, name): + self.requested.append(name) + area = self.async_get_area_by_name(name) + if area is None: + area = AreaEntry(name.lower().replace(" ", "_"), name) + self.areas[area.id] = area + return area + + def async_get_area_by_name(self, name): + return next( + (area for area in self.areas.values() if area.name == name), + None, + ) + + def async_get_area(self, area_id): + return self.areas.get(area_id) class EntityRegistry: @@ -39,10 +83,29 @@ class EntityRegistry: def __init__(self, entities=None) -> None: self.entities = entities or {} self.updated = [] + self.removed = [] + + def async_get_entity_id(self, domain, platform, unique_id): + return next( + ( + entry.entity_id + for entry in self.entities.values() + if entry.domain == domain + and entry.platform == platform + and entry.unique_id == unique_id + ), + None, + ) + + def async_get(self, entity_id): + return self.entities.get(entity_id) def async_update_entity(self, entity_id, **kwargs) -> None: self.updated.append((entity_id, kwargs)) + def async_remove(self, entity_id) -> None: + self.removed.append(entity_id) + class DeviceRegistry: """Minimal current Home Assistant device registry.""" @@ -228,8 +291,8 @@ def test_registers_device_with_area_and_physical_hub_parent(self) -> None: registry, "entry-id", ("wiser", "WiserHeat123456 Wiser Thermostat"), - "physical-hub-id", "Andys Bedroom", + {"via_device_id": "physical-hub-id"}, manufacturer="Drayton Wiser", name="Wiser Thermostat", model="RoomStat", @@ -238,13 +301,189 @@ def test_registers_device_with_area_and_physical_hub_parent(self) -> None: created = registry.created[0] self.assertEqual(created["suggested_area"], "Andys Bedroom") - self.assertEqual(created["via_device_id"], "physical-hub-id") + self.assertEqual( + created["via_device_id"], "physical-hub-id" + ) self.assertEqual( created["identifiers"], {("wiser", "WiserHeat123456 Wiser Thermostat")}, ) +class ConfirmedDeletedRoomIdsTest(unittest.TestCase): + """Test protection against transient room-list data gaps.""" + + def test_requires_two_consecutive_missing_observations(self) -> None: + missing_counts = {} + + first = DEVICE.confirmed_deleted_room_ids( + {"7", "8"}, {"8"}, missing_counts + ) + second = DEVICE.confirmed_deleted_room_ids( + {"7", "8"}, {"8"}, missing_counts + ) + + self.assertEqual(first, set()) + self.assertEqual(second, {"7"}) + + def test_empty_room_response_requires_three_observations(self) -> None: + missing_counts = {} + + first = DEVICE.confirmed_deleted_room_ids( + {"7"}, set(), missing_counts + ) + second = DEVICE.confirmed_deleted_room_ids( + {"7"}, set(), missing_counts + ) + third = DEVICE.confirmed_deleted_room_ids( + {"7"}, set(), missing_counts + ) + + self.assertEqual(first, set()) + self.assertEqual(second, set()) + self.assertEqual(third, {"7"}) + self.assertEqual(missing_counts, {"7": 3}) + + def test_rooms_reappearing_after_empty_response_clear_missing_counts(self): + missing_counts = {"7": 2} + + confirmed = DEVICE.confirmed_deleted_room_ids( + {"7"}, {"7"}, missing_counts + ) + + self.assertEqual(confirmed, set()) + self.assertEqual(missing_counts, {}) + + def test_room_reappearing_clears_its_missing_count(self) -> None: + missing_counts = {"7": 1} + + confirmed = DEVICE.confirmed_deleted_room_ids( + {"7"}, {"7"}, missing_counts + ) + + self.assertEqual(confirmed, set()) + self.assertEqual(missing_counts, {}) + + def test_pending_room_name_is_retained_until_confirmed(self) -> None: + previous = {"7": "Test", "8": "Kitchen"} + current = {"8": "Kitchen"} + + pending = DEVICE.room_names_with_pending_deletions( + previous, current, set() + ) + confirmed = DEVICE.room_names_with_pending_deletions( + previous, current, {"7"} + ) + + self.assertEqual(pending, previous) + self.assertEqual(confirmed, current) + + +class AssignDeviceAreaIfUnsetTest(unittest.TestCase): + """Test conservative assignment of Wiser devices to areas.""" + + def test_assigns_unassigned_device_to_wiser_room_area(self) -> None: + device_registry = DeviceRegistry() + area_registry = AreaRegistry() + + result = DEVICE.assign_device_area_if_unset( + device_registry, + area_registry, + DeviceEntry("room-device"), + "Andys Bedroom", + ) + + self.assertEqual(area_registry.requested, ["Andys Bedroom"]) + self.assertEqual( + device_registry.updated, + [("room-device", {"area_id": "andys_bedroom"})], + ) + self.assertEqual(result.id, "room-device") + + def test_preserves_existing_user_area(self) -> None: + device_registry = DeviceRegistry() + area_registry = AreaRegistry() + device = DeviceEntry("room-device", area_id="custom_area") + + result = DEVICE.assign_device_area_if_unset( + device_registry, area_registry, device, "Andys Bedroom" + ) + + self.assertIs(result, device) + self.assertEqual(area_registry.requested, []) + self.assertEqual(device_registry.updated, []) + + def test_moves_only_devices_still_in_previous_managed_area(self) -> None: + device_registry = DeviceRegistry() + area_registry = AreaRegistry() + area_registry.areas["test"] = AreaEntry("test", "Test") + devices = [ + DeviceEntry("room-device", area_id="test"), + DeviceEntry("itrv", area_id="test"), + DeviceEntry("manually-moved", area_id="upstairs"), + ] + + moved = DEVICE.move_devices_from_managed_area( + device_registry, + area_registry, + devices, + "Test", + "Test2", + ) + + self.assertEqual(moved, 2) + self.assertEqual( + device_registry.updated, + [ + ("room-device", {"area_id": "test2"}), + ("itrv", {"area_id": "test2"}), + ], + ) + + def test_does_not_create_area_when_no_device_qualifies(self) -> None: + device_registry = DeviceRegistry() + area_registry = AreaRegistry() + area_registry.areas["test"] = AreaEntry("test", "Test") + + moved = DEVICE.move_devices_from_managed_area( + device_registry, + area_registry, + [DeviceEntry("manually-moved", area_id="upstairs")], + "Test", + "Test2", + ) + + self.assertEqual(moved, 0) + self.assertEqual(area_registry.requested, []) + self.assertEqual(device_registry.updated, []) + + def test_recognises_area_matching_a_current_wiser_room(self) -> None: + area_registry = AreaRegistry() + area_registry.areas["test"] = AreaEntry("test", "Test") + + result = DEVICE.known_wiser_room_area_name( + area_registry, + "test", + {"Kitchen", "Test"}, + ) + + self.assertEqual(result, "Test") + + def test_rejects_user_selected_area_on_first_upgrade(self) -> None: + area_registry = AreaRegistry() + area_registry.areas["living_room"] = AreaEntry( + "living_room", "Living Room" + ) + + result = DEVICE.known_wiser_room_area_name( + area_registry, + "living_room", + {"Kitchen", "Lounge"}, + ) + + self.assertIsNone(result) + + class MigrateRoomDeviceTest(unittest.TestCase): """Test migration of logical Wiser room devices.""" @@ -252,6 +491,41 @@ class MigrateRoomDeviceTest(unittest.TestCase): legacy_identifier = ("wiser", "WiserHeat123456 Wiser Andys Bedroom") config_entry_id = "entry-id" + def test_creates_missing_room_device_before_entity_setup(self) -> None: + device_registry = DeviceRegistry() + area_registry = AreaRegistry() + + result = DEVICE.migrate_room_device( + device_registry, + EntityRegistry(), + self.config_entry_id, + self.identifier, + self.legacy_identifier, + "Wiser Room", + creation_parent_info={"via_device_id": "physical-hub-id"}, + ) + result = DEVICE.assign_device_area_if_unset( + device_registry, area_registry, result, "Test" + ) + + self.assertEqual(result.id, "new-hub") + self.assertEqual( + device_registry.created, + [ + { + "config_entry_id": self.config_entry_id, + "identifiers": {self.identifier}, + "name": "Wiser Room", + "via_device_id": "physical-hub-id", + } + ], + ) + self.assertEqual(area_registry.requested, ["Test"]) + self.assertEqual( + device_registry.updated, + [("new-hub", {"area_id": "test"})], + ) + def test_migrates_legacy_room_identifier_and_name(self) -> None: registry = DeviceRegistry( { @@ -311,3 +585,386 @@ def test_merges_duplicate_legacy_room_into_stable_room(self) -> None: [("sensor.temperature", {"device_id": "stable-room"})], ) self.assertEqual(registry.removed, ["legacy-room"]) + + def test_preserves_legacy_area_when_merging_duplicate_room(self) -> None: + registry = DeviceRegistry( + { + (self.identifier, self.config_entry_id): DeviceEntry("stable-room"), + (self.legacy_identifier, self.config_entry_id): DeviceEntry( + "legacy-room", area_id="custom_area" + ), + } + ) + + DEVICE.migrate_room_device( + registry, + EntityRegistry(), + self.config_entry_id, + self.identifier, + self.legacy_identifier, + "Wiser Room", + ) + + self.assertEqual(registry.updated[0][0], "stable-room") + self.assertEqual(registry.updated[0][1]["area_id"], "custom_area") + + +class RemoveRoomDevicesTest(unittest.TestCase): + """Test cleanup of rooms removed from the Wiser system.""" + + config_entry_id = "entry-id" + stable_identifier = ("wiser", "WiserHeat123456 room 7") + legacy_identifier = ("wiser", "WiserHeat123456 Wiser Test") + + def test_removes_room_device_and_its_entities(self) -> None: + room = DeviceEntry("room-device") + registry = DeviceRegistry( + { + (self.stable_identifier, self.config_entry_id): room, + (self.legacy_identifier, self.config_entry_id): room, + } + ) + entities = EntityRegistry( + { + "climate.test": EntityEntry("climate.test", "room-device"), + "sensor.other": EntityEntry("sensor.other", "other-device"), + } + ) + + removed = DEVICE.remove_room_devices( + registry, + entities, + self.config_entry_id, + (self.stable_identifier, self.legacy_identifier), + ) + + self.assertEqual(removed, 1) + self.assertEqual(entities.removed, ["climate.test"]) + self.assertEqual(registry.removed, ["room-device"]) + + def test_ignores_unknown_room_identifiers(self) -> None: + registry = DeviceRegistry() + entities = EntityRegistry() + + removed = DEVICE.remove_room_devices( + registry, + entities, + self.config_entry_id, + (self.stable_identifier,), + ) + + self.assertEqual(removed, 0) + self.assertEqual(entities.removed, []) + self.assertEqual(registry.removed, []) + + +class MigratePhysicalDeviceTest(unittest.TestCase): + """Test migration of room-derived physical device records.""" + + def test_finds_previously_renamed_non_itrv_by_stable_entity(self) -> None: + old_device = DeviceEntry( + "old-roomstat", + identifiers={ + ("wiser", "WiserHeat123456 Wiser RoomStat Old Room") + }, + ) + unrelated_device = DeviceEntry( + "unrelated", + identifiers={ + ("wiser", "WiserHeat123456 Wiser RoomStat Other Room") + }, + ) + entities = [ + EntityEntry( + "sensor.roomstat_signal", + "old-roomstat", + unique_id="stable-signal-id", + ), + EntityEntry( + "sensor.unrelated_signal", + "unrelated", + unique_id="other-signal-id", + ), + ] + + candidates = DEVICE.find_physical_device_candidates( + [old_device, unrelated_device], + entities, + { + ("wiser", "WiserHeat123456 device 21"), + ("wiser", "WiserHeat123456 Wiser RoomStat New Room"), + }, + {"stable-signal-id", "stable-battery-id"}, + ) + + self.assertEqual(candidates, [old_device]) + + def test_uses_identifier_parent_only_when_creating_device(self) -> None: + registry = DeviceRegistry() + + result = DEVICE.migrate_physical_device( + registry, + EntityRegistry(), + "entry-id", + ("wiser", "WiserHeat123456 device 31"), + [], + "Wiser iTRV", + creation_parent_info={"via_device_id": "physical-hub-id"}, + via_device_id="physical-hub-id", + model="iTRV", + ) + + self.assertEqual(result.id, "new-hub") + self.assertEqual( + registry.created, + [ + { + "config_entry_id": "entry-id", + "identifiers": { + ("wiser", "WiserHeat123456 device 31") + }, + "name": "Wiser iTRV", + "via_device_id": "physical-hub-id", + "model": "iTRV", + } + ], + ) + + def test_keeps_oldest_device_and_merges_renamed_duplicate(self) -> None: + old = DeviceEntry( + "old-device", + created_at=datetime(2026, 1, 1, tzinfo=timezone.utc), + identifiers={ + ("wiser", "WiserHeat123456 Wiser iTRV Bedroom"), + ("matter", "matter-device-31"), + }, + connections={("mac", "aa:bb:cc:dd:ee:ff")}, + ) + renamed = DeviceEntry( + "renamed-device", + created_at=datetime(2026, 10, 1, tzinfo=timezone.utc), + identifiers={ + ("wiser", "WiserHeat123456 Wiser iTRV Lounge"), + ("zigbee", "zigbee-device-31"), + }, + connections={("zigbee", "00:11:22:33:44:55")}, + ) + registry = DeviceRegistry() + entities = EntityRegistry( + { + "sensor.old_temperature": EntityEntry( + "sensor.old_temperature", "old-device" + ), + "sensor.signal": EntityEntry("sensor.signal", "renamed-device"), + } + ) + + result = DEVICE.migrate_physical_device( + registry, + entities, + "entry-id", + ("wiser", "WiserHeat123456 device 31"), + [renamed, old], + "Wiser iTRV", + creation_parent_info={"via_device_id": "physical-hub-id"}, + via_device_id="physical-hub-id", + model="iTRV", + ) + + self.assertEqual(result.id, "old-device") + self.assertEqual(registry.updated[0][0], "old-device") + self.assertEqual( + registry.updated[0][1]["new_identifiers"], + { + ("wiser", "WiserHeat123456 device 31"), + ("matter", "matter-device-31"), + ("zigbee", "zigbee-device-31"), + }, + ) + self.assertEqual( + registry.updated[0][1]["via_device_id"], "physical-hub-id" + ) + self.assertEqual( + registry.updated[0][1]["new_connections"], + { + ("mac", "aa:bb:cc:dd:ee:ff"), + ("zigbee", "00:11:22:33:44:55"), + }, + ) + self.assertNotIn("via_device", registry.updated[0][1]) + self.assertEqual( + entities.updated, + [("sensor.signal", {"device_id": "old-device"})], + ) + self.assertEqual(registry.removed, ["renamed-device"]) + + def test_keeps_oldest_temperature_entity_and_retains_duplicate(self) -> None: + old = EntityEntry( + "sensor.room_temperature", + "old-device", + unique_id="old-room-id", + created_at=datetime(2026, 1, 1, tzinfo=timezone.utc), + ) + renamed = EntityEntry( + "sensor.test_temperature", + "renamed-device", + unique_id="renamed-room-id", + created_at=datetime(2026, 10, 1, tzinfo=timezone.utc), + ) + entities = EntityRegistry( + {old.entity_id: old, renamed.entity_id: renamed} + ) + + result = DEVICE.migrate_entity_unique_id_duplicates( + entities, + [renamed, old], + "stable-id", + ) + + self.assertEqual(result.entity_id, "sensor.room_temperature") + self.assertEqual( + entities.updated, + [("sensor.room_temperature", {"new_unique_id": "stable-id"})], + ) + self.assertEqual(entities.removed, []) + + def test_keeps_existing_stable_target_and_retains_older_duplicate(self) -> None: + old = EntityEntry( + "sensor.room_temperature", + "old-device", + unique_id="old-room-id", + created_at=datetime(2026, 1, 1, tzinfo=timezone.utc), + ) + target = EntityEntry( + "sensor.test_temperature", + "renamed-device", + unique_id="stable-id", + created_at=datetime(2026, 10, 1, tzinfo=timezone.utc), + ) + entities = EntityRegistry( + {old.entity_id: old, target.entity_id: target} + ) + + result = DEVICE.migrate_entity_unique_id_duplicates( + entities, + [old], + "stable-id", + ) + + self.assertIs(result, target) + self.assertEqual(entities.updated, []) + self.assertEqual(entities.removed, []) + + def test_migrates_all_room_name_derived_entity_types(self) -> None: + entities = {} + definitions = ( + ("climate", None, None, "climate"), + ( + "binary_sensor", + "window_detection_active", + None, + "binary_sensor_window_detection_active", + ), + ("switch", "window_detection", None, "switch_window_detection"), + ("sensor", "heating_demand", None, "heating_demand"), + ("sensor", "target_temperature", "temperature", "current_target_temp"), + ("sensor", None, "temperature", "current_temp"), + ) + for domain, translation_key, device_class, expected_type in definitions: + for suffix, created_at in ( + ("old", datetime(2026, 1, 1, tzinfo=timezone.utc)), + ("new", datetime(2026, 10, 1, tzinfo=timezone.utc)), + ): + entity_id = f"{domain}.{expected_type}_{suffix}" + entities[entity_id] = EntityEntry( + entity_id, + "room-device", + domain=domain, + unique_id=f"{expected_type}-{suffix}", + created_at=created_at, + translation_key=translation_key, + device_class=device_class, + ) + registry = EntityRegistry(entities) + + DEVICE.migrate_room_entities( + registry, + "room-device", + lambda entity_type: f"stable-{entity_type}", + ) + + self.assertEqual(len(registry.updated), len(definitions)) + self.assertEqual(registry.removed, []) + self.assertEqual( + {update[1]["new_unique_id"] for update in registry.updated}, + {f"stable-{definition[3]}" for definition in definitions}, + ) + + def test_migrates_room_entities_created_before_translation_keys(self) -> None: + definitions = ( + ( + "binary_sensor.window_detection_active", + "binary_sensor", + "legacy-room-window-detection-active", + "binary_sensor_window_detection_active", + ), + ( + "switch.window_detection", + "switch", + "WiserHeat-room-switch-Wiser Lounge Window Detection", + "switch_window_detection", + ), + ( + "switch.summer_comfort", + "switch", + "WiserHeat-room-switch-Wiser Lounge Include In Summer Comfort", + "switch_include_in_summer_comfort", + ), + ( + "switch.passive_mode", + "switch", + "WiserHeat-passive-mode-switch-Wiser Lounge Passive Mode-1", + "switch_passive_mode", + ), + ( + "sensor.temperature", + "sensor", + "WiserHeat-sensor-current_temp-1", + "current_temp", + ), + ( + "sensor.target_temperature", + "sensor", + "WiserHeat-sensor-current_target_temp-1", + "current_target_temp", + ), + ( + "sensor.heating_demand", + "sensor", + "WiserHeat-sensor-room-1", + "heating_demand", + ), + ) + registry = EntityRegistry( + { + entity_id: EntityEntry( + entity_id, + "room-device", + domain=domain, + unique_id=unique_id, + device_class="temperature" if domain == "sensor" else None, + ) + for entity_id, domain, unique_id, _entity_type in definitions + } + ) + + DEVICE.migrate_room_entities( + registry, + "room-device", + lambda entity_type: f"stable-{entity_type}", + ) + + self.assertEqual( + {update[1]["new_unique_id"] for update in registry.updated}, + {f"stable-{definition[3]}" for definition in definitions}, + ) diff --git a/tests/test_entity_naming.py b/tests/test_entity_naming.py index 4d27a533..08e225ad 100644 --- a/tests/test_entity_naming.py +++ b/tests/test_entity_naming.py @@ -18,6 +18,7 @@ "climate.py": {"WiserTempProbe", "WiserRoom", "WiserHotWater"}, "cover.py": {"WiserShutter"}, "light.py": {"WiserLight"}, + "opentherm_hot_water.py": {"WiserOpenThermHotWater"}, "number.py": { "WiserAwayModeTempNumber", "WiserFloorTempSensorNumber", @@ -43,6 +44,7 @@ "WiserDiscomfortIndoorTempNumber", "WiserDiscomfortOutdoorTempNumber", }, + "opentherm_hot_water.py": {"WiserOpenThermHotWater"}, "select.py": {"WiserSelectEntity"}, "sensor.py": {"WiserSensor"}, "switch.py": {"WiserSwitch"}, @@ -181,7 +183,13 @@ def test_unique_ids_do_not_depend_on_new_entity_names(self) -> None: ) def test_all_entity_unique_ids_are_uuid_backed(self) -> None: - uuid_helpers = {"get_unique_id", "get_uuid_unique_id"} + uuid_helpers = { + "get_light_binary_sensor_unique_id", + "get_physical_entity_unique_id", + "get_room_entity_unique_id", + "get_unique_id", + "get_uuid_unique_id", + } for filename in ENTITY_BASE_CLASSES: tree = ast.parse((COMPONENT_PATH / filename).read_text()) for node in ast.walk(tree): @@ -379,9 +387,7 @@ def test_custom_integration_translations_use_resolved_text(self) -> None: self.assertEqual( translations["entity"][domain][key]["name"], expected_name ) - for filename in ( - "translations/en.json", - "translations/de.json", - "translations/fr.json", + for translation in sorted( + (COMPONENT_PATH / "translations").glob("*.json") ): - self.assertNotIn("[%key:", (COMPONENT_PATH / filename).read_text()) + self.assertNotIn("[%key:", translation.read_text()) diff --git a/tests/test_frontend_version.py b/tests/test_frontend_version.py new file mode 100644 index 00000000..cacc0c6a --- /dev/null +++ b/tests/test_frontend_version.py @@ -0,0 +1,57 @@ +"""Test the shared version contract for any frontend bundle.""" + +import ast +from hashlib import sha256 +from pathlib import Path +import re +from tempfile import TemporaryDirectory +import unittest + +SOURCE = Path(__file__).resolve().parents[1] / "custom_components/wiser/frontend/__init__.py" +reader = next( + node for node in ast.parse(SOURCE.read_text()).body + if isinstance(node, ast.FunctionDef) and node.name == "card_version" +) +namespace = {"Path": Path, "sha256": sha256, "re": re} +exec(compile(ast.Module(body=[reader], type_ignores=[]), str(SOURCE), "exec"), namespace) +card_version = namespace["card_version"] + + +class CardVersionTest(unittest.TestCase): + def version(self, source, filename="wiser-example-card.js"): + with TemporaryDirectory() as directory: + path = Path(directory) / filename + path.write_text(source) + return card_version(path) + + def test_marker_supports_any_bundle_and_semantic_version(self): + for name in ("wiser-schedule-card", "wiser-zigbee-card", "wiser-future-card"): + for version in ("1.2.3", "2.0.0-beta.1-dev.2", "3.0.0+build.42"): + with self.subTest(name=name, version=version): + source = f'/*! WISER-CARD-VERSION {name} {version} */\nconst library="9.9.9";' + self.assertEqual(self.version(source, f"{name}.js"), version) + + def test_other_bundle_marker_is_ignored(self): + self.assertTrue( + self.version('/*! WISER-CARD-VERSION wiser-other-card 4.0.0 */').startswith("sha256-") + ) + + def test_unmarked_content_never_guesses_a_version_from_javascript(self): + sources = ( + 'const library="3.3.3";const $t="1.5.6";console.info(`WISER-SCHEDULE-CARD ${Vt("common.version")} ${$t}`)', + 'const cardBuild="3.0.0-dev.7";console.info(`WISER-ZIGBEE-CARD ${translate("common.version")} ${cardBuild}`)', + 'html`
${this.t("common.version")}: ${"3.0.0"}
`', + ) + for source in sources: + with self.subTest(source=source): + expected = f"sha256-{sha256(source.encode()).hexdigest()[:16]}" + self.assertEqual(self.version(source), expected) + + def test_content_hash_is_stable_and_changes_with_content(self): + first = self.version('const library="3.3.3"') + self.assertEqual(first, self.version('const library="3.3.3"')) + self.assertNotEqual(first, self.version('const library="4.0.0"')) + + def test_missing_file_returns_missing(self): + with TemporaryDirectory() as directory: + self.assertEqual(card_version(Path(directory) / "missing.js"), "missing") diff --git a/tests/test_hub_naming.py b/tests/test_hub_naming.py index 3a1f2e16..b9961f8e 100644 --- a/tests/test_hub_naming.py +++ b/tests/test_hub_naming.py @@ -24,6 +24,10 @@ def _load_helpers_module() -> ModuleType: sys.modules["aioWiserHeatAPI.wiserhub"] = wiserhub sys.modules["homeassistant"] = ModuleType("homeassistant") + const = ModuleType("homeassistant.const") + const.MAJOR_VERSION = 2025 + const.MINOR_VERSION = 5 + sys.modules["homeassistant.const"] = const core = ModuleType("homeassistant.core") core.HomeAssistant = object sys.modules["homeassistant.core"] = core @@ -89,6 +93,8 @@ def setUp(self) -> None: self.data = SimpleNamespace( wiserhub=SimpleNamespace(system=system, rooms=rooms, devices=devices) ) + self.helpers.MAJOR_VERSION = 2025 + self.helpers.MINOR_VERSION = 5 def test_single_hub_has_concise_device_name(self) -> None: self.assertEqual(self.helpers.get_hub_device_name(self.data), "Wiser HeatHub") @@ -105,6 +111,47 @@ def test_hub_entity_object_id_uses_mac_suffix(self) -> None: "058a52_away_mode", ) + def test_hub_entity_object_id_does_not_repeat_device_mac_suffix(self) -> None: + self.assertEqual( + self.helpers.get_hub_entity_object_id( + self.data, "Away Mode", "Wiser HeatHub (058A52)" + ), + "away_mode", + ) + + def test_renamed_hub_entity_object_id_keeps_mac_suffix(self) -> None: + self.assertEqual( + self.helpers.get_hub_entity_object_id( + self.data, "Away Mode", "Test" + ), + "058a52_away_mode", + ) + + def test_active_rooms_exclude_nothing_assigned_records(self) -> None: + active = SimpleNamespace(_data={}, id=1) + deleted = SimpleNamespace( + _data={"Invalid": "NothingAssigned"}, id=2 + ) + self.data.wiserhub.rooms.all = [active, deleted] + + self.assertEqual(self.helpers.active_wiser_rooms(self.data), [active]) + + def test_hub_parent_uses_compatible_identifier(self) -> None: + self.assertEqual( + self.helpers.get_hub_via_device_info(self.data), + {"via_device": ("wiser", "WiserHeatNOTUSED")}, + ) + + def test_hub_parent_uses_device_id_on_current_home_assistant(self) -> None: + self.helpers.MAJOR_VERSION = 2026 + self.helpers.MINOR_VERSION = 8 + self.data.hub_device_id = "physical-hub-id" + + self.assertEqual( + self.helpers.get_hub_via_device_info(self.data), + {"via_device_id": "physical-hub-id"}, + ) + def test_hub_entity_mixin_suggests_mac_derived_object_id(self) -> None: class DefaultEntity: @property @@ -130,6 +177,15 @@ def device_info(entity_self): "058a52_away_mode", ) + renamed_entity = HubEntity(self.data) + renamed_entity.device_entry = SimpleNamespace( + name="Wiser HeatHub (058A52)", name_by_user="Test" + ) + self.assertEqual( + renamed_entity.suggested_object_id, + "058a52_away_mode", + ) + def test_hub_entity_object_id_does_not_repeat_device_name(self) -> None: object_id = self.helpers.get_hub_entity_object_id(self.data, "Away Mode") self.assertNotIn("wiser_heathub", object_id) @@ -191,9 +247,55 @@ def test_roomstat_name_uses_area_for_room_context(self) -> None: ) self.assertEqual( self.helpers.get_identifier(self.data, 21), + "WiserHeatNOTUSED device 21", + ) + self.assertEqual( + self.helpers.get_legacy_device_identifier(self.data, 21), "WiserHeatNOTUSED Wiser RoomStat Andys Bedroom", ) + def test_legacy_naming_restores_room_context_without_changing_identity(self): + for device_type, device_id, expected in ( + ("room", 7, "Wiser Andys Bedroom"), + ("device", 21, "Wiser Thermostat Andys Bedroom"), + ): + self.data.legacy_naming = False + identifier = self.helpers.get_identifier(self.data, device_id, device_type) + unique_id = self.helpers.get_unique_id(self.data, "sensor", "Temperature", device_id) + self.data.legacy_naming = True + self.assertEqual( + self.helpers.get_device_name(self.data, device_id, device_type), expected + ) + self.assertEqual( + self.helpers.get_identifier(self.data, device_id, device_type), identifier + ) + self.assertEqual( + self.helpers.get_unique_id(self.data, "sensor", "Temperature", device_id), unique_id + ) + + def test_legacy_temperature_sensor_name_and_unassigned_fallback(self): + self.data.wiserhub.devices.get_by_id = lambda device_id: SimpleNamespace( + id=device_id, name="Sensor 31", product_type="TemperatureHumiditySensor" + ) + self.data.legacy_naming = False + identifier = self.helpers.get_identifier(self.data, 31) + self.data.legacy_naming = True + self.assertEqual( + self.helpers.get_device_name(self.data, 31), + "Wiser Temperature/Humidity Sensor Andys Bedroom", + ) + self.assertEqual(self.helpers.get_identifier(self.data, 31), identifier) + self.data.wiserhub.rooms.get_by_device_id = lambda _id: None + self.assertEqual( + self.helpers.get_device_name(self.data, 31), + "Wiser Temperature/Humidity Sensor Sensor 31", + ) + self.data.legacy_naming = False + self.assertEqual( + self.helpers.get_device_name(self.data, 31), + "Wiser Temperature/Humidity Sensor", + ) + def test_physical_device_suggests_its_wiser_room_as_area(self) -> None: self.assertEqual( self.helpers.get_device_area_info(self.data, 21), @@ -217,6 +319,10 @@ def test_temperature_sensor_has_concise_name_and_stable_identifier(self) -> None ) self.assertEqual( self.helpers.get_identifier(self.data, 31), + "WiserHeatNOTUSED device 31", + ) + self.assertEqual( + self.helpers.get_legacy_device_identifier(self.data, 31), "WiserHeatNOTUSED Wiser TemperatureHumiditySensor " "Andys Bedroom Kitchen Temperature Sensor", ) @@ -226,6 +332,343 @@ def test_temperature_sensor_has_concise_name_and_stable_identifier(self) -> None "Andys Bedroom Kitchen Temperature Sensor", ) + def test_itrv_name_respects_legacy_option_without_changing_identity(self): + self.data.wiserhub.devices.get_by_id = lambda device_id: SimpleNamespace( + id=device_id, + product_type="iTRV", + ) + self.data.wiserhub.rooms.get_by_device_id = lambda _device_id: SimpleNamespace( + id=7, + name="Andys Bedroom", + number_of_smartvalves=1, + smartvalve_ids=[31], + ) + + self.data.legacy_naming = False + self.assertEqual(self.helpers.get_device_name(self.data, 31), "Wiser iTRV") + self.assertEqual( + self.helpers.get_legacy_device_name(self.data, 31), + "Wiser iTRV Andys Bedroom", + ) + identifier = self.helpers.get_identifier(self.data, 31) + self.assertEqual(identifier, "WiserHeatNOTUSED device 31") + + self.data.legacy_naming = True + self.assertEqual( + self.helpers.get_device_name(self.data, 31), + "Wiser iTRV Andys Bedroom", + ) + self.assertEqual(self.helpers.get_identifier(self.data, 31), identifier) + + def test_room_assigned_device_names_respect_legacy_option(self): + room = SimpleNamespace( + id=7, + name="Andys Bedroom", + number_of_heating_actuators=1, + heating_actuator_ids=[31], + ) + devices = { + 31: SimpleNamespace( + id=31, + name="Actuator", + product_type="HeatingActuator", + room_id=7, + ), + 32: SimpleNamespace( + id=32, + name="Blind", + product_type="Shutter", + room_id=7, + ), + 33: SimpleNamespace( + id=33, + name="Smoke Alarm", + product_type="SmokeAlarmDevice", + room_id=7, + ), + } + self.data.wiserhub.devices.get_by_id = lambda device_id: devices[device_id] + self.data.wiserhub.rooms.get_by_id = lambda _room_id: room + self.data.wiserhub.rooms.get_by_device_id = lambda _device_id: room + + self.data.legacy_naming = False + self.assertEqual( + self.helpers.get_device_name(self.data, 31), + "Wiser HeatingActuator", + ) + self.assertEqual( + self.helpers.get_device_name(self.data, 32), + "Wiser Shutter Blind", + ) + self.assertEqual( + self.helpers.get_device_name(self.data, 33), + "Wiser Smoke Alarm 33", + ) + + self.data.legacy_naming = True + self.assertEqual( + self.helpers.get_device_name(self.data, 31), + "Wiser HeatingActuator Andys Bedroom", + ) + self.assertEqual( + self.helpers.get_device_name(self.data, 32), + "Wiser Shutter Andys Bedroom Blind", + ) + self.assertEqual( + self.helpers.get_device_name(self.data, 33), + "Wiser Andys Bedroom Smoke Alarm", + ) + + def test_historical_device_names_ignore_legacy_option(self): + room = SimpleNamespace( + id=7, + name="Andys Bedroom", + number_of_heating_actuators=1, + heating_actuator_ids=[31], + ) + device = SimpleNamespace( + id=31, + name="Actuator", + product_type="HeatingActuator", + room_id=7, + ) + self.data.wiserhub.devices.get_by_id = lambda _device_id: device + self.data.wiserhub.rooms.get_by_device_id = lambda _device_id: room + + self.data.legacy_naming = False + modern_name = self.helpers.get_device_name(self.data, 31) + historical_name = self.helpers.get_legacy_device_name(self.data, 31) + + self.assertEqual(modern_name, "Wiser HeatingActuator") + self.assertEqual( + historical_name, + "Wiser HeatingActuator Andys Bedroom", + ) + self.assertEqual( + self.helpers.get_legacy_device_identifier(self.data, 31), + "WiserHeatNOTUSED Wiser HeatingActuator Andys Bedroom", + ) + + def test_physical_entity_migration_uses_historical_itrv_name(self): + device = SimpleNamespace(id=31, product_type="iTRV") + room = SimpleNamespace( + id=7, + name="Andys Bedroom", + number_of_smartvalves=1, + smartvalve_ids=[31], + ) + self.data.legacy_naming = False + self.data.wiserhub.devices = SimpleNamespace( + all=[device], + get_by_id=lambda _device_id: device, + ) + self.data.wiserhub.rooms.get_by_device_id = lambda _device_id: room + + mapping = self.helpers.build_physical_entity_unique_id_migration( + self.data + ) + old_unique_id = self.helpers.get_unique_id( + self.data, + "binary_sensor", + "Controllable", + "Wiser iTRV Andys Bedroom Controllable", + ) + + self.assertEqual( + mapping[old_unique_id], + self.helpers.get_physical_entity_unique_id( + self.data, "binary_sensor", 31, "Controllable" + ), + ) + + def test_physical_entity_unique_id_does_not_use_room_name(self): + before = self.helpers.get_physical_entity_unique_id( + self.data, "sensor", 31, "smartvalve_temp" + ) + self.data.wiserhub.rooms.get_by_device_id = lambda _device_id: SimpleNamespace( + id=7, + name="Renamed Room", + ) + self.assertEqual( + self.helpers.get_physical_entity_unique_id( + self.data, "sensor", 31, "smartvalve_temp" + ), + before, + ) + + def test_all_physical_device_identifiers_use_immutable_device_id(self): + self.data.wiserhub.devices.get_by_id = lambda _device_id: ( + _ for _ in () + ).throw( + AssertionError( + "physical identifiers must not inspect mutable device data" + ) + ) + + self.assertEqual( + self.helpers.get_identifier(self.data, 42), + "WiserHeatNOTUSED device 42", + ) + + def test_physical_entity_migration_replaces_name_based_ids(self): + device = SimpleNamespace( + id=42, + name="Bedroom Blind", + product_type="Shutter", + ) + self.data.wiserhub.devices = SimpleNamespace( + all=[device], + get_by_id=lambda _device_id: device, + ) + + mapping = self.helpers.build_physical_entity_unique_id_migration( + self.data + ) + old_cover_id = self.helpers.get_uuid_unique_id( + "WiserHeatNOTUSED-Wisershutter-42-" + "Wiser Shutter Andys Bedroom Bedroom Blind Control" + ) + self.assertEqual( + mapping[old_cover_id], + self.helpers.get_physical_entity_unique_id( + self.data, "cover", 42, "control" + ), + ) + + def test_physical_entity_migration_uses_registry_device_names(self): + device = SimpleNamespace( + id=42, + name="Bedroom Blind", + product_type="Shutter", + ) + self.data.wiserhub.devices = SimpleNamespace( + all=[device], + get_by_id=lambda _device_id: device, + ) + + mapping = self.helpers.build_physical_entity_unique_id_migration( + self.data, + legacy_device_names_by_id={ + 42: {"Wiser Shutter Previous Bedroom Bedroom Blind"} + }, + device_ids={42}, + ) + old_binary_id = self.helpers.get_unique_id( + self.data, + "binary_sensor", + "Is Open", + "Wiser Shutter Previous Bedroom Bedroom Blind Is Open", + ) + old_cover_id = self.helpers.get_uuid_unique_id( + "WiserHeatNOTUSED-Wisershutter-42-" + "Wiser Shutter Previous Bedroom Bedroom Blind Control" + ) + + self.assertEqual( + mapping[old_binary_id], + self.helpers.get_physical_entity_unique_id( + self.data, "binary_sensor", 42, "Is Open" + ), + ) + self.assertEqual( + mapping[old_cover_id], + self.helpers.get_physical_entity_unique_id( + self.data, "cover", 42, "control" + ), + ) + + def test_physical_sensor_migration_covers_humidity_power_and_energy(self): + room = SimpleNamespace( + id=7, + name="Andys Bedroom", + number_of_heating_actuators=1, + heating_actuator_ids=[42], + ) + devices = [ + SimpleNamespace( + id=41, + name="Roomstat", + product_type="RoomStat", + ), + SimpleNamespace( + id=42, + name="Actuator", + product_type="HeatingActuator", + ), + ] + self.data.wiserhub.devices = SimpleNamespace( + all=devices, + get_by_id=lambda device_id: next( + device for device in devices if device.id == device_id + ), + ) + self.data.wiserhub.rooms.get_by_device_id = lambda _device_id: room + + mapping = self.helpers.build_physical_entity_unique_id_migration( + self.data, + {"7": "Previous Bedroom"}, + ) + + expected = { + self.helpers.get_unique_id( + self.data, + "sensor", + "LTS Humidity Andys Bedroom", + 41, + ): self.helpers.get_physical_entity_unique_id( + self.data, "sensor", 41, "humidity" + ), + self.helpers.get_unique_id( + self.data, + "sensor", + "LTS Power Andys Bedroom", + 42, + ): self.helpers.get_physical_entity_unique_id( + self.data, "sensor", 42, "power" + ), + self.helpers.get_unique_id( + self.data, + "sensor", + "LTS Energy Andys Bedroom", + 42, + ): self.helpers.get_physical_entity_unique_id( + self.data, "sensor", 42, "energy" + ), + self.helpers.get_unique_id( + self.data, + "sensor", + "LTS Humidity Previous Bedroom", + 41, + ): self.helpers.get_physical_entity_unique_id( + self.data, "sensor", 41, "humidity" + ), + self.helpers.get_unique_id( + self.data, + "sensor", + "LTS Power Previous Bedroom", + 42, + ): self.helpers.get_physical_entity_unique_id( + self.data, "sensor", 42, "power" + ), + } + for old_unique_id, new_unique_id in expected.items(): + self.assertEqual(mapping[old_unique_id], new_unique_id) + + def test_room_entity_unique_id_does_not_use_room_name(self): + before = self.helpers.get_room_entity_unique_id( + self.data, 7, "current_temp" + ) + self.data.wiserhub.rooms.get_by_id = lambda room_id: SimpleNamespace( + id=room_id, + name="Renamed Room", + ) + self.assertEqual( + self.helpers.get_room_entity_unique_id( + self.data, 7, "current_temp" + ), + before, + ) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_light_optimistic_state.py b/tests/test_light_optimistic_state.py index 8bb5cb96..03f11c8d 100644 --- a/tests/test_light_optimistic_state.py +++ b/tests/test_light_optimistic_state.py @@ -49,6 +49,9 @@ class CoordinatorEntity: def __init__(self, *args: object) -> None: pass + async def async_will_remove_from_hass(self) -> None: + self.removed = True + _module( "homeassistant.helpers.update_coordinator", CoordinatorEntity=CoordinatorEntity ) @@ -71,6 +74,7 @@ def hub_error_handler(func): get_device_name=lambda *_a, **_k: "Wiser Light", get_hub_via_device_info=lambda _data: {}, get_identifier=lambda *_a, **_k: "identifier", + get_light_entity_name=lambda _data, _light, entity_name: entity_name, get_unique_id=lambda *_a, **_k: "unique-id", hub_error_handler=hub_error_handler, ) @@ -137,30 +141,92 @@ def _light(self, *, is_on=False): async def _noop_async(self, *_args, **_kwargs): return None - def test_single_command_clears_optimistic(self): + def test_single_command_returns_before_confirmation(self): light, device = self._light(is_on=False) - light.async_force_update = self._noop_async + scheduled = [] + light._schedule_command_confirmation = scheduled.append + asyncio.run(light.async_turn_on()) - # The one and only command clears its own optimistic state on cleanup. + + self.assertIs(light._optimistic_is_on, True) + self.assertTrue(light.is_on) + self.assertEqual(scheduled, [1]) + + def test_superseded_confirmation_does_not_clobber_newer(self): + light, device = self._light(is_on=False) + light._optimistic_gen = 2 + light._optimistic_is_on = False + light.async_force_update = self._noop_async + + asyncio.run(light._async_confirm_command(1)) + + self.assertIs(light._optimistic_is_on, False) + + def test_latest_confirmation_clears_optimistic_state(self): + light, device = self._light(is_on=False) + light._optimistic_gen = 1 + light._optimistic_is_on = True + refreshes = [] + + async def stale_refresh(*_args, **_kwargs): + refreshes.append(True) + + light.async_force_update = stale_refresh + + asyncio.run(light._async_confirm_command(1)) + + self.assertEqual(len(refreshes), 2) + self.assertIsNone(light._optimistic_is_on) + + def test_failed_confirmation_clears_optimistic_state(self): + light, _device = self._light(is_on=False) + light._optimistic_gen = 1 + light._optimistic_is_on = True + + async def fail_refresh(*_args, **_kwargs): + raise RuntimeError("refresh failed") + + light.async_force_update = fail_refresh + + with self.assertRaisesRegex(RuntimeError, "refresh failed"): + asyncio.run(light._async_confirm_command(1)) + self.assertIsNone(light._optimistic_is_on) - self.assertTrue(light.is_on) # falls through to the (now on) device - def test_superseded_command_does_not_clobber_newer(self): + def test_stale_first_refresh_retries_until_hub_confirms(self): light, device = self._light(is_on=False) + light._optimistic_gen = 1 + light._optimistic_is_on = True + refreshes = [] - # Command A is turn_on. While A awaits its follow-up refresh, a newer - # command B (turn_off) lands: it bumps the generation and sets its own - # optimistic value. A's cleanup must then leave B's value alone. - async def force_update_with_intervening_command(delay=0): - light._optimistic_gen += 1 - light._optimistic_is_on = False # command B = turn off + async def refresh(*_args, **_kwargs): + refreshes.append(True) + if len(refreshes) == 2: + device.is_on = True + light._handle_coordinator_update() - light.async_force_update = force_update_with_intervening_command - asyncio.run(light.async_turn_on()) # command A + light.async_force_update = refresh - # Without the generation guard, A's finally would wipe this back to None - # and the UI would revert to the stale device state. - self.assertIs(light._optimistic_is_on, False) + asyncio.run(light._async_confirm_command(1)) + + self.assertEqual(len(refreshes), 2) + self.assertIsNone(light._optimistic_is_on) + self.assertTrue(light.is_on) + + def test_failed_command_restores_hub_state(self): + light, device = self._light(is_on=False) + light._schedule_command_confirmation = lambda _gen: None + + async def fail(): + raise RuntimeError("command failed") + + device.turn_on = fail + + with self.assertRaisesRegex(RuntimeError, "command failed"): + asyncio.run(light.async_turn_on()) + + self.assertIsNone(light._optimistic_is_on) + self.assertFalse(light.is_on) def test_coordinator_update_clears_optimistic_once_confirmed(self): light, device = self._light(is_on=False) @@ -178,6 +244,63 @@ def test_coordinator_update_clears_optimistic_once_confirmed(self): self.assertIsNone(light._optimistic_is_on) self.assertTrue(light.is_on) + def test_brightness_optimistic_state_waits_for_matching_hub_value(self): + light, device = self._light(is_on=True) + light._optimistic_percentage = 60 + + device.current_percentage = 20 + light._handle_coordinator_update() + self.assertEqual(light._optimistic_percentage, 60) + + device.current_percentage = 60 + light._handle_coordinator_update() + self.assertIsNone(light._optimistic_percentage) + + def test_new_confirmation_cancels_superseded_task(self): + light, _device = self._light(is_on=False) + tasks = [] + + class FakeTask: + cancelled = False + + def cancel(self): + self.cancelled = True + + def create_task(coro, _name): + coro.close() + task = FakeTask() + tasks.append(task) + return task + + light.hass = SimpleNamespace(async_create_task=create_task) + light.entity_id = "light.test" + + light._schedule_command_confirmation(1) + light._schedule_command_confirmation(2) + + self.assertTrue(tasks[0].cancelled) + self.assertFalse(tasks[1].cancelled) + + def test_removal_invalidates_and_cancels_confirmation(self): + light, _device = self._light(is_on=False) + + class FakeTask: + cancelled = False + + def cancel(self): + self.cancelled = True + + task = FakeTask() + light._confirmation_task = task + previous_gen = light._optimistic_gen + + asyncio.run(light.async_will_remove_from_hass()) + + self.assertTrue(task.cancelled) + self.assertEqual(light._optimistic_gen, previous_gen + 1) + self.assertIsNone(light._confirmation_task) + self.assertTrue(light.removed) + if __name__ == "__main__": unittest.main() diff --git a/tests/test_light_unique_id_migration.py b/tests/test_light_unique_id_migration.py index 554cd1f1..be85c3e0 100644 --- a/tests/test_light_unique_id_migration.py +++ b/tests/test_light_unique_id_migration.py @@ -42,6 +42,7 @@ def _load_helpers_module() -> ModuleType: WiserHubRESTError=type("WiserHubRESTError", (Exception,), {}), ) _module("homeassistant") + _module("homeassistant.const", MAJOR_VERSION=2026, MINOR_VERSION=10) _module("homeassistant.core", HomeAssistant=object) package = _module("wiser") @@ -70,15 +71,28 @@ def _hub_data(): _light(device_id=101, light_id=1, product_type="DimmableLight", name="Diele"), _light(device_id=102, light_id=2, product_type="OnOffLight", name="Flur"), # Two-gang dimmer: both channels share physical device id 200. - _light(device_id=200, light_id=3, product_type="DimmableLight", name="Bad Dusche"), - _light(device_id=200, light_id=4, product_type="DimmableLight", name="Bad Wanne"), + _light( + device_id=200, + light_id=3, + product_type="DimmableLight", + name="DualRelay Lightsource1", + ), + _light( + device_id=200, + light_id=4, + product_type="DimmableLight", + name="DualRelay Lightsource2", + ), ] - # get_device_name resolves the physical device; only single-gang ids should - # ever be looked up (multi-gang is skipped before this is called). Raising on - # an unexpected id turns an accidental multi-gang resolution into a failure. + # get_device_name resolves the physical device during migration, while the + # light naming helper also uses it to remove the shared device portion from + # a multi-gang channel name. devices_by_id = { 101: SimpleNamespace(id=101, product_type="DimmableLight", name="Diele"), 102: SimpleNamespace(id=102, product_type="OnOffLight", name="Flur"), + 200: SimpleNamespace( + id=200, product_type="DimmableLight", name="DualRelay" + ), } rooms_by_device = { 101: SimpleNamespace(name="Diele"), @@ -136,28 +150,168 @@ def test_away_switch_maps_name_and_id(self) -> None: "Wiser DimmableLight Diele Diele Away Mode Turns Off", 101, ) - new = self._unique_id( - "DimmableLight", "Wiser Diele Away Mode Turns Off", 1 - ) + new = self._unique_id("switch", "light_away_mode_turns_off", 1) self.assertEqual(mapping[old], new) + def test_light_and_smart_plug_away_switch_ids_cannot_collide(self) -> None: + light_id = self._unique_id( + "switch", "light_away_mode_turns_off", 2 + ) + smart_plug_id = self._unique_id( + "switch", "away_mode_turns_off", 2 + ) + + self.assertNotEqual(light_id, smart_plug_id) + def test_capability_binary_sensors_map_name(self) -> None: mapping = self._mapping() + data = _hub_data() + light = data.wiserhub.devices.lights.all[0] old = self._unique_id( "binary_sensor", "Is Dimmable", "Wiser DimmableLight Diele Diele Is Dimmable", ) - new = self._unique_id( - "binary_sensor", "Is Dimmable", "Wiser Diele Is Dimmable" - ) + new = self._unique_id("binary_sensor", "Is Dimmable", 1) self.assertEqual(mapping[old], new) + self.assertEqual( + self.helpers.get_light_binary_sensor_unique_id( + data, light, "Is Dimmable" + ), + new, + ) def test_no_room_light_uses_type_and_name(self) -> None: mapping = self._mapping() old = self._unique_id("device", "light", "Wiser OnOffLight Flur Light") self.assertEqual(mapping[old], self._unique_id("device", "light", 2)) + def test_single_light_entity_name_omits_physical_device_name(self) -> None: + data = _hub_data() + light = data.wiserhub.devices.lights.all[0] + + self.assertEqual( + self.helpers.get_light_entity_name( + data, light, "Away Mode Turns Off" + ), + "Away Mode Turns Off", + ) + + def test_multi_gang_entity_name_keeps_only_channel_name(self) -> None: + data = _hub_data() + light = data.wiserhub.devices.lights.all[2] + + self.assertEqual( + self.helpers.get_light_entity_name( + data, light, "Away Mode Turns Off" + ), + "Lightsource1 Away Mode Turns Off", + ) + + def test_prefixed_device_name_is_removed_from_channel_name(self) -> None: + light = _light( + device_id=300, + light_id=5, + product_type="DimmableLight", + name="Dobbeltrelæ Bordlampe", + ) + physical_device = SimpleNamespace( + id=300, + product_type="DimmableLight", + name="Wiser Dobbeltrelæ", + ) + data = SimpleNamespace( + legacy_naming=False, + wiserhub=SimpleNamespace( + devices=SimpleNamespace( + get_by_id=lambda _device_id: physical_device + ) + ), + ) + + self.assertEqual( + self.helpers.get_light_entity_name(data, light, "Is Dimmable"), + "Bordlampe Is Dimmable", + ) + + def test_single_dimmer_omits_its_only_light_source_name(self) -> None: + light = _light( + device_id=400, + light_id=6, + product_type="DimmableLight", + name="Dimmer Loftlampe", + ) + light._data = {"Name": "Dimmer"} + light.node_id = 50 + data = SimpleNamespace( + legacy_naming=False, + wiserhub=SimpleNamespace( + devices=SimpleNamespace( + all=[light], + lights=SimpleNamespace(all=[light]), + get_by_id=lambda _device_id: light, + ) + ), + ) + + self.assertEqual( + self.helpers.get_light_entity_name(data, light, "Is Dimmable"), + "Is Dimmable", + ) + + def test_dual_relay_uses_embedded_name_for_synthetic_channel_ids( + self, + ) -> None: + light = _light( + device_id=300001, + light_id=5, + product_type="DimmableLight", + name="Dobbeltrelæ Bordlampe", + ) + light._data = {"Name": "Dobbeltrelæ"} + light.node_id = 42 + other_light = SimpleNamespace( + id=300002, + product_type="DimmableLight", + name="Dobbeltrelæ Loftlampe", + node_id=42, + ) + button_panel = SimpleNamespace( + id=300, + product_type="DimmableLight", + name="Dobbeltrelæ", + node_id=21, + number_of_gangs=2, + events=[], + ) + data = SimpleNamespace( + legacy_naming=False, + wiserhub=SimpleNamespace( + devices=SimpleNamespace( + all=[other_light, button_panel], + lights=SimpleNamespace(all=[light, other_light]), + get_by_id=lambda _device_id: other_light, + ) + ), + ) + + self.assertEqual( + self.helpers.get_light_entity_name(data, light, "Is Dimmable"), + "Bordlampe Is Dimmable", + ) + + def test_legacy_naming_retains_full_light_name(self) -> None: + data = _hub_data() + data.legacy_naming = True + light = data.wiserhub.devices.lights.all[0] + + self.assertEqual( + self.helpers.get_light_entity_name( + data, light, "Away Mode Turns Off" + ), + "Wiser Diele Away Mode Turns Off", + ) + def test_multi_gang_channels_are_skipped(self) -> None: mapping = self._mapping() # No mapping may reference the shared physical device id 200 ... @@ -173,8 +327,8 @@ def test_no_noop_entries_and_exact_count(self) -> None: mapping = self._mapping() # Every entry is a real rename. self.assertTrue(all(old != new for old, new in mapping.items())) - # 2 single-gang lights x (1 light + 3 selects + 1 away + 4 binary) = 18. - self.assertEqual(len(mapping), 18) + # 2 lights x (1 light + 3 selects + 2 away aliases + 4 binary) = 20. + self.assertEqual(len(mapping), 20) if __name__ == "__main__": diff --git a/tests/test_opentherm.py b/tests/test_opentherm.py index 18e22d82..4a6d3576 100644 --- a/tests/test_opentherm.py +++ b/tests/test_opentherm.py @@ -697,7 +697,12 @@ async def test_initial_mismatch_is_retried_when_no_update_arrives(self): transport = self.first.wiserhub.system.opentherm._wiser_rest_controller._do_hub_action self.assertEqual(transport.await_count, 2) self.assertEqual(self.first.async_refresh.await_count, 2) - self.hass.services.async_call.assert_not_awaited() + self.hass.services.async_call.assert_awaited_once_with( + "persistent_notification", + "dismiss", + {"notification_id": "wiser_opentherm_hub_dhwFlowSetpoint"}, + blocking=False, + ) self.hass.bus.async_fire.assert_not_called() async def test_matching_feedback_is_not_retried(self): diff --git a/tests/test_opentherm_hot_water.py b/tests/test_opentherm_hot_water.py new file mode 100644 index 00000000..147a1a2f --- /dev/null +++ b/tests/test_opentherm_hot_water.py @@ -0,0 +1,322 @@ +"""Focused tests for the integration-owned OpenTherm hot-water schedule.""" + +import ast +from copy import deepcopy +from datetime import datetime, timedelta +from pathlib import Path +from time import strptime +import unittest + + +COMPONENT = Path(__file__).parents[1] / "custom_components/wiser" +WEEKDAYS = ( + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", +) + + +def _load_schedule_helpers(): + """Load pure schedule helpers without requiring Home Assistant.""" + tree = ast.parse((COMPONENT / "local_schedule.py").read_text()) + selected = [ + node + for node in tree.body + if isinstance(node, ast.FunctionDef) + and node.name + in { + "_schedule_preset_options", + "_default_schedule", + "_validated_schedule", + "_schedule_position", + } + ] + namespace = { + "deepcopy": deepcopy, + "timedelta": timedelta, + "strptime": strptime, + "LOCAL_HOT_WATER_SCHEDULE_ID": -1000, + "LOCAL_HOT_WATER_SCHEDULE_NAME": "OpenTherm Hot Water", + "LOCAL_HOT_WATER_SCHEDULE_TYPE": "HotWater", + "WEEKDAYS": WEEKDAYS, + } + exec( + compile(ast.Module(body=selected, type_ignores=[]), "", "exec"), + namespace, + ) + return namespace + + +HELPERS = _load_schedule_helpers() +default_schedule = HELPERS["_default_schedule"] +validated_schedule = HELPERS["_validated_schedule"] +schedule_position = HELPERS["_schedule_position"] + + +class OpenThermHotWaterScheduleTest(unittest.TestCase): + def test_control_uses_water_heater_operations(self): + source = (COMPONENT / "opentherm_hot_water.py").read_text() + self.assertIn( + "CoordinatorEntity, RestoreEntity, WaterHeaterEntity", source + ) + self.assertIn("STATE_OFF,", source) + self.assertIn("MODE_MANUAL,", source) + self.assertIn("MODE_AUTO,", source) + + def test_card_feature_uses_hot_water_mode_icons(self): + source = (COMPONENT / "frontend/wiser-hot-water-control.js").read_text() + self.assertIn('off: "mdi:power"', source) + self.assertIn('Manual: "mdi:water-thermometer"', source) + self.assertIn('Auto: "mdi:calendar-clock"', source) + self.assertIn('operation_mode: value', source) + self.assertIn( + '"opentherm-hot-water-control-presets-feature"', source + ) + self.assertIn('name: "Hot-water control modes"', source) + self.assertIn('name: "Hot-water control presets"', source) + + def test_control_exposes_configured_presets(self): + source = (COMPONENT / "opentherm_hot_water.py").read_text() + self.assertIn('"opentherm_presets": self._presets', source) + self.assertIn('*(preset["name"] for preset in self._presets)', source) + self.assertIn('self._start_boost(preset["duration_minutes"])', source) + + def test_external_temperature_sensor_updates_and_falls_back_to_opentherm(self): + source = (COMPONENT / "opentherm_hot_water.py").read_text() + self.assertIn("CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID", source) + self.assertIn("self.hass.states.get(", source) + self.assertIn("TemperatureConverter.convert(", source) + self.assertIn("async_track_state_change_event(", source) + self.assertIn("self._async_temperature_sensor_changed", source) + self.assertIn( + 'getattr(operational_data, "hw_temperature", None)', source + ) + + def test_preset_editor_opens_directly_from_nested_options_dialog(self): + source = (COMPONENT / "frontend/wiser-hot-water-control.js").read_text() + self.assertIn('PRESETS_SELECTOR_KEY = "opentherm_presets_v2"', source) + self.assertIn("document.body.querySelector(PRESETS_DIALOG_TYPE)", source) + self.assertIn("document.body.append(dialog)", source) + self.assertIn("dialog.showDialog(dialogParams)", source) + + def test_restart_of_timed_boost_reapplies_manual_target(self): + source = (COMPONENT / "opentherm_hot_water.py").read_text() + self.assertIn("reset_expired_boost = bool", source) + self.assertIn( + "self.hass.async_create_task(self._async_finish_boost_safely())", + source, + ) + + def test_confirmed_command_failure_restores_previous_ui_operation(self): + source = (COMPONENT / "opentherm_hot_water.py").read_text() + self.assertIn("ATTR_OPENTHERM_REQUEST_ID: request_id", source) + self.assertIn("EVENT_OPENTHERM_COMMAND_FAILED", source) + self.assertIn("self._operation = previous_operation", source) + self.assertIn("self._cancel_active_boost()", source) + self.assertIn("self._pending_previous_boost_seconds", source) + self.assertIn("self._start_boost(previous_boost_seconds / 60)", source) + self.assertIn("self._operation = previous_operation", source) + self.assertIn("(MODE_AUTO, failed_temperature)", source) + self.assertIn("except HomeAssistantError:", source) + + def test_preset_editor_uses_boiler_temperature_limits(self): + source = (COMPONENT / "frontend/wiser-hot-water-control.js").read_text() + self.assertIn("min: this._minimum", source) + self.assertIn("max: this._maximum", source) + self.assertNotIn("temperature between 35 and 55", source) + + def test_presets_are_list_only(self): + source = (COMPONENT / "frontend/wiser-hot-water-control.js").read_text() + self.assertNotIn("display_style", source) + self.assertNotIn('displayStyle.label = "Show as"', source) + self.assertIn( + "return PRIMARY_MODES.filter((mode) => entityModes.includes(mode))", + source, + ) + + def test_default_schedule_starts_safe_and_covers_every_day(self): + schedule = default_schedule(35) + self.assertEqual(schedule["Id"], -1000) + self.assertEqual(schedule["Type"], "HotWater") + self.assertEqual(schedule["MinSetpoint"], 35) + self.assertEqual(schedule["MaxSetpoint"], 55) + self.assertEqual( + [day["day"] for day in schedule["ScheduleData"]], list(WEEKDAYS) + ) + self.assertTrue( + all( + day["slots"] + == [ + {"Time": "00:00", "Setpoint": "35"}, + {"Time": "06:00", "Setpoint": "45"}, + {"Time": "09:00", "Setpoint": "35"}, + {"Time": "17:00", "Setpoint": "45"}, + {"Time": "22:00", "Setpoint": "35"}, + ] + for day in schedule["ScheduleData"] + ) + ) + + def test_schedule_uses_latest_slot_and_reports_next_transition(self): + schedule = default_schedule(35) + monday = schedule["ScheduleData"][0] + monday["slots"] = [ + {"Time": "06:00", "Setpoint": "50"}, + {"Time": "08:00", "Setpoint": "35"}, + ] + value, next_event = schedule_position( + schedule, datetime(2026, 10, 5, 7, 0) + ) + self.assertEqual(value, 50) + self.assertEqual(next_event, datetime(2026, 10, 5, 8, 0)) + + def test_schedule_wraps_to_the_previous_and_next_week(self): + schedule = default_schedule(35) + for day in schedule["ScheduleData"]: + day["slots"] = [] + schedule["ScheduleData"][6]["slots"] = [ + {"Time": "22:00", "Setpoint": "48"} + ] + value, next_event = schedule_position( + schedule, datetime(2026, 10, 5, 5, 0) + ) + self.assertEqual(value, 48) + self.assertEqual(next_event, datetime(2026, 10, 11, 22, 0)) + + def test_single_slot_at_current_time_advances_one_week(self): + schedule = default_schedule(35) + for day in schedule["ScheduleData"]: + day["slots"] = [] + schedule["ScheduleData"][0]["slots"] = [ + {"Time": "06:00", "Setpoint": "50"} + ] + value, next_event = schedule_position( + schedule, datetime(2026, 10, 5, 6, 0) + ) + self.assertEqual(value, 50) + self.assertEqual(next_event, datetime(2026, 10, 12, 6, 0)) + + def test_schedule_changes_at_exact_sunday_minute(self): + schedule = default_schedule(35) + sunday = schedule["ScheduleData"][6] + sunday["slots"] = [ + {"Time": "14:00", "Setpoint": "49"}, + {"Time": "19:15", "Setpoint": "45"}, + ] + + value, next_event = schedule_position( + schedule, datetime(2026, 10, 11, 19, 15, 32) + ) + + self.assertEqual(value, 45) + self.assertEqual(next_event, datetime(2026, 10, 12, 0, 0)) + + def test_schedule_has_timer_and_coordinator_recovery_paths(self): + source = (COMPONENT / "opentherm_hot_water.py").read_text() + self.assertIn("async_track_point_in_time(", source) + self.assertIn("next_event + timedelta(seconds=1)", source) + self.assertIn("def _handle_coordinator_update", source) + self.assertIn( + "(MODE_AUTO, temperature) != self._last_schedule_request", + source, + ) + + def test_temperature_change_in_auto_is_a_temporary_override(self): + source = (COMPONENT / "opentherm_hot_water.py").read_text() + self.assertIn("auto_override = self._operation == MODE_AUTO", source) + self.assertIn("previous_schedule_request =", source) + self.assertIn("self._target_write_in_progress = True", source) + self.assertIn( + "self._operation == MODE_AUTO and not self._target_write_in_progress", + source, + ) + self.assertIn("if auto_override:", source) + self.assertIn("self._scheduled_temperature()", source) + self.assertIn("self._operation = MODE_MANUAL", source) + + def test_validation_normalizes_and_rejects_unsafe_setpoints(self): + schedule = default_schedule(35) + schedule["ScheduleData"][0]["slots"] = [ + {"Time": "08:30", "Setpoint": "45.5"}, + {"Time": "06:00", "Setpoint": 50}, + ] + normalized = validated_schedule(schedule, 35, 55) + self.assertEqual( + [slot["Time"] for slot in normalized["ScheduleData"][0]["slots"]], + ["06:00", "08:30"], + ) + + schedule["ScheduleData"][0]["slots"][0]["Setpoint"] = 60 + with self.assertRaises(ValueError): + validated_schedule(schedule, 35, 55) + + def test_schedule_card_off_value_uses_safe_low_setpoint(self): + schedule = default_schedule(40) + schedule["ScheduleData"][0]["slots"][0]["Setpoint"] = "Off" + normalized = validated_schedule(schedule, 40, 55) + self.assertEqual( + normalized["ScheduleData"][0]["slots"][0]["Setpoint"], "40" + ) + + def test_non_timed_presets_can_be_scheduled_and_resolve_to_temperature(self): + presets = [ + { + "name": "Hand wash", + "icon": "mdi:hand-wash", + "temperature": 42, + "duration_minutes": 0, + }, + { + "name": "Boost 30 Mins", + "icon": "mdi:numeric-1", + "temperature": 50, + "duration_minutes": 30, + }, + ] + schedule = default_schedule(35, 55, 45, presets) + self.assertEqual( + schedule["PresetOptions"], + [ + { + "name": "Hand wash", + "icon": "mdi:hand-wash", + "temperature": 42.0, + } + ], + ) + schedule["ScheduleData"][0]["slots"] = [ + {"Time": "06:00", "Setpoint": "hand WASH"} + ] + normalized = validated_schedule(schedule, 35, 55, presets) + self.assertEqual( + normalized["ScheduleData"][0]["slots"][0]["Setpoint"], + "Hand wash", + ) + value, _ = schedule_position( + normalized, datetime(2026, 10, 5, 7, 0) + ) + self.assertEqual(value, 42) + + schedule["ScheduleData"][0]["slots"][0]["Setpoint"] = ( + "Boost 30 Mins" + ) + with self.assertRaisesRegex(ValueError, "Unknown hot-water preset"): + validated_schedule(schedule, 35, 55, presets) + + def test_removed_preset_falls_back_to_safe_minimum_when_loading(self): + schedule = default_schedule(35) + schedule["ScheduleData"][0]["slots"][0]["Setpoint"] = "Deleted" + normalized = validated_schedule( + schedule, 35, 55, [], replace_unknown_presets=True + ) + self.assertEqual( + normalized["ScheduleData"][0]["slots"][0]["Setpoint"], "35" + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_room_migration_orchestration.py b/tests/test_room_migration_orchestration.py new file mode 100644 index 00000000..d665efb4 --- /dev/null +++ b/tests/test_room_migration_orchestration.py @@ -0,0 +1,441 @@ +"""Tests for Wiser room-migration orchestration.""" + +from __future__ import annotations + +import importlib.util +from pathlib import Path +import sys +from types import ModuleType, SimpleNamespace +import unittest +from unittest.mock import Mock + + +COMPONENT_PATH = Path(__file__).parents[1] / "custom_components/wiser" + + +def _module(name: str, **attributes: object) -> ModuleType: + module = ModuleType(name) + module.__dict__.update(attributes) + sys.modules[name] = module + return module + + +def _load_wiser_init() -> ModuleType: + """Load the integration module with lightweight dependency stubs.""" + _module("homeassistant") + _module("homeassistant.config_entries", ConfigEntry=object) + _module("homeassistant.const", CONF_NAME="name") + _module( + "homeassistant.core", + HomeAssistant=object, + callback=lambda function: function, + ) + _module("homeassistant.exceptions", ConfigEntryNotReady=RuntimeError) + + area_registry = _module("homeassistant.helpers.area_registry") + device_registry = _module( + "homeassistant.helpers.device_registry", + CONNECTION_NETWORK_MAC="mac", + ) + entity_registry = _module("homeassistant.helpers.entity_registry") + _module( + "homeassistant.helpers", + area_registry=area_registry, + device_registry=device_registry, + entity_registry=entity_registry, + ) + _module("homeassistant.helpers.storage", Store=object) + + package = _module("wiser_init_test") + package.__path__ = [] + constants = { + name: name.lower() + for name in ( + "CONF_AUTOMATIONS_HW_AUTO_MODE", + "CONF_AUTOMATIONS_HW_CLIMATE", + "CONF_AUTOMATIONS_HW_HEAT_MODE", + "CONF_AUTOMATIONS_HW_SENSOR_ENTITY_ID", + "CONF_AUTOMATIONS_PASSIVE", + "CONF_AUTOMATIONS_PASSIVE_TEMP_INCREMENT", + "CONF_DEPRECATED_HW_TARGET_TEMP", + "CONF_LEGACY_NAMING", + "CONF_OPENTHERM_EVER_CONNECTED", + "CONF_OPENTHERM_HOT_WATER_CAPABLE", + "CONF_OPENTHERM_HOT_WATER_CONTROL", + "CONF_OPENTHERM_HOT_WATER_MANUAL_TEMP", + "CONF_OPENTHERM_HOT_WATER_PRESETS", + "CONF_WISER_ROOM_NAMES", + "DATA", + "OPENTHERM_HOT_WATER_SCHEDULE", + "UPDATE_LISTENER", + ) + } + constants.update( + DOMAIN="wiser", + ENTITY_PREFIX="Wiser", + MANUFACTURER="Drayton Wiser", + DEFAULT_OPENTHERM_HOT_WATER_MANUAL_TEMP=45.0, + WISER_PLATFORMS=[], + WISER_SERVICES={}, + HWCycleModes=SimpleNamespace(CONTINUOUS="continuous", ONCE="once"), + ) + _module("wiser_init_test.const", **constants) + _module("wiser_init_test.coordinator", WiserUpdateCoordinator=object) + + device_helpers = { + name: Mock(name=name) + for name in ( + "assign_device_area_if_unset", + "confirmed_deleted_room_ids", + "find_physical_device_candidates", + "known_wiser_room_area_name", + "merge_legacy_hub_device", + "migrate_entity_unique_id_duplicates", + "migrate_physical_device", + "migrate_room_entities", + "migrate_room_device", + "move_devices_from_managed_area", + "register_hub_device", + "register_room_assigned_device", + "remove_room_devices", + "room_names_with_pending_deletions", + ) + } + _module("wiser_init_test.device", **device_helpers) + _module( + "wiser_init_test.entity_migration", + migrate_entity_unique_ids=Mock(), + ) + _module("wiser_init_test.frontend", JSModuleRegistration=object) + _module( + "wiser_init_test.frontend.entry_updates", + async_handle_entry_update=Mock(), + integration_reload_settings=Mock(return_value={}), + ) + _module( + "wiser_init_test.frontend.wiser_sidebar", + async_update_wiser_panel=Mock(), + ) + helper_names = ( + "active_wiser_rooms", + "build_light_unique_id_migration", + "build_physical_entity_unique_id_migration", + "get_device_name", + "get_hub_device_name", + "get_hub_via_device_info", + "get_identifier", + "get_instance_count", + "get_legacy_device_identifier", + "get_legacy_room_identifier", + "get_physical_entity_unique_id", + "get_room_entity_unique_id", + "get_unique_id", + ) + helper_stubs = {name: Mock(name=name) for name in helper_names} + helper_stubs["active_wiser_rooms"] = ( + lambda data: data.wiserhub.rooms.all + ) + helper_stubs["get_hub_via_device_info"] = Mock( + return_value={"via_device_id": "hub-device"} + ) + _module("wiser_init_test.helpers", **helper_stubs) + _module( + "wiser_init_test.opentherm_detection", + opentherm_entity_unique_ids=Mock(return_value=set()), + opentherm_hot_water_control_is_supported=Mock(return_value=False), + opentherm_hot_water_limits=Mock(return_value=(35.0, 55.0)), + opentherm_is_detected=Mock(return_value=False), + ) + _module( + "wiser_init_test.local_schedule", + OpenThermHotWaterSchedule=Mock(), + ) + _module("wiser_init_test.services", async_setup_services=Mock()) + _module("wiser_init_test.update", async_unload_card_updates=Mock()) + _module("wiser_init_test.websockets", async_register_websockets=Mock()) + + spec = importlib.util.spec_from_file_location( + "wiser_init_test.__init__", COMPONENT_PATH / "__init__.py" + ) + assert spec and spec.loader + module = importlib.util.module_from_spec(spec) + sys.modules[spec.name] = module + spec.loader.exec_module(module) + return module + + +class FakeStore: + """Record delayed Home Assistant storage writes.""" + + def __init__(self) -> None: + self.saved = [] + + def async_delay_save(self, data, delay) -> None: + self.saved.append((data(), delay)) + + +class RoomMigrationOrchestrationTest(unittest.TestCase): + """Test composition of the room-migration helpers.""" + + @classmethod + def setUpClass(cls) -> None: + cls.wiser = _load_wiser_init() + + def setUp(self) -> None: + self.entry = SimpleNamespace(entry_id="entry-id", options={}) + self.room = SimpleNamespace(id=7, name="Lounge") + self.coordinator = SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace(name="WiserHeat123456"), + rooms=SimpleNamespace(all=[self.room]), + devices=SimpleNamespace(all=[]), + ) + ) + self.store = FakeStore() + self.hass = SimpleNamespace( + data={ + "wiser": { + "entry-id": { + "data": self.coordinator, + "conf_wiser_room_names": {"7": "Living Room"}, + "room_names_store": self.store, + "missing_room_counts": {}, + } + } + }, + config_entries=SimpleNamespace( + async_update_entry=Mock( + side_effect=AssertionError("must not update config entry") + ) + ), + ) + + def test_invalid_storage_falls_back_to_config_entry_room_names(self) -> None: + previous, runtime = self.wiser._initial_room_names( + ["invalid-storage"], + {"7": "Living Room"}, + ) + + self.assertEqual(previous, {"7": "Living Room"}) + self.assertEqual(runtime, {"7": "Living Room"}) + + def test_invalid_storage_without_fallback_starts_uninitialised(self) -> None: + previous, runtime = self.wiser._initial_room_names( + ["invalid-storage"], + None, + ) + + self.assertEqual(previous, {}) + self.assertIsNone(runtime) + + def test_current_room_names_use_only_active_rooms(self) -> None: + self.wiser.active_wiser_rooms = Mock(return_value=[self.room]) + + self.assertEqual( + self.wiser._current_wiser_room_names(self.coordinator), + {"7": "Lounge"}, + ) + + def test_refresh_moves_areas_and_saves_names_without_config_reload(self) -> None: + self.wiser._confirmed_deleted_room_ids = Mock(return_value=set()) + self.wiser.remove_deleted_room_devices = Mock() + self.wiser.sync_wiser_room_areas = Mock() + self.wiser.room_names_with_pending_deletions = Mock( + return_value={"7": "Lounge"} + ) + + self.wiser._sync_wiser_room_names( + self.hass, self.entry, self.coordinator + ) + + self.wiser.remove_deleted_room_devices.assert_called_once_with( + self.hass, + self.entry, + {"7": "Living Room"}, + set(), + ) + self.wiser.sync_wiser_room_areas.assert_called_once_with( + self.hass, + self.entry, + {"7": "Living Room"}, + ) + self.assertEqual(self.store.saved, [({"7": "Lounge"}, 1)]) + self.assertEqual( + self.hass.data["wiser"]["entry-id"]["conf_wiser_room_names"], + {"7": "Lounge"}, + ) + + def test_confirmed_room_removal_passes_only_deleted_room_identifiers(self) -> None: + self.wiser.get_identifier = Mock( + return_value="WiserHeat123456 room 7" + ) + self.wiser.remove_room_devices = Mock(return_value=1) + self.wiser.dr.async_get = Mock(return_value="device-registry") + self.wiser.er.async_get = Mock(return_value="entity-registry") + + removed = self.wiser.remove_deleted_room_devices( + self.hass, + self.entry, + {"7": "Living Room", "8": "Kitchen"}, + {"7"}, + ) + + self.assertEqual(removed, 1) + self.wiser.remove_room_devices.assert_called_once_with( + "device-registry", + "entity-registry", + "entry-id", + [ + ("wiser", "WiserHeat123456 room 7"), + ("wiser", "WiserHeat123456 Wiser Living Room"), + ], + ) + + def test_physical_migration_composes_power_and_energy_migrations(self) -> None: + device = SimpleNamespace( + id=42, + product_type="HeatingActuator", + firmware_version="1.0", + floor_temperature_sensor=None, + ) + physical_device = SimpleNamespace( + id="physical-device", + identifiers={ + ( + "wiser", + "WiserHeat123456 Wiser HeatingActuator Living Room", + ) + }, + ) + power = SimpleNamespace( + entity_id="sensor.power", + unique_id="old-power", + device_id="physical-device", + domain="sensor", + platform="wiser", + ) + energy = SimpleNamespace( + entity_id="sensor.energy", + unique_id="old-energy", + device_id="physical-device", + domain="sensor", + platform="wiser", + ) + device_registry = SimpleNamespace() + entity_registry = SimpleNamespace(entities={}) + self.coordinator.wiserhub.devices.all = [device] + self.coordinator.wiserhub.rooms.get_by_device_id = Mock( + return_value=self.room + ) + self.wiser.dr.async_get = Mock(return_value=device_registry) + self.wiser.dr.async_entries_for_config_entry = Mock( + return_value=[physical_device] + ) + self.wiser.er.async_get = Mock(return_value=entity_registry) + self.wiser.er.async_entries_for_config_entry = Mock( + return_value=[power, energy] + ) + self.wiser.ar.async_get = Mock(return_value="area-registry") + self.wiser.get_identifier = Mock(return_value="stable-device") + self.wiser.get_legacy_device_identifier = Mock( + return_value="legacy-device" + ) + self.wiser.get_unique_id = Mock( + side_effect=lambda _data, _domain, entity_type, _device_id: ( + f"base-{entity_type}" + ) + ) + self.wiser.get_physical_entity_unique_id = Mock( + side_effect=lambda _data, _domain, device_id, entity_type: ( + f"stable-{entity_type}-{device_id}" + ) + ) + self.wiser.build_physical_entity_unique_id_migration = Mock( + return_value={ + "old-power": "stable-power-42", + "old-energy": "stable-energy-42", + } + ) + self.wiser.find_physical_device_candidates = Mock( + return_value=[physical_device] + ) + self.wiser.migrate_entity_unique_id_duplicates = Mock() + self.wiser.migrate_physical_device = Mock( + return_value=physical_device + ) + self.wiser.get_device_name = Mock(return_value="Wiser HeatingActuator") + self.wiser.assign_device_area_if_unset = Mock() + + self.wiser.migrate_physical_device_registry( + self.hass, + self.entry, + "hub-device", + {"7": "Living Room"}, + ) + + migrations = { + call.args[2]: {entry.entity_id for entry in call.args[1]} + for call in self.wiser.migrate_entity_unique_id_duplicates.call_args_list + } + self.assertEqual( + migrations, + { + "stable-power-42": {"sensor.power"}, + "stable-energy-42": {"sensor.energy"}, + }, + ) + self.assertTrue( + any( + call.args[2] + == { + 42: { + "Wiser HeatingActuator Living Room", + } + } + and call.args[3] == {42} + for call in self.wiser.build_physical_entity_unique_id_migration.call_args_list + if len(call.args) == 4 + ) + ) + self.wiser.migrate_physical_device.assert_called_once() + + def test_area_sync_composes_room_and_physical_devices(self) -> None: + room_device = SimpleNamespace(id="room-device", area_id="old-area") + physical_device = SimpleNamespace(id="physical-device", area_id="old-area") + physical = SimpleNamespace(id=31) + self.coordinator.wiserhub.devices.all = [physical] + self.coordinator.wiserhub.rooms.get_by_device_id = Mock( + return_value=self.room + ) + self.wiser.dr.async_get = Mock(return_value="device-registry") + self.wiser.ar.async_get = Mock(return_value="area-registry") + self.wiser.get_identifier = Mock( + side_effect=lambda _data, item_id, device_type="device": ( + f"room-{item_id}" + if device_type == "room" + else f"device-{item_id}" + ) + ) + self.wiser._device_by_identifier = Mock( + side_effect=[room_device, physical_device] + ) + self.wiser.move_devices_from_managed_area = Mock(return_value=2) + + moved = self.wiser.sync_wiser_room_areas( + self.hass, + self.entry, + {"7": "Living Room"}, + ) + + self.assertEqual(moved, 2) + self.wiser.move_devices_from_managed_area.assert_called_once_with( + "device-registry", + "area-registry", + [room_device, physical_device], + "Living Room", + "Lounge", + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_schedule_card_hide_hw_schedule.py b/tests/test_schedule_card_hide_hw_schedule.py index 83763565..d5bd4679 100644 --- a/tests/test_schedule_card_hide_hw_schedule.py +++ b/tests/test_schedule_card_hide_hw_schedule.py @@ -1,90 +1,12 @@ +"""Integration contracts for schedule operations used by the external card.""" from pathlib import Path import unittest +ROOT = Path(__file__).resolve().parents[1] / "custom_components/wiser" +WEBSOCKETS = ROOT / "websockets.py" -import re -CARD = ( - Path(__file__).resolve().parents[1] - / "custom_components" - / "wiser" - / "frontend" - / "wiser-schedule-card.js" -) -CONST = Path(__file__).resolve().parents[1] / "custom_components" / "wiser" / "const.py" -WEBSOCKETS = ( - Path(__file__).resolve().parents[1] - / "custom_components" - / "wiser" - / "websockets.py" -) - - -class ScheduleCardHideHotWaterScheduleTest(unittest.TestCase): - def test_schedule_card_filters_hot_water_schedule_when_configured(self): - card_source = CARD.read_text() - - self.assertTrue( - "hide_hw_schedule" in card_source, - "schedule card should read the hide_hw_schedule config option", - ) - self.assertTrue( - "filter(e=>1e3!=e.Id)" in card_source, - "schedule card should remove the hot water schedule when configured", - ) - - def test_schedule_card_editor_exposes_hide_hot_water_schedule_option(self): - card_source = CARD.read_text() - - self.assertTrue( - "get _hide_hw_schedule()" in card_source, - "card editor should expose the hide_hw_schedule getter", - ) - self.assertTrue( - "Hide Hot Water Schedule" in card_source, - "card editor should show the hot water schedule toggle", - ) - self.assertTrue( - 'configValue=${"hide_hw_schedule"}' in card_source, - "card editor should write the hide_hw_schedule config value", - ) - - def test_registered_schedule_card_resource_version_matches_card_version(self): - card_source = CARD.read_text() - const_source = CONST.read_text() - - self.assertTrue( - 'const $t="1.5.6"' in card_source, - "schedule card should advertise version 1.5.6", - ) - self.assertTrue( - '"version": "1.5.6"' in const_source, - "registered Lovelace module version should match the card version", - ) - - def test_delete_confirmation_uses_current_home_assistant_dialog_footer(self): - card_source = CARD.read_text() - - self.assertTrue( - 'header-title=${Vt("wiser.headings.delete_schedule")}' in card_source, - "delete dialog should use the current Home Assistant title attribute", - ) - self.assertTrue( - '' in card_source, - "delete dialog should put its controls in the dialog footer", - ) - self.assertTrue( - 'slot="primaryAction"' in card_source, - "delete dialog should expose its confirmation control as the primary action", - ) - self.assertTrue( - 'slot="secondaryAction"' in card_source, - "delete dialog should expose its cancellation control as the secondary action", - ) - self.assertTrue( - 'data-dialog="close"' in card_source, - "delete dialog actions should use the current Home Assistant close API", - ) +class ScheduleCardBundleTest(unittest.TestCase): def test_delete_websocket_reconciles_ambiguous_hub_errors(self): websocket_source = WEBSOCKETS.read_text() @@ -106,56 +28,3 @@ def test_delete_websocket_reconciles_ambiguous_hub_errors(self): in websocket_source, "delete websocket should report an error only when the schedule remains", ) - - def test_successful_delete_notifies_parent_to_return_to_overview(self): - card_source = CARD.read_text() - - self.assertTrue( - 'new CustomEvent("schedule-deleted",{bubbles:!0,composed:!0})' - in card_source, - "successful deletion should emit an event across card boundaries", - ) - self.assertTrue( - "@schedule-deleted=${this._scheduleDeleted}" in card_source, - "parent card should listen for the standard delete completion event", - ) - self.assertTrue( - "_scheduleDeleted(){this._schedule_id=0,this._schedule_type=\"\",this._view=Et.Overview}" - in card_source, - "parent card should clear the deleted selection before returning to overview", - ) - - def test_delete_confirmation_explains_delayed_ui_update(self): - card_source = CARD.read_text() - - self.assertTrue( - "Note : UI can take 5-10 seconds to reflect change" in card_source, - "delete dialog should set expectations for the hub response delay", - ) - - def test_schedule_name_forms_use_current_home_assistant_input(self): - card_source = CARD.read_text() - inputs = re.findall( - r".*?)", - card_source, - re.DOTALL, - ) - - self.assertEqual(len(inputs), 2) - self.assertNotIn(" ModuleType: def _load_sensor_module() -> ModuleType: """Load the sensor module with only the imports needed by this test.""" _module("aioWiserHeatAPI") - _module("aioWiserHeatAPI.const", TEXT_UNKNOWN="Unknown") + _module( + "aioWiserHeatAPI.const", TEXT_OFF="Off", TEXT_ON="On", TEXT_UNKNOWN="Unknown" + ) _module("aioWiserHeatAPI.wiserhub", TEMP_MINIMUM=5, TEMP_OFF="Off") _module("homeassistant") @@ -56,6 +58,7 @@ class SensorEntity: LIGHT_LUX="lx", STATE_UNAVAILABLE="unavailable", STATE_UNKNOWN="unknown", + STATE_OFF="off", STATE_ON="on", EntityCategory=SimpleNamespace(DIAGNOSTIC="diagnostic"), UnitOfTemperature=SimpleNamespace(CELSIUS="°C"), @@ -63,13 +66,14 @@ class SensorEntity: UnitOfElectricCurrent=SimpleNamespace(), UnitOfElectricPotential=SimpleNamespace(), PERCENTAGE="%", - UnitOfPower=SimpleNamespace(KILO_WATT="kW"), + UnitOfPower=SimpleNamespace(KILO_WATT="kW", WATT="W"), UnitOfEnergy=SimpleNamespace(), UnitOfPressure=SimpleNamespace(BAR="bar"), UnitOfVolumeFlowRate=SimpleNamespace(LITERS_PER_MINUTE="L/min"), ) _module("homeassistant.core", HomeAssistant=object, callback=lambda func: func) - _module("homeassistant.helpers") + entity_registry = _module("homeassistant.helpers.entity_registry") + _module("homeassistant.helpers", entity_registry=entity_registry) class CoordinatorEntity: def __init__(self, coordinator, *args: object) -> None: @@ -88,6 +92,9 @@ def available(self): _module( "wiser.const", DATA="data", + CONF_ADDITIONAL_SENSORS="additional_sensors", + CONF_SCHEDULE_SETPOINT_ROOMS="schedule_setpoint_rooms", + CONF_EQUIPMENT_SENSORS="equipment_sensors", CONF_OPENTHERM_SENSORS="opentherm_sensors", DOMAIN="wiser", ENTITY_PREFIX="Wiser", @@ -99,6 +106,7 @@ def available(self): ) _module( "wiser.helpers", + active_wiser_rooms=lambda data: data.wiserhub.rooms.all, get_device_area_info=lambda _data, _device_id: {}, get_device_name=lambda _data, device_id, device_type="device": ( "Wiser HeatHub" @@ -108,12 +116,25 @@ def available(self): get_identifier=lambda *_args: "identifier", get_hub_device_info=lambda _data: {"identifiers": {("wiser", "hub")}}, get_hub_via_device_info=lambda _data: {}, + get_legacy_unique_id=lambda *_args: "legacy-unique-id", + get_light_binary_sensor_unique_id=lambda *_args: "light-binary-sensor-id", + get_light_entity_name=lambda _data, _light, entity_name: entity_name, + get_physical_entity_unique_id=lambda _data, domain, device_id, entity_type: ( + f"physical-{domain}-{device_id}-{entity_type}" + ), + get_room_entity_unique_id=lambda *_args: "room-entity-id", get_unique_id=lambda *_args: "unique-id", + get_uuid_unique_id=lambda unique_id: f"uuid-{unique_id}", + room_has_climate=lambda room: bool(room.devices), ) class WiserEntityMixin: pass _module("wiser.entity", WiserEntityMixin=WiserEntityMixin) + _module( + "wiser.opentherm_detection", + opentherm_is_detected=lambda *_args: True, + ) def relative_modulation_level(opentherm): raw = opentherm.operational_data.json_data.get("RelativeModulationLevel") if isinstance(raw, bool) or not isinstance(raw, (int, float)): @@ -239,6 +260,16 @@ def opentherm_sensor_value(opentherm, key): ), relative_modulation_level=relative_modulation_level, ) + _module( + "wiser.opentherm_detection", + opentherm_is_detected=lambda config_entry, opentherm: bool( + opentherm + and ( + config_entry.data.get("opentherm_ever_connected", False) + or getattr(opentherm, "connection_status", None) == "Connected" + ) + ), + ) _module( "wiser.temperature", room_target_temperature=lambda room, frost_temp, off_temp: ( @@ -285,10 +316,371 @@ class BinarySensorEntity: assert spec and spec.loader module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module - spec.loader.exec_module(module) + # The classes exercised here precede WiserStateActive, whose structural + # pattern matching requires a newer Python than the lightweight local test + # runtime. Home Assistant itself runs a supported newer Python version. + source = BINARY_SENSOR_SOURCE_PATH.read_text(encoding="utf-8").split( + "class WiserStateActive", 1 + )[0] + exec(compile(source, BINARY_SENSOR_SOURCE_PATH, "exec"), module.__dict__) return module +class WiserSummerComfortSetupTest(unittest.TestCase): + """Tests for second-generation hub system sensor setup.""" + + @classmethod + def setUpClass(cls) -> None: + setup_source = BINARY_SENSOR_SOURCE_PATH.read_text().split( + "class BaseBinarySensor", 1 + )[0] + cls.tree = ast.parse(setup_source) + tested_functions = [ + node + for node in cls.tree.body + if isinstance(node, ast.FunctionDef) + and node.name + in { + "_supports_v2_system_property", + "_supports_summer_discomfort_prevention", + "_supports_summer_comfort_available", + "_supports_pcm_binary_sensor", + "_requires_v2_entity_cleanup", + "_remove_unsupported_v2_entities", + } + ] + cls.registry = Mock() + cls.get_legacy_unique_id = Mock(return_value="legacy-unique-id") + cls.get_uuid_unique_id = Mock(return_value="uuid-unique-id") + namespace = { + "DOMAIN": "wiser", + "HomeAssistant": object, + "TEXT_UNKNOWN": "Unknown", + "er": SimpleNamespace(async_get=lambda _hass: cls.registry), + "get_legacy_unique_id": cls.get_legacy_unique_id, + "get_uuid_unique_id": cls.get_uuid_unique_id, + } + exec( + compile( + ast.Module(body=tested_functions, type_ignores=[]), + BINARY_SENSOR_SOURCE_PATH.name, + "exec", + ), + namespace, + ) + cls.supports_summer_discomfort = staticmethod( + namespace["_supports_summer_discomfort_prevention"] + ) + cls.supports_summer_comfort = staticmethod( + namespace["_supports_summer_comfort_available"] + ) + cls.supports_pcm = staticmethod(namespace["_supports_pcm_binary_sensor"]) + cls.requires_cleanup = staticmethod( + namespace["_requires_v2_entity_cleanup"] + ) + cls.remove_unsupported_entities = staticmethod( + namespace["_remove_unsupported_v2_entities"] + ) + + def setUp(self) -> None: + self.registry.reset_mock() + self.get_legacy_unique_id.reset_mock() + self.get_uuid_unique_id.reset_mock() + self.get_legacy_unique_id.return_value = "legacy-unique-id" + self.get_uuid_unique_id.return_value = "uuid-unique-id" + + def test_v1_does_not_support_v2_system_sensors(self) -> None: + data = SimpleNamespace(hub_version=1) + self.assertFalse(self.supports_summer_discomfort(data)) + self.assertFalse(self.supports_summer_comfort(data)) + self.assertFalse(self.supports_pcm(data)) + + def test_v2_without_summer_comfort_data_is_not_supported(self) -> None: + data = SimpleNamespace( + hub_version=2, + wiserhub=SimpleNamespace(system=SimpleNamespace()), + ) + self.assertFalse(self.supports_summer_discomfort(data)) + self.assertFalse(self.supports_summer_comfort(data)) + + def test_v2_summer_properties_are_detected_independently(self) -> None: + data = SimpleNamespace( + hub_version=2, + wiserhub=SimpleNamespace( + system=SimpleNamespace(summer_comfort_available=False) + ), + ) + self.assertTrue(self.supports_summer_comfort(data)) + self.assertFalse(self.supports_summer_discomfort(data)) + + data.wiserhub.system = SimpleNamespace( + summer_discomfort_prevention=False + ) + self.assertTrue(self.supports_summer_discomfort(data)) + self.assertFalse(self.supports_summer_comfort(data)) + + def test_v2_without_pcm_data_is_not_supported(self) -> None: + data = SimpleNamespace( + hub_version=2, + wiserhub=SimpleNamespace(system=SimpleNamespace()), + ) + self.assertFalse(self.supports_pcm(data)) + + def test_v2_with_pcm_data_is_supported(self) -> None: + data = SimpleNamespace( + hub_version=2, + wiserhub=SimpleNamespace(system=SimpleNamespace(pcm_version="1.0")), + ) + self.assertTrue(self.supports_pcm(data)) + + def test_future_hub_versions_can_support_v2_system_sensors(self) -> None: + data = SimpleNamespace( + hub_version=3, + wiserhub=SimpleNamespace( + system=SimpleNamespace( + summer_comfort_available=False, + summer_discomfort_prevention=False, + pcm_version="1.0", + ) + ), + ) + self.assertTrue(self.supports_summer_discomfort(data)) + self.assertTrue(self.supports_summer_comfort(data)) + self.assertTrue(self.supports_pcm(data)) + + def test_cleanup_is_limited_to_v1_hubs(self) -> None: + self.assertTrue(self.requires_cleanup(SimpleNamespace(hub_version=1))) + self.assertFalse(self.requires_cleanup(SimpleNamespace(hub_version=2))) + self.assertFalse(self.requires_cleanup(SimpleNamespace(hub_version=3))) + + def test_cleanup_removes_owned_migrated_entity(self) -> None: + self.registry.async_get_entity_id.return_value = "binary_sensor.pcm_limit" + self.registry.async_get.return_value = SimpleNamespace( + config_entry_id="entry-id" + ) + data = SimpleNamespace( + wiserhub=SimpleNamespace(system=SimpleNamespace(name="WiserHeat123456")) + ) + + self.remove_unsupported_entities( + object(), "entry-id", data, ("PCM Device Limit Reached",) + ) + + self.get_legacy_unique_id.assert_called_once_with( + data, + "sensor", + "PCM Device Limit Reached", + "HeatHub123456 PCM Device Limit Reached", + ) + self.get_uuid_unique_id.assert_called_once_with("legacy-unique-id") + self.registry.async_get_entity_id.assert_called_once_with( + "binary_sensor", "wiser", "uuid-unique-id" + ) + self.registry.async_remove.assert_called_once_with( + "binary_sensor.pcm_limit" + ) + + def test_cleanup_keeps_entity_owned_by_another_entry(self) -> None: + self.registry.async_get_entity_id.return_value = "binary_sensor.pcm_limit" + self.registry.async_get.return_value = SimpleNamespace( + config_entry_id="other-entry" + ) + data = SimpleNamespace( + wiserhub=SimpleNamespace(system=SimpleNamespace(name="WiserHeat123456")) + ) + + self.remove_unsupported_entities( + object(), "entry-id", data, ("PCM Device Limit Reached",) + ) + + self.registry.async_remove.assert_not_called() + + def test_summer_comfort_sensors_use_capability_guard(self) -> None: + setup = next( + node + for node in self.tree.body + if isinstance(node, ast.AsyncFunctionDef) + and node.name == "async_setup_entry" + ) + + def guard_for(function_name: str) -> ast.If: + return next( + node + for node in ast.walk(setup) + if isinstance(node, ast.If) + and isinstance(node.test, ast.Call) + and isinstance(node.test.func, ast.Name) + and node.test.func.id == function_name + ) + + discomfort_guard = guard_for("_supports_summer_discomfort_prevention") + discomfort_calls = { + node.func.id + for node in ast.walk(discomfort_guard) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) + } + self.assertIn( + "WiserSummerDiscomfortPrevention", + discomfort_calls, + ) + + comfort_guard = guard_for("_supports_summer_comfort_available") + comfort_calls = { + node.func.id + for node in ast.walk(comfort_guard) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) + } + self.assertIn( + "WiserSummerComfortAvailable", + comfort_calls, + ) + + pcm_guard = guard_for("_supports_pcm_binary_sensor") + pcm_calls = { + node.func.id + for node in ast.walk(pcm_guard) + if isinstance(node, ast.Call) and isinstance(node.func, ast.Name) + } + self.assertIn( + "WiserPCMDeviceLimitReached", + pcm_calls, + ) + + cleanup_call = next( + node + for node in ast.walk(setup) + if isinstance(node, ast.Call) + and isinstance(node.func, ast.Name) + and node.func.id == "_remove_unsupported_v2_entities" + ) + self.assertIsNotNone(cleanup_call) + + +class WiserSystemBinarySensorUpdateTest(unittest.TestCase): + """Tests for coordinator updates to system binary sensors.""" + + @classmethod + def setUpClass(cls) -> None: + source = BINARY_SENSOR_SOURCE_PATH.read_text().split( + "class RoomBinarySensor", 1 + )[0] + tree = ast.parse(source) + system_class = next( + node + for node in tree.body + if isinstance(node, ast.ClassDef) + and node.name == "SystemBinarySensor" + ) + update_method = next( + node + for node in system_class.body + if isinstance(node, ast.FunctionDef) + and node.name == "_handle_coordinator_update" + ) + update_method.decorator_list = [] + namespace = {"_LOGGER": Mock()} + exec( + compile( + ast.Module(body=[update_method], type_ignores=[]), + BINARY_SENSOR_SOURCE_PATH.name, + "exec", + ), + namespace, + ) + cls.handle_update = staticmethod(namespace["_handle_coordinator_update"]) + + def setUp(self) -> None: + self.sensor = SimpleNamespace( + _data=SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace(summer_comfort_available=False) + ) + ), + _sensor_type="Summer Comfort Available", + _state=True, + async_write_ha_state=Mock(), + ) + + def test_coordinator_update_refreshes_state(self) -> None: + self.handle_update(self.sensor) + + self.assertFalse(self.sensor._state) + self.sensor.async_write_ha_state.assert_called_once_with() + + def test_missing_property_sets_unknown_state(self) -> None: + self.sensor._data.wiserhub.system = SimpleNamespace() + + self.handle_update(self.sensor) + + self.assertIsNone(self.sensor._state) + self.sensor.async_write_ha_state.assert_called_once_with() + + +class WiserRoomScheduledSetpointSensorTest(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.sensor_module = _load_sensor_module() + + @staticmethod + def _data(rooms): + rooms_by_id = {room.id: room for room in rooms} + return SimpleNamespace( + wiserhub=SimpleNamespace( + rooms=SimpleNamespace( + all=rooms, + get_by_id=rooms_by_id.get, + get_by_device_id=lambda _device_id: None, + ), + system=SimpleNamespace(name="WiserHeat123456"), + ) + ) + + def test_sensor_reports_room_scheduled_setpoint(self) -> None: + room = SimpleNamespace( + id=4, + name="Kitchen", + devices=[], + scheduled_target_temperature=19.5, + ) + sensor = self.sensor_module.WiserRoomScheduledSetpointSensor( + self._data([room]), room.id + ) + + self.assertEqual(sensor.native_value, 19.5) + self.assertEqual( + sensor._attr_translation_key, "current_schedule_temperature" + ) + self.assertEqual(sensor.unique_id, "room-entity-id") + self.assertEqual(sensor.device_info["manufacturer"], "Drayton") + self.assertEqual(sensor.device_info["model"], "Room") + self.assertEqual(sensor.device_info["suggested_area"], "Kitchen") + + def test_only_selected_rooms_without_climate_get_sensors(self) -> None: + empty = SimpleNamespace( + id=4, + name="Kitchen", + devices=[], + scheduled_target_temperature=19.5, + ) + occupied = SimpleNamespace( + id=5, + name="Lounge", + devices=[SimpleNamespace(id=10)], + scheduled_target_temperature=21, + ) + data = self._data([empty, occupied]) + + self.assertEqual( + self.sensor_module._selected_room_scheduled_setpoint_sensors(data, []), + [], + ) + sensors = self.sensor_module._selected_room_scheduled_setpoint_sensors( + data, [str(empty.id), str(occupied.id)] + ) + self.assertEqual(len(sensors), 1) + self.assertEqual(sensors[0]._device_id, empty.id) + + class WiserDeviceSignalSensorNameTest(unittest.TestCase): """Tests for controller and device signal sensor names.""" @@ -348,6 +740,56 @@ def test_room_measurements_do_not_expose_lts_implementation_detail(self) -> None self.assertIsNone(getattr(humidity, "_attr_translation_key", None)) self.assertEqual(demand._attr_translation_key, "heating_demand") + def test_humidity_uses_physical_device_unique_id(self) -> None: + room = SimpleNamespace(id=1, name="Kitchen") + roomstat = SimpleNamespace(id=2, room_id=1) + data = SimpleNamespace( + wiserhub=SimpleNamespace( + devices=SimpleNamespace(get_by_id=lambda _device_id: roomstat), + rooms=SimpleNamespace( + get_by_id=lambda _room_id: room, + get_by_device_id=lambda _device_id: room, + ), + system=SimpleNamespace(name="WiserHeat123456"), + ) + ) + + sensor = self.sensor_module.WiserLTSHumiditySensor(data, 2) + + self.assertEqual(sensor.unique_id, "physical-sensor-2-humidity") + + def test_power_sensors_use_physical_device_unique_ids(self) -> None: + room = SimpleNamespace(id=1, name="Kitchen") + device = SimpleNamespace( + id=9, + room_id=1, + product_type="HeatingActuator", + ) + devices = SimpleNamespace( + get_by_id=lambda _device_id: device, + smartplugs=SimpleNamespace(get_by_id=lambda _device_id: device), + ) + data = SimpleNamespace( + wiserhub=SimpleNamespace( + devices=devices, + rooms=SimpleNamespace( + get_by_id=lambda _room_id: room, + get_by_device_id=lambda _device_id: room, + ), + system=SimpleNamespace(name="WiserHeat123456"), + ) + ) + + smartplug = self.sensor_module.WiserSmartplugPower( + data, 9, sensor_type="Total Power" + ) + actuator = self.sensor_module.WiserLTSPowerSensor( + data, 9, sensor_type="Power" + ) + + self.assertEqual(smartplug.unique_id, "physical-sensor-9-energy") + self.assertEqual(actuator.unique_id, "physical-sensor-9-power") + def test_heating_demand_is_not_classified_as_power_factor(self) -> None: """A percentage heating demand is not an electrical power factor.""" demand_class = next( @@ -368,9 +810,16 @@ def test_controller_signal_belongs_to_physical_hub(self) -> None: self.assertEqual(sensor.device_info, {"identifiers": {("wiser", "hub")}}) - def test_hub_v2_setup_does_not_create_duplicate_equipment_readings(self) -> None: + def test_detailed_equipment_readings_are_opt_in(self) -> None: setup_source = SOURCE_PATH.read_text().split("class WiserSensor", 1)[0] - self.assertNotIn("WiserEquipmentSensor(", setup_source) + self.assertIn( + 'config_entry.options.get(\n CONF_EQUIPMENT_SENSORS, False', + setup_source, + ) + self.assertEqual(setup_source.count("WiserEquipmentSensor("), 5) + self.assertEqual(setup_source.count("if equipment_sensors_enabled"), 5) + self.assertNotIn("equipment_id > 0", setup_source) + self.assertIn("power_tags_c", setup_source) self.assertNotIn('legacy_name="Equipment Energy Delivered"', setup_source) def test_power_display_name_does_not_change_historical_unique_id_input( @@ -409,6 +858,242 @@ def test_power_display_name_does_not_change_historical_unique_id_input( self.assertEqual(energy._attr_translation_key, "total_energy") self.assertEqual(energy._sensor_type, "Equipment Total Energy ") + def test_equipment_attributes_handle_missing_delivered_energy(self) -> None: + class OptionalAttributes(SimpleNamespace): + def __getattr__(self, _name): + return None + + power = OptionalAttributes( + active_power=10, + total_active_power=20, + current_summation_delivered=None, + ) + equipment = OptionalAttributes( + device_type="Other", + equipment_name="Test equipment", + power=power, + ) + sensor = object.__new__(self.sensor_module.WiserEquipmentSensor) + sensor._device = OptionalAttributes( + product_identifier="product", + equipment=equipment, + ) + + attributes = sensor.extra_state_attributes + self.assertEqual(attributes["equipment_name"], "Test equipment") + self.assertNotIn("name", attributes) + self.assertNotIn("energy", attributes) + + power.current_summation_delivered = 0 + self.assertEqual(sensor.extra_state_attributes["energy"], 0) + + def test_equipment_name_uses_translation_without_changing_unique_id(self) -> None: + device = SimpleNamespace(id=9) + data = SimpleNamespace( + wiserhub=SimpleNamespace( + devices=SimpleNamespace(get_by_id=lambda _device_id: device), + rooms=SimpleNamespace(get_by_device_id=lambda _device_id: None), + system=SimpleNamespace(name="WiserHeat123456"), + ) + ) + + sensor = self.sensor_module.WiserEquipmentSensor(data, 9) + + self.assertEqual(sensor._sensor_type, "") + self.assertEqual(sensor._attr_translation_key, "equipment_power") + self.assertNotIn("name", self.sensor_module.WiserEquipmentSensor.__dict__) + + def test_equipment_power_uses_native_measurement_metadata(self) -> None: + device = SimpleNamespace( + id=9, + instantaneous_power=45.6, + equipment=SimpleNamespace( + power=SimpleNamespace(total_active_power=123.4, active_power=78.9) + ), + ) + data = SimpleNamespace( + wiserhub=SimpleNamespace( + devices=SimpleNamespace(get_by_id=lambda _device_id: device), + rooms=SimpleNamespace(get_by_device_id=lambda _device_id: None), + system=SimpleNamespace(name="WiserHeat123456"), + ) + ) + + sensor = self.sensor_module.WiserEquipmentSensor(data, 9) + sensor.async_write_ha_state = Mock() + + self.assertEqual(sensor.native_value, 123.4) + self.assertEqual(sensor.state, 123.4) + self.assertEqual(sensor.native_unit_of_measurement, "W") + self.assertEqual(sensor._attr_device_class, "power") + self.assertEqual(sensor._attr_state_class, "measurement") + self.assertEqual(sensor._attr_entity_category, "diagnostic") + self.assertNotIn("state", self.sensor_module.WiserEquipmentSensor.__dict__) + + device.equipment.power.total_active_power = None + sensor._handle_coordinator_update() + self.assertEqual(sensor.state, 78.9) + + device.equipment.power.active_power = None + sensor._handle_coordinator_update() + self.assertEqual(sensor.state, 45.6) + + device.instantaneous_power = 0 + sensor._handle_coordinator_update() + self.assertEqual(sensor.state, 0) + + def test_equipment_sensor_recovers_from_missing_runtime_data(self) -> None: + equipment = SimpleNamespace( + power=SimpleNamespace(total_active_power=123.4) + ) + device = SimpleNamespace(id=9, equipment=equipment) + data = SimpleNamespace( + last_update_success=True, + wiserhub=SimpleNamespace( + devices=SimpleNamespace(get_by_id=lambda _device_id: device), + rooms=SimpleNamespace(get_by_device_id=lambda _device_id: None), + system=SimpleNamespace(name="WiserHeat123456"), + ), + ) + sensor = self.sensor_module.WiserEquipmentSensor(data, 9) + + self.assertTrue(sensor.available) + self.assertEqual(sensor.native_value, 123.4) + + device.equipment = None + self.assertFalse(sensor.available) + self.assertIsNone(sensor.native_value) + self.assertEqual(sensor.extra_state_attributes, {}) + + sensor._device = None + self.assertFalse(sensor.available) + self.assertIsNone(sensor.native_value) + self.assertEqual(sensor.extra_state_attributes, {}) + + sensor._device = device + device.equipment = equipment + self.assertTrue(sensor.available) + self.assertEqual(sensor.native_value, 123.4) + + def test_live_equipment_attributes_are_excluded_from_recorder(self) -> None: + self.assertEqual( + self.sensor_module.WiserEquipmentSensor._unrecorded_attributes, + { + "active_power", + "energy", + "energy_delivered", + "energy_received", + "rms_current", + "rms_voltage", + "total_active_power", + }, + ) + + +class WiserSystemCircuitStateTest(unittest.TestCase): + """Tests for normalized heating and hot-water circuit states.""" + + @classmethod + def setUpClass(cls) -> None: + cls.sensor_module = _load_sensor_module() + + def _sensor( + self, + sensor_type: str, + *, + heating_state="Off", + demand_state="Off", + hot_water_state="Off", + system=None, + ): + heating_channel = SimpleNamespace( + name="Channel 1", + percentage_demand=0, + room_ids=[], + is_smart_valve_preventing_demand=False, + heating_relay_status=heating_state, + demand_on_off_output=demand_state, + ) + sensor = object.__new__(self.sensor_module.WiserSystemCircuitState) + sensor._sensor_type = sensor_type + sensor._device_id = 1 + sensor._data = SimpleNamespace( + wiserhub=SimpleNamespace( + heating_channels=SimpleNamespace( + get_by_id=lambda _device_id: heating_channel + ), + hotwater=SimpleNamespace(current_state=hot_water_state), + system=system, + ) + ) + sensor.async_write_ha_state = Mock() + return sensor + + def test_heating_state_is_normalized(self) -> None: + sensor = self._sensor("Heating", heating_state="On") + + sensor._handle_coordinator_update() + + self.assertEqual(sensor.native_value, "on") + + def test_unknown_heating_relay_uses_normalized_demand_output(self) -> None: + sensor = self._sensor( + "Heating", heating_state="Unknown", demand_state="Off" + ) + + sensor._handle_coordinator_update() + + self.assertEqual(sensor.native_value, "off") + + def test_hot_water_state_is_normalized(self) -> None: + sensor = self._sensor("Hot Water", hot_water_state="On") + + sensor._handle_coordinator_update() + + self.assertEqual(sensor.native_value, "on") + + def test_unknown_or_unexpected_state_is_not_reported_as_off(self) -> None: + sensor = self._sensor("Hot Water", hot_water_state="Unknown") + + sensor._handle_coordinator_update() + + self.assertIsNone(sensor.native_value) + + def test_heating_attributes_handle_missing_opentherm_data(self) -> None: + for system in ( + None, + SimpleNamespace(opentherm=None), + SimpleNamespace( + opentherm=SimpleNamespace( + connection_status="Connected", + operational_data=None, + ) + ), + ): + with self.subTest(system=system): + sensor = self._sensor("Heating", system=system) + + self.assertEqual( + sensor.extra_state_attributes, + { + "percentage_demand_Channel 1": 0, + "room_ids_Channel 1": [], + "is_smartvalve_preventing_demand_Channel 1": False, + }, + ) + + def test_circuit_states_have_translations(self) -> None: + files = [ + SOURCE_PATH.parent / "strings.json", + *(SOURCE_PATH.parent / "translations").glob("*.json"), + ] + + for path in files: + with self.subTest(path=path.name): + sensors = json.loads(path.read_text())["entity"]["sensor"] + for key in ("heating", "heating_channel", "hot_water"): + self.assertEqual(set(sensors[key]["state"]), {"off", "on"}) + class WiserBatterySensorAvailabilityTest(unittest.TestCase): """Regression tests for battery availability when level text is absent.""" @@ -643,7 +1328,7 @@ def test_delta_t_has_temperature_measurement_metadata(self): def test_additional_boiler_readings_have_native_metadata(self): readings = { "boiler_exhaust_temperature": (28, "°C", "temperature", "mdi:smoke"), - "maximum_capacity_kw": (30, "kW", "power", "mdi:flash"), + "maximum_capacity_kw": (30, "kW", "power", "mdi:gas-burner"), "minimum_modulation_level": (27, "%", None, "mdi:percent"), } for key, (value, unit, device_class, icon) in readings.items(): @@ -680,7 +1365,7 @@ def test_estimated_boiler_output_uses_capacity_and_modulation(self): self.assertEqual(sensor.native_unit_of_measurement, "kW") self.assertEqual(sensor.device_class, "power") self.assertEqual(sensor.state_class, "measurement") - self.assertEqual(sensor.icon, "mdi:flash") + self.assertEqual(sensor.icon, "mdi:gas-burner") self.opentherm.operational_data.json_data["SlaveStatus"] = 0 sensor._handle_coordinator_update() @@ -713,7 +1398,7 @@ def test_coprocessor_fields_are_diagnostic_sensors(self): class WiserOpenThermFlameStatisticsSensorTest(unittest.TestCase): - """Tests for the optional rolling flame-runtime entity.""" + """Tests for the optional daily flame-runtime entity.""" @classmethod def setUpClass(cls): @@ -816,3 +1501,129 @@ def test_capability_flag_is_a_diagnostic_entity(self): ) self.assertEqual(sensor._attr_entity_category, "diagnostic") + + +class WiserSmokeAlarmStatusSensorTest(unittest.TestCase): + """Tests for smoke-alarm status fields exposed by the API.""" + + @classmethod + def setUpClass(cls): + cls.binary_sensor_module = _load_binary_sensor_module() + cls.sensor_module = _load_sensor_module() + + def setUp(self): + self.device = SimpleNamespace( + ac_mains=True, + test_mode=False, + hush_mode=True, + report_count=42, + product_type="SmokeAlarmDevice", + firmware_version="1.0", + ) + self.data = SimpleNamespace( + last_update_success=True, + wiserhub=SimpleNamespace( + devices=SimpleNamespace(get_by_id=lambda _device_id: self.device), + rooms=SimpleNamespace(get_by_device_id=lambda _device_id: None), + system=SimpleNamespace(name="WiserHeat123456"), + ), + ) + + def test_reads_ac_mains_state(self): + sensor = self.binary_sensor_module.WiserACMains( + self.data, 7, "AC Mains" + ) + sensor.async_write_ha_state = Mock() + + self.assertTrue(sensor.is_on) + self.assertEqual(sensor._attr_device_class, "power") + + self.device.ac_mains = False + sensor._handle_coordinator_update() + + self.assertFalse(sensor.is_on) + sensor.async_write_ha_state.assert_called_once_with() + + def test_reads_test_mode_state(self): + sensor = self.binary_sensor_module.WiserTestMode( + self.data, 7, "Test Mode" + ) + + self.assertFalse(sensor.is_on) + self.assertEqual(sensor._attr_icon, "mdi:test-tube") + + def test_reads_hush_mode_state(self): + sensor = self.binary_sensor_module.WiserHushMode( + self.data, 7, "Hush Mode" + ) + + self.assertTrue(sensor.is_on) + self.assertEqual(sensor._attr_icon, "mdi:volume-off") + + def test_reads_report_count_and_refreshes(self): + sensor = self.sensor_module.WiserSmokeAlarmReportCountSensor( + self.data, 7 + ) + sensor.async_write_ha_state = Mock() + + self.assertEqual(sensor.native_value, 42) + self.assertEqual(sensor._attr_entity_category, "diagnostic") + self.assertEqual(sensor._attr_icon, "mdi:counter") + self.assertEqual(sensor.unique_id, "physical-sensor-7-report_count") + + self.device.report_count = 43 + sensor._handle_coordinator_update() + + self.assertEqual(sensor.native_value, 43) + sensor.async_write_ha_state.assert_called_once_with() + + +class WiserEquipmentBinarySensorTest(unittest.TestCase): + """Tests for binary sensors backed by optional equipment data.""" + + @classmethod + def setUpClass(cls): + cls.binary_sensor_module = _load_binary_sensor_module() + + def setUp(self): + self.equipment = SimpleNamespace(controllable=True, pcm_mode=False) + self.device = SimpleNamespace( + id=9, + equipment=self.equipment, + product_type="SmartPlug", + firmware_version="1.0", + ) + self.data = SimpleNamespace( + last_update_success=True, + wiserhub=SimpleNamespace( + devices=SimpleNamespace(get_by_id=lambda _device_id: self.device), + rooms=SimpleNamespace(get_by_device_id=lambda _device_id: None), + system=SimpleNamespace(name="WiserHeat123456"), + ), + ) + self.sensor = self.binary_sensor_module.WiserEquipment( + self.data, 9, "Controllable", "equipment" + ) + self.sensor.async_write_ha_state = Mock() + + def test_becomes_unavailable_and_recovers_with_equipment_data(self): + self.assertTrue(self.sensor.available) + self.assertTrue(self.sensor.is_on) + + self.device.equipment = None + self.sensor._handle_coordinator_update() + self.assertFalse(self.sensor.available) + self.assertIsNone(self.sensor.is_on) + + self.device.equipment = self.equipment + self.sensor._handle_coordinator_update() + self.assertTrue(self.sensor.available) + self.assertTrue(self.sensor.is_on) + + def test_handles_device_temporarily_disappearing(self): + self.device = None + + self.sensor._handle_coordinator_update() + + self.assertFalse(self.sensor.available) + self.assertIsNone(self.sensor.is_on) diff --git a/tests/test_sidebar_icons.py b/tests/test_sidebar_icons.py new file mode 100644 index 00000000..63d25ab8 --- /dev/null +++ b/tests/test_sidebar_icons.py @@ -0,0 +1,56 @@ +"""Check custom sidebar icons load without opening a Lovelace dashboard.""" +import ast +from pathlib import Path +from types import SimpleNamespace +import unittest +from unittest.mock import AsyncMock, Mock, call + +ROOT = Path(__file__).resolve().parents[1] / "custom_components/wiser/frontend" + + +class SidebarIconsTest(unittest.IsolatedAsyncioTestCase): + def test_icons_include_official_wiser_w(self): + source = (ROOT / "wiser-icons.js").read_text() + self.assertIn('wiser: {', source) + self.assertIn('viewBox: "0 0 877 768"', source) + self.assertIn('{ name: "wiser", keywords:', source) + + async def test_icons_register_after_static_path_without_lovelace(self): + tree = ast.parse((ROOT / "__init__.py").read_text()) + cls = next(node for node in tree.body if isinstance(node, ast.ClassDef) and node.name == "JSModuleRegistration") + method = next(node for node in cls.body if isinstance(node, ast.AsyncFunctionDef) and node.name == "async_register") + register = Mock() + version = Mock(return_value="sha256-icons") + namespace = { + "__file__": str(ROOT / "__init__.py"), "Path": Path, + "card_version": version, "add_extra_js_url": register, + "async_load_registry": AsyncMock(), + "URL_BASE": "/wiser", "MODE_STORAGE": "storage", + } + exec(compile(ast.Module(body=[method], type_ignores=[]), str(ROOT / "__init__.py"), "exec"), namespace) + host = SimpleNamespace( + hass=SimpleNamespace(async_add_executor_job=AsyncMock(side_effect=lambda fn, *args: fn(*args))), + lovelace=None, + _async_register_path=AsyncMock(), + _async_wait_for_lovelace_resources=AsyncMock(), + ) + register.side_effect = lambda *args: host._async_register_path.assert_awaited_once() + await namespace["async_register"](host) + self.assertEqual( + version.call_args_list, + [ + call(ROOT / "wiser-icons.js"), + call(ROOT / "wiser-hot-water-control.js"), + ], + ) + self.assertEqual( + register.call_args_list, + [ + call(host.hass, "/wiser/wiser-icons.js?v=sha256-icons"), + call( + host.hass, + "/wiser/wiser-hot-water-control.js?v=sha256-icons", + ), + ], + ) + host._async_wait_for_lovelace_resources.assert_not_awaited() diff --git a/tests/test_switch_hot_water.py b/tests/test_switch_hot_water.py index fbb39641..703f9f8a 100644 --- a/tests/test_switch_hot_water.py +++ b/tests/test_switch_hot_water.py @@ -2,6 +2,7 @@ from __future__ import annotations +import __future__ import asyncio import importlib.util from pathlib import Path @@ -45,6 +46,9 @@ class CoordinatorEntity: def __init__(self, *args: object) -> None: pass + async def async_will_remove_from_hass(self) -> None: + self.removed = True + _module( "homeassistant.helpers.update_coordinator", CoordinatorEntity=CoordinatorEntity ) @@ -58,6 +62,7 @@ def __init__(self, *args: object) -> None: ENTITY_PREFIX="Wiser", HOT_WATER="hot_water", MANUFACTURER="Drayton", + MANUFACTURER_SCHNEIDER="Schneider Electric", ) def hub_error_handler(func): @@ -70,10 +75,12 @@ def hub_error_handler(func): ), get_hub_device_info=lambda _data: {"identifiers": {("wiser", "hub")}}, get_hub_via_device_info=lambda _data: {}, - get_room_name=lambda *_args: "Room", get_identifier=lambda *_args: "identifier", get_legacy_device_name=lambda *_args, **_kwargs: "Wiser HeatHub", get_legacy_unique_id=lambda *_args: "legacy-unique-id", + get_light_entity_name=lambda _data, _light, entity_name: entity_name, + get_physical_entity_unique_id=lambda *_args: "physical-entity-id", + get_room_entity_unique_id=lambda *_args: "room-entity-id", get_unique_id=lambda *_args: "unique-id", get_uuid_unique_id=lambda *_args: "uuid-unique-id", hub_error_handler=hub_error_handler, @@ -95,7 +102,17 @@ class WiserScheduleEntity: assert spec and spec.loader module = importlib.util.module_from_spec(spec) sys.modules[spec.name] = module - spec.loader.exec_module(module) + # The integration targets Home Assistant's newer Python runtime. Compile + # annotations lazily so this lightweight regression test also runs with the + # older system Python available in the local development environment. + source = SOURCE_PATH.read_text(encoding="utf-8") + code = compile( + source, + SOURCE_PATH, + "exec", + flags=__future__.annotations.compiler_flag, + ) + exec(code, module.__dict__) return module @@ -153,5 +170,283 @@ def test_turn_off_overrides_hot_water_state_to_off(self) -> None: self.assertEqual(self._override_calls, ["Off"]) +class WiserPassiveModeRoomSupportTest(unittest.TestCase): + """Tests for deciding which rooms receive a passive-mode switch.""" + + @classmethod + def setUpClass(cls) -> None: + cls.switch_module = _load_switch_module() + + def test_supports_smart_valve_room(self) -> None: + room = SimpleNamespace( + number_of_smartvalves=1, + underfloor_heating_id=None, + ) + + self.assertTrue(self.switch_module._room_supports_passive_mode(room)) + + def test_supports_ufh_only_room(self) -> None: + room = SimpleNamespace( + number_of_smartvalves=0, + underfloor_heating_id=4, + ) + + self.assertTrue(self.switch_module._room_supports_passive_mode(room)) + + def test_ignores_room_without_heating_device(self) -> None: + room = SimpleNamespace( + number_of_smartvalves=0, + underfloor_heating_id=None, + ) + + self.assertFalse(self.switch_module._room_supports_passive_mode(room)) + + +class _FakeSmartPlug: + def __init__(self, *, is_on: bool) -> None: + self.is_on = is_on + self.schedule = None + self.control_source = "Manual" + self.manual_state = "On" if is_on else "Off" + self.mode = "Manual" + self.name = "Test Plug" + self.room_id = 0 + self.away_mode_action = "Off" + self.scheduled_state = "Off" + self.schedule_id = 0 + self.commands = [] + + async def turn_on(self) -> None: + # The real API object is not updated until the follow-up hub refresh. + self.commands.append("turn_on") + + async def turn_off(self) -> None: + self.commands.append("turn_off") + + +class WiserSmartPlugImmediateStateTest(unittest.TestCase): + """Tests that smart-plug commands update the HA entity before refresh.""" + + @classmethod + def setUpClass(cls) -> None: + cls.switch_module = _load_switch_module() + + def _switch(self, *, is_on: bool): + plug = _FakeSmartPlug(is_on=is_on) + data = SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace(name="WiserHeat045XXX"), + devices=SimpleNamespace(get_by_id=lambda _id: plug), + rooms=SimpleNamespace(get_by_id=lambda _id: None), + ) + ) + switch = self.switch_module.WiserSmartPlugSwitch(data, 1, "Wiser Test Plug") + events = [] + switch.async_write_ha_state = lambda: events.append( + ("write", switch.is_on, switch.extra_state_attributes["output_state"]) + ) + + async def refresh(delay=0): + events.append(("refresh", delay, switch.is_on)) + + switch.async_force_update = refresh + scheduled = [] + switch._schedule_command_confirmation = scheduled.append + return switch, plug, events, scheduled + + def test_turn_on_publishes_on_before_background_confirmation(self) -> None: + switch, plug, events, scheduled = self._switch(is_on=False) + + asyncio.run(switch.async_turn_on()) + + self.assertEqual(plug.commands, ["turn_on"]) + self.assertEqual(events, [("write", True, "On")]) + self.assertEqual(scheduled, [1]) + + def test_turn_off_publishes_off_before_background_confirmation(self) -> None: + switch, plug, events, scheduled = self._switch(is_on=True) + + asyncio.run(switch.async_turn_off()) + + self.assertEqual(plug.commands, ["turn_off"]) + self.assertEqual(events, [("write", False, "Off")]) + self.assertEqual(scheduled, [1]) + + def test_stale_first_refresh_retries_until_hub_confirms(self) -> None: + switch, plug, events, scheduled = self._switch(is_on=False) + asyncio.run(switch.async_turn_on()) + refreshes = [] + + async def refresh(*_args, **_kwargs): + refreshes.append(True) + if len(refreshes) == 2: + plug.is_on = True + switch._handle_coordinator_update() + + switch.async_force_update = refresh + asyncio.run(switch._async_confirm_command(1)) + + self.assertEqual(len(refreshes), 2) + self.assertIsNone(switch._optimistic_is_on) + self.assertTrue(switch.is_on) + + def test_failed_confirmation_restores_hub_state(self) -> None: + switch, _plug, _events, _scheduled = self._switch(is_on=False) + switch._optimistic_gen = 1 + switch._optimistic_is_on = True + switch._is_on = True + + async def fail_refresh(*_args, **_kwargs): + raise RuntimeError("refresh failed") + + switch.async_force_update = fail_refresh + + with self.assertRaisesRegex(RuntimeError, "refresh failed"): + asyncio.run(switch._async_confirm_command(1)) + + self.assertIsNone(switch._optimistic_is_on) + self.assertFalse(switch.is_on) + + def test_superseded_confirmation_does_not_clobber_newer_command(self) -> None: + switch, plug, events, scheduled = self._switch(is_on=True) + switch._optimistic_gen = 2 + switch._optimistic_is_on = False + switch._is_on = False + + asyncio.run(switch._async_confirm_command(1)) + + self.assertIs(switch._optimistic_is_on, False) + self.assertFalse(switch.is_on) + + def test_failed_command_restores_hub_state(self) -> None: + switch, plug, events, scheduled = self._switch(is_on=False) + + async def fail(): + raise RuntimeError("command failed") + + plug.turn_on = fail + + with self.assertRaisesRegex(RuntimeError, "command failed"): + asyncio.run(switch.async_turn_on()) + + self.assertEqual(scheduled, []) + self.assertIsNone(switch._optimistic_is_on) + self.assertFalse(switch.is_on) + self.assertEqual( + events, + [("write", True, "On"), ("write", False, "Off")], + ) + + def test_new_confirmation_cancels_superseded_task(self) -> None: + switch, _plug, _events, _scheduled = self._switch(is_on=False) + tasks = [] + + class FakeTask: + cancelled = False + + def cancel(self): + self.cancelled = True + + def create_task(coro, _name): + coro.close() + task = FakeTask() + tasks.append(task) + return task + + switch.hass = SimpleNamespace(async_create_task=create_task) + switch.entity_id = "switch.test" + + # Restore the real method replaced by the helper's command spy. + switch._schedule_command_confirmation = ( + self.switch_module.WiserSmartPlugSwitch._schedule_command_confirmation.__get__( + switch + ) + ) + switch._schedule_command_confirmation(1) + switch._schedule_command_confirmation(2) + + self.assertTrue(tasks[0].cancelled) + self.assertFalse(tasks[1].cancelled) + + def test_removal_invalidates_and_cancels_confirmation(self) -> None: + switch, _plug, _events, _scheduled = self._switch(is_on=False) + + class FakeTask: + cancelled = False + + def cancel(self): + self.cancelled = True + + task = FakeTask() + switch._confirmation_task = task + previous_gen = switch._optimistic_gen + + asyncio.run(switch.async_will_remove_from_hass()) + + self.assertTrue(task.cancelled) + self.assertEqual(switch._optimistic_gen, previous_gen + 1) + self.assertIsNone(switch._confirmation_task) + self.assertTrue(switch.removed) + + +class _FakeEVSE: + def __init__(self, *, is_on: bool) -> None: + self.is_on = is_on + self.control_state = "selfManagement" if is_on else "consumptionForceOff" + self.model = "EVSCKT/OUTLET/1" + self.firmware_version = "2.10.0" + self.commands = [] + + async def turn_on(self) -> None: + self.commands.append("turn_on") + + async def turn_off(self) -> None: + self.commands.append("turn_off") + + +class WiserEVSESwitchTest(unittest.TestCase): + """Tests for EV charger switch commands and registry identity.""" + + @classmethod + def setUpClass(cls) -> None: + cls.switch_module = _load_switch_module() + + def _switch(self, *, is_on: bool): + device = _FakeEVSE(is_on=is_on) + evse = SimpleNamespace(get_by_id=lambda _id: device) + data = SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace(name="WiserHeat045XXX"), + devices=SimpleNamespace(evse=evse), + ) + ) + switch = self.switch_module.WiserEVSESwitch(data, 12) + switch.async_force_update = self._noop_async + return switch, device + + async def _noop_async(self, *_args, **_kwargs) -> None: + return None + + def test_turn_on_returns_control_to_wiser(self) -> None: + switch, device = self._switch(is_on=False) + + asyncio.run(switch.async_turn_on()) + + self.assertEqual(device.commands, ["turn_on"]) + + def test_turn_off_force_disables_charging(self) -> None: + switch, device = self._switch(is_on=True) + + asyncio.run(switch.async_turn_off()) + + self.assertEqual(device.commands, ["turn_off"]) + + def test_uses_physical_device_identity(self) -> None: + switch, _device = self._switch(is_on=True) + + self.assertEqual(switch.unique_id, "physical-entity-id") + self.assertEqual(switch.device_info["model"], "EVSCKT/OUTLET/1") + + if __name__ == "__main__": unittest.main() diff --git a/tests/test_ui_options.py b/tests/test_ui_options.py new file mode 100644 index 00000000..f3c58f7f --- /dev/null +++ b/tests/test_ui_options.py @@ -0,0 +1,769 @@ +"""Exercise UI options and migrations without requiring a running HA instance.""" + +from __future__ import annotations + +import ast +import logging +from pathlib import Path +from types import SimpleNamespace +import unittest +from unittest.mock import AsyncMock, Mock + +COMPONENT = Path(__file__).parents[1] / "custom_components/wiser" + + +def load_functions(filename, names, namespace): + """Execute actual flow/migration methods with minimal HA dependencies.""" + tree = ast.parse((COMPONENT / filename).read_text()) + functions = [ + node for node in ast.walk(tree) + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) + and node.name in names + ] + for node in functions: + node.decorator_list = [] + module = ast.Module( + body=[ + ast.ImportFrom( + module="__future__", + names=[ast.alias(name="annotations")], + level=0, + ), + *functions, + ], + type_ignores=[], + ) + ast.fix_missing_locations(module) + exec(compile(module, filename, "exec", dont_inherit=True), namespace) + return SimpleNamespace(**namespace) + + +def function_call_lines(filename, function_name): + """Return call names and line numbers from a function's syntax tree.""" + source = (COMPONENT / filename).read_text() + try: + tree = ast.parse(source) + except SyntaxError: + # The local test runner can be older than the integration's supported + # Python version. Platform setup precedes the entity classes, so parse + # only that section when later syntax is unsupported by the runner. + tree = ast.parse(source.partition("\nclass ")[0]) + function = next( + node + for node in ast.walk(tree) + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) + and node.name == function_name + ) + calls = [] + for node in ast.walk(function): + if not isinstance(node, ast.Call): + continue + if isinstance(node.func, ast.Name): + calls.append((node.func.id, node.lineno)) + elif isinstance(node.func, ast.Attribute): + calls.append((node.func.attr, node.lineno)) + return calls + + +class UIOptionsTest(unittest.IsolatedAsyncioTestCase): + def setUp(self): + self.namespace = { + "CONF_ADDITIONAL_SENSORS": "additional_sensors", + "CONF_LEGACY_NAMING": "legacy_naming", + "CONF_EQUIPMENT_SENSORS": "equipment_sensors", + "CONF_SCHEDULE_SETPOINT_ROOMS": "schedule_setpoint_rooms", + "CONF_OPENTHERM_EVER_CONNECTED": "opentherm_ever_connected", + "CONF_OPENTHERM_HOT_WATER_CAPABLE": "opentherm_hot_water_capable", + "CONF_OPENTHERM_HOT_WATER_CONTROL": "opentherm_hot_water_control", + "CONF_OPENTHERM_HOT_WATER_SENSOR_ENTITY_ID": "opentherm_hot_water_sensor_entity_id", + "CONF_SHOW_WISER_SIDEBAR": "show_wiser_sidebar", + "CONF_NAME": "name", + "DATA": "data", + "DOMAIN": "wiser", + "HomeAssistant": object, + "ConfigEntry": object, + "FlowResult": dict, + "Any": object, + "_LOGGER": logging.getLogger(__name__), + "validate_input": AsyncMock(return_value={"title": "Wiser", "unique_id": "1"}), + "MAJOR_VERSION": 2026, + "MINOR_VERSION": 8, + "room_has_climate": lambda room: bool(room.devices), + } + + def test_opentherm_detection_uses_live_or_remembered_connection(self): + functions = load_functions( + "opentherm_detection.py", {"opentherm_is_detected"}, self.namespace + ) + disconnected = SimpleNamespace( + enabled=True, connection_status="Disconnected" + ) + connected = SimpleNamespace(enabled=True, connection_status="Connected") + + self.assertFalse( + functions.opentherm_is_detected( + SimpleNamespace(data={}), disconnected + ) + ) + self.assertTrue( + functions.opentherm_is_detected(SimpleNamespace(data={}), connected) + ) + self.assertTrue( + functions.opentherm_is_detected( + SimpleNamespace(data={"opentherm_ever_connected": True}), + disconnected, + ) + ) + self.assertFalse( + functions.opentherm_is_detected(SimpleNamespace(data={}), None) + ) + + def test_first_opentherm_connection_is_remembered_once(self): + sync_registry = Mock() + self.namespace["_sync_opentherm_entity_registry"] = sync_registry + functions = load_functions( + "__init__.py", {"_remember_opentherm_connection"}, self.namespace + ) + update = Mock() + hass = SimpleNamespace( + config_entries=SimpleNamespace(async_update_entry=update) + ) + entry = SimpleNamespace(data={"host": "wiser.local"}) + opentherm = SimpleNamespace( + enabled=True, connection_status="Disconnected" + ) + coordinator = SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace(opentherm=opentherm) + ) + ) + + functions._remember_opentherm_connection(hass, entry, coordinator) + update.assert_not_called() + sync_registry.assert_called_once_with( + hass, entry, coordinator, detected=False + ) + + sync_registry.reset_mock() + functions._remember_opentherm_connection(hass, entry, coordinator) + update.assert_not_called() + sync_registry.assert_not_called() + + opentherm.connection_status = "Connected" + functions._remember_opentherm_connection(hass, entry, coordinator) + update.assert_called_once_with( + entry, + data={"host": "wiser.local", "opentherm_ever_connected": True}, + ) + sync_registry.assert_called_once_with( + hass, entry, coordinator, detected=True + ) + + update.reset_mock() + sync_registry.reset_mock() + entry.data = {"host": "wiser.local", "opentherm_ever_connected": True} + opentherm.connection_status = "Disconnected" + functions._remember_opentherm_connection(hass, entry, coordinator) + update.assert_not_called() + sync_registry.assert_not_called() + + def test_writable_hot_water_capability_is_remembered(self): + sync_registry = Mock() + self.namespace["_sync_opentherm_entity_registry"] = sync_registry + functions = load_functions( + "__init__.py", {"_remember_opentherm_connection"}, self.namespace + ) + update = Mock() + hass = SimpleNamespace( + config_entries=SimpleNamespace(async_update_entry=update) + ) + entry = SimpleNamespace( + data={"opentherm_ever_connected": True} + ) + opentherm = SimpleNamespace( + enabled=True, + connection_status="Connected", + boiler_parameters=SimpleNamespace(hw_setpoint_read_write=True), + ) + coordinator = SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace(opentherm=opentherm) + ) + ) + + functions._remember_opentherm_connection(hass, entry, coordinator) + + update.assert_called_once_with( + entry, + data={ + "opentherm_ever_connected": True, + "opentherm_hot_water_capable": True, + }, + ) + + def test_hot_water_support_survives_capability_data_gap(self): + functions = load_functions( + "opentherm_detection.py", + {"opentherm_hot_water_control_is_supported"}, + self.namespace, + ) + opentherm = SimpleNamespace(boiler_parameters=SimpleNamespace()) + + self.assertTrue( + functions.opentherm_hot_water_control_is_supported( + SimpleNamespace( + data={"opentherm_hot_water_capable": True}, options={} + ), + opentherm, + ) + ) + self.assertTrue( + functions.opentherm_hot_water_control_is_supported( + SimpleNamespace( + data={}, options={"opentherm_hot_water_control": True} + ), + opentherm, + ) + ) + + def test_hot_water_presets_follow_boiler_limits(self): + self.namespace.update( + vol=SimpleNamespace(Invalid=ValueError), + MAX_OPENTHERM_HOT_WATER_PRESETS=10, + DEFAULT_OPENTHERM_HOT_WATER_PRESET_TEMP=55.0, + OPENTHERM_HOT_WATER_DEFAULT_BOOSTS=(15, 30, 45), + ) + functions = load_functions( + "config_flow.py", + { + "_default_opentherm_hot_water_presets", + "_validate_opentherm_hot_water_presets", + }, + self.namespace, + ) + defaults = functions._default_opentherm_hot_water_presets(40, 50) + self.assertTrue( + all(preset["temperature"] == 50 for preset in defaults) + ) + stored = [{ + "name": "Boost", + "temperature": 55, + "duration_minutes": 15, + }] + with self.assertRaises(ValueError): + functions._validate_opentherm_hot_water_presets(stored, 40, 50) + clamped = functions._validate_opentherm_hot_water_presets( + stored, 40, 50, clamp_temperature=True + ) + self.assertEqual(clamped[0]["temperature"], 50) + + def test_disabling_opentherm_clears_remembered_connection(self): + sync_registry = Mock() + self.namespace["_sync_opentherm_entity_registry"] = sync_registry + functions = load_functions( + "__init__.py", {"_remember_opentherm_connection"}, self.namespace + ) + update = Mock() + hass = SimpleNamespace( + config_entries=SimpleNamespace(async_update_entry=update) + ) + entry = SimpleNamespace( + data={ + "host": "wiser.local", + "opentherm_ever_connected": True, + } + ) + coordinator = SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace( + opentherm=SimpleNamespace( + enabled=False, + connection_status="Disconnected", + ) + ) + ) + ) + + functions._remember_opentherm_connection(hass, entry, coordinator) + + update.assert_called_once_with( + entry, + data={"host": "wiser.local"}, + ) + sync_registry.assert_called_once_with( + hass, entry, coordinator, detected=False + ) + + def test_missing_system_preserves_remembered_connection(self): + sync_registry = Mock() + self.namespace["_sync_opentherm_entity_registry"] = sync_registry + functions = load_functions( + "__init__.py", {"_remember_opentherm_connection"}, self.namespace + ) + update = Mock() + hass = SimpleNamespace( + config_entries=SimpleNamespace(async_update_entry=update) + ) + entry = SimpleNamespace( + data={ + "host": "wiser.local", + "opentherm_ever_connected": True, + } + ) + coordinator = SimpleNamespace(wiserhub=SimpleNamespace(system=None)) + + functions._remember_opentherm_connection(hass, entry, coordinator) + + update.assert_not_called() + sync_registry.assert_not_called() + + def test_opentherm_registry_entries_are_disabled_without_detection(self): + integration = "integration" + registry = SimpleNamespace(async_update_entity=Mock()) + entries = [ + SimpleNamespace( + domain="sensor", + unique_id="flow", + entity_id="sensor.boiler_flow_temperature", + disabled_by=None, + ), + SimpleNamespace( + domain="sensor", + unique_id="return", + entity_id="sensor.boiler_return_temperature", + disabled_by="user", + ), + SimpleNamespace( + domain="sensor", + unique_id="cloud", + entity_id="sensor.cloud", + disabled_by=None, + ), + ] + fake_er = SimpleNamespace( + RegistryEntryDisabler=SimpleNamespace(INTEGRATION=integration), + async_get=lambda _hass: registry, + async_entries_for_config_entry=lambda _registry, _entry_id: entries, + ) + self.namespace.update( + er=fake_er, + opentherm_entity_unique_ids=lambda _data: { + ("sensor", "flow"), + ("sensor", "return"), + }, + opentherm_is_detected=lambda entry, opentherm: bool( + entry.data.get("opentherm_ever_connected") + or opentherm.connection_status == "Connected" + ), + ) + functions = load_functions( + "__init__.py", {"_sync_opentherm_entity_registry"}, self.namespace + ) + config_entry = SimpleNamespace(entry_id="entry", data={}) + + functions._sync_opentherm_entity_registry( + object(), + config_entry, + SimpleNamespace(wiserhub=SimpleNamespace(system=None)), + ) + registry.async_update_entity.assert_not_called() + + coordinator = SimpleNamespace( + wiserhub=SimpleNamespace( + system=SimpleNamespace( + opentherm=SimpleNamespace(connection_status="Disconnected") + ) + ) + ) + + functions._sync_opentherm_entity_registry( + object(), config_entry, coordinator + ) + registry.async_update_entity.assert_called_once_with( + "sensor.boiler_flow_temperature", disabled_by=integration + ) + + registry.async_update_entity.reset_mock() + entries[0].disabled_by = integration + config_entry.data["opentherm_ever_connected"] = True + functions._sync_opentherm_entity_registry( + object(), config_entry, coordinator + ) + registry.async_update_entity.assert_called_once_with( + "sensor.boiler_flow_temperature", disabled_by=None + ) + + def test_options_hide_opentherm_until_detected(self): + detection = load_functions( + "opentherm_detection.py", {"opentherm_is_detected"}, self.namespace + ) + self.namespace["opentherm_is_detected"] = detection.opentherm_is_detected + self.namespace.update(DATA="data", DOMAIN="wiser") + functions = load_functions("config_flow.py", {"_opentherm"}, self.namespace) + opentherm = SimpleNamespace( + enabled=True, connection_status="Disconnected" + ) + coordinator = SimpleNamespace( + wiserhub=SimpleNamespace(system=SimpleNamespace(opentherm=opentherm)) + ) + flow = SimpleNamespace( + config_entry=SimpleNamespace(entry_id="entry", data={}), + hass=SimpleNamespace(data={"wiser": {"entry": {"data": coordinator}}}), + ) + + self.assertIsNone(functions._opentherm(flow)) + flow.config_entry.data["opentherm_ever_connected"] = True + self.assertIs(functions._opentherm(flow), opentherm) + + def test_sensor_setup_requires_detected_opentherm(self): + for filename in ("sensor.py", "binary_sensor.py"): + with self.subTest(filename=filename): + calls = function_call_lines(filename, "async_setup_entry") + self.assertIn("opentherm_is_detected", {name for name, _ in calls}) + + def test_detection_precedes_listener_and_reload_snapshot(self): + calls = function_call_lines("__init__.py", "async_setup_entry") + call_lines = {name: line for name, line in calls} + + self.assertLess( + call_lines["_remember_opentherm_connection"], + call_lines["add_update_listener"], + ) + self.assertLess( + call_lines["add_update_listener"], + call_lines["integration_reload_settings"], + ) + + async def test_new_manual_and_discovered_install_defaults(self): + for method in ("async_step_user", "async_step_zeroconf_confirm"): + for version, expected in (((2025, 12), True), ((2026, 7), True), + ((2026, 8), False), ((2026, 9), False), + ((2027, 1), False)): + with self.subTest(method=method, version=version): + self.namespace.update(MAJOR_VERSION=version[0], MINOR_VERSION=version[1]) + functions = load_functions("config_flow.py", {method}, self.namespace) + flow = SimpleNamespace( + hass=object(), async_set_unique_id=AsyncMock(), + _abort_if_unique_id_configured=Mock(), + async_create_entry=lambda **kwargs: kwargs, + ) + result = await getattr(functions, method)(flow, {"host": "test"}) + self.assertEqual(result["options"], {"legacy_naming": expected}) + + async def test_migration_enables_legacy_and_preserves_other_options(self): + functions = load_functions("__init__.py", {"async_migrate_entry"}, self.namespace) + for options, expected in (({"scan_interval": 30}, True), + ({"legacy_naming": False}, False)): + entry = SimpleNamespace(version=1, minor_version=4, options=options) + update = Mock() + hass = SimpleNamespace(config_entries=SimpleNamespace(async_update_entry=update)) + self.assertTrue(await functions.async_migrate_entry(hass, entry)) + saved = update.call_args.kwargs + self.assertEqual( + saved["options"], + options | {"legacy_naming": expected}, + ) + self.assertEqual(saved["minor_version"], 5) + + async def test_menu_available_without_opentherm(self): + functions = load_functions("config_flow.py", {"async_step_init"}, self.namespace) + flow = SimpleNamespace( + _opentherm=lambda: None, + _equipment_available=lambda: False, + config_entry=SimpleNamespace(options={}), + async_show_menu=lambda **kw: kw, + ) + result = await functions.async_step_init(flow) + self.assertIn("ui_options", result["menu_options"]) + self.assertNotIn("opentherm", result["menu_options"]) + self.assertNotIn("equipment_sensors", result["menu_options"]) + self.assertNotIn("additional_sensors", result["menu_options"]) + + async def test_additional_sensors_is_section_in_automation_parameters(self): + source = (COMPONENT / "config_flow.py").read_text() + tree = ast.parse(source) + function = next( + node + for node in ast.walk(tree) + if isinstance(node, ast.AsyncFunctionDef) + and node.name == "async_step_automation_params" + ) + self.assertIn( + "vol.Required(CONF_ADDITIONAL_SENSORS)", ast.unparse(function) + ) + self.assertNotIn("async_step_additional_sensors", source) + + def test_room_choices_exclude_rooms_with_climate(self): + functions = load_functions( + "config_flow.py", {"_rooms_without_climate"}, self.namespace + ) + kitchen = SimpleNamespace(id=4, name="Kitchen", devices=[]) + lounge = SimpleNamespace(id=5, name="Lounge", devices=[object()]) + flow = SimpleNamespace( + hass=SimpleNamespace( + data={ + "wiser": { + "entry": { + "data": SimpleNamespace( + wiserhub=SimpleNamespace( + rooms=SimpleNamespace(all=[kitchen, lounge]) + ) + ) + } + } + } + ), + config_entry=SimpleNamespace(entry_id="entry"), + ) + + self.assertEqual(functions._rooms_without_climate(flow), [kitchen]) + + async def test_additional_sensor_section_preserves_other_options(self): + functions = load_functions( + "config_flow.py", {"async_step_automation_params"}, self.namespace + ) + flow = SimpleNamespace( + config_entry=SimpleNamespace(options={"scan_interval": 30}), + async_create_entry=lambda **kwargs: kwargs, + ) + + saved = await functions.async_step_automation_params( + flow, + { + "additional_sensors": { + "schedule_setpoint_rooms": ["4"] + } + }, + ) + self.assertEqual( + saved["data"], + { + "scan_interval": 30, + "additional_sensors": { + "schedule_setpoint_rooms": ["4"] + }, + }, + ) + + async def test_equipment_menu_is_below_opentherm(self): + functions = load_functions( + "config_flow.py", {"async_step_init"}, self.namespace + ) + flow = SimpleNamespace( + _opentherm=lambda: object(), + _equipment_available=lambda: True, + config_entry=SimpleNamespace(options={}), + async_show_menu=lambda **kw: kw, + ) + result = await functions.async_step_init(flow) + self.assertLess( + result["menu_options"].index("opentherm"), + result["menu_options"].index("equipment_sensors"), + ) + + async def test_opentherm_menu_groups_control_and_sensors(self): + self.namespace["opentherm_hot_water_control_is_supported"] = ( + lambda entry, opentherm: True + ) + functions = load_functions( + "config_flow.py", {"async_step_opentherm"}, self.namespace + ) + flow = SimpleNamespace( + _opentherm=lambda: object(), + config_entry=SimpleNamespace(), + async_show_menu=lambda **kwargs: kwargs, + ) + result = await functions.async_step_opentherm(flow) + self.assertEqual(result["step_id"], "opentherm") + self.assertEqual( + result["menu_options"], + ["opentherm_hot_water_control", "opentherm_sensors"], + ) + + async def test_opentherm_menu_hides_unsupported_hot_water_control(self): + self.namespace["opentherm_hot_water_control_is_supported"] = ( + lambda entry, opentherm: False + ) + functions = load_functions( + "config_flow.py", {"async_step_opentherm"}, self.namespace + ) + flow = SimpleNamespace( + _opentherm=lambda: object(), + config_entry=SimpleNamespace(), + async_show_menu=lambda **kwargs: kwargs, + ) + result = await functions.async_step_opentherm(flow) + self.assertEqual(result["menu_options"], ["opentherm_sensors"]) + + async def test_equipment_menu_does_not_require_opentherm(self): + functions = load_functions( + "config_flow.py", {"async_step_init"}, self.namespace + ) + flow = SimpleNamespace( + _opentherm=lambda: None, + _equipment_available=lambda: True, + config_entry=SimpleNamespace(options={}), + async_show_menu=lambda **kw: kw, + ) + result = await functions.async_step_init(flow) + self.assertIn("equipment_sensors", result["menu_options"]) + self.assertNotIn("opentherm", result["menu_options"]) + + async def test_enabled_equipment_option_remains_available_during_data_gap(self): + self.namespace.update( + vol=SimpleNamespace( + Optional=lambda key, default: (key, default), + Schema=lambda data: data, + ), + BooleanSelector=lambda: bool, + ) + functions = load_functions( + "config_flow.py", + {"async_step_init", "async_step_equipment_sensors"}, + self.namespace, + ) + flow = SimpleNamespace( + _opentherm=lambda: None, + _equipment_available=lambda: False, + config_entry=SimpleNamespace(options={"equipment_sensors": True}), + async_show_menu=lambda **kwargs: kwargs, + async_show_form=lambda **kwargs: kwargs, + async_create_entry=lambda **kwargs: kwargs, + ) + + menu = await functions.async_step_init(flow) + self.assertIn("equipment_sensors", menu["menu_options"]) + + form = await functions.async_step_equipment_sensors(flow) + self.assertIn(("equipment_sensors", True), form["data_schema"]) + + saved = await functions.async_step_equipment_sensors( + flow, {"equipment_sensors": False} + ) + self.assertEqual(saved["data"], {"equipment_sensors": False}) + + async def test_unconfigured_equipment_option_aborts_without_data(self): + functions = load_functions( + "config_flow.py", {"async_step_equipment_sensors"}, self.namespace + ) + flow = SimpleNamespace( + _equipment_available=lambda: False, + config_entry=SimpleNamespace(options={}), + async_abort=lambda **kwargs: kwargs, + ) + + result = await functions.async_step_equipment_sensors(flow) + self.assertEqual(result["reason"], "equipment_not_available") + + def test_equipment_menu_requires_a_compatible_device(self): + functions = load_functions( + "config_flow.py", {"_equipment_available"}, self.namespace + ) + + def flow_with_devices( + *, smartplugs=(), power_tags=(), power_tags_c=(), actuators=() + ): + devices = SimpleNamespace( + smartplugs=SimpleNamespace(all=smartplugs), + power_tags=SimpleNamespace(all=power_tags), + power_tags_c=SimpleNamespace(all=power_tags_c), + heating_actuators=SimpleNamespace(all=actuators), + ) + return SimpleNamespace( + config_entry=SimpleNamespace(entry_id="entry"), + hass=SimpleNamespace( + data={ + "wiser": { + "entry": { + "data": SimpleNamespace( + wiserhub=SimpleNamespace(devices=devices) + ) + } + } + } + ), + ) + + self.assertFalse(functions._equipment_available(flow_with_devices())) + self.assertTrue( + functions._equipment_available( + flow_with_devices( + power_tags=(SimpleNamespace(equipment=object()),) + ) + ) + ) + self.assertFalse( + functions._equipment_available( + flow_with_devices( + smartplugs=(SimpleNamespace(equipment=None),), + actuators=(SimpleNamespace(equipment=None),), + ) + ) + ) + self.assertTrue( + functions._equipment_available( + flow_with_devices( + power_tags_c=(SimpleNamespace(equipment=object()),) + ) + ) + ) + + async def test_equipment_option_defaults_off_and_preserves_options(self): + self.namespace.update( + vol=SimpleNamespace( + Optional=lambda key, default: (key, default), + Schema=lambda data: data, + ), + BooleanSelector=lambda: bool, + ) + functions = load_functions( + "config_flow.py", {"async_step_equipment_sensors"}, self.namespace + ) + flow = SimpleNamespace( + _equipment_available=lambda: True, + config_entry=SimpleNamespace(options={"scan_interval": 30}), + async_show_form=lambda **kwargs: kwargs, + async_create_entry=lambda **kwargs: kwargs, + ) + form = await functions.async_step_equipment_sensors(flow) + self.assertIn(("equipment_sensors", False), form["data_schema"]) + + saved = await functions.async_step_equipment_sensors( + flow, {"equipment_sensors": True} + ) + self.assertEqual( + saved["data"], + {"scan_interval": 30, "equipment_sensors": True}, + ) + + async def test_saving_ui_options_preserves_other_options(self): + functions = load_functions("config_flow.py", {"async_step_ui_options"}, self.namespace) + for enabled in (False, True): + flow = SimpleNamespace( + config_entry=SimpleNamespace(options={"scan_interval": 30}), + async_create_entry=lambda **kwargs: kwargs, + ) + preferences = { + "legacy_naming": enabled, + "show_wiser_sidebar": enabled, + } + result = await functions.async_step_ui_options(flow, preferences) + self.assertEqual(result["data"], {"scan_interval": 30} | preferences) + + async def test_form_uses_saved_preference(self): + self.namespace.update( + vol=SimpleNamespace(Optional=lambda key, default: (key, default), Schema=lambda data: data), + BooleanSelector=lambda: bool, + ) + functions = load_functions("config_flow.py", {"async_step_ui_options"}, self.namespace) + for enabled in (False, True): + flow = SimpleNamespace( + config_entry=SimpleNamespace(options={ + "legacy_naming": enabled, + **({} if enabled else {"show_wiser_sidebar": False}), + }), + async_show_form=lambda **kwargs: kwargs, + ) + result = await functions.async_step_ui_options(flow) + self.assertIn(("legacy_naming", enabled), result["data_schema"]) + self.assertIn(("show_wiser_sidebar", enabled), result["data_schema"]) diff --git a/tests/test_wiser_sidebar.py b/tests/test_wiser_sidebar.py new file mode 100644 index 00000000..2db8926b --- /dev/null +++ b/tests/test_wiser_sidebar.py @@ -0,0 +1,454 @@ +"""Test the shared Wiser sidebar and automatic panel discovery.""" + +from __future__ import annotations + +import importlib +import json +from pathlib import Path +import sys +from tempfile import TemporaryDirectory +from types import ModuleType, SimpleNamespace +import unittest +from unittest.mock import AsyncMock, Mock, patch + + +ROOT = Path(__file__).resolve().parents[1] / "custom_components/wiser" + + +class WiserSidebarTest(unittest.IsolatedAsyncioTestCase): + def setUp(self): + self.temporary = TemporaryDirectory() + self.addCleanup(self.temporary.cleanup) + assets = Path(self.temporary.name) + (assets / "wiser-schedule-card.js").write_text( + 'customElements.define("wiser-schedules-panel", class extends HTMLElement {})' + ) + (assets / "wiser-zigbee-card.js").write_text( + 'customElements.define("wiser-zigbee-panel", class extends HTMLElement {})' + ) + (assets / "wiser-rooms-card.js").write_text( + 'customElements.define("wiser-rooms-card", class extends HTMLElement {});' + 'customElements.define("wiser-rooms-panel", class extends HTMLElement {})' + ) + + self.frontend = ModuleType("homeassistant.components.frontend") + self.frontend.async_remove_panel = Mock() + self.frontend.async_register_built_in_panel = Mock() + self.custom = ModuleType("homeassistant.components.panel_custom") + self.custom.async_register_panel = AsyncMock() + components = ModuleType("homeassistant.components") + components.frontend = self.frontend + components.panel_custom = self.custom + + package = ModuleType("wiser_sidebar_test.frontend") + package.__path__ = [str(ROOT / "frontend")] + package.card_version = Mock(return_value="1.0.0") + + async def card_resource(_hass, filename): + path = assets / filename + return path, f"/wiser/{filename}?v=1", "1" + + package.async_card_resource = card_resource + constants = ModuleType("wiser_sidebar_test.const") + for name, value in { + "CONF_SHOW_WISER_SIDEBAR": "show_wiser_sidebar", + "CONF_WISER_PANEL_CONFIG": "wiser_panel_config", + "DATA": "data", + "DOMAIN": "wiser", + "URL_BASE": "/wiser", + }.items(): + setattr(constants, name, value) + constants.CARD_MANIFEST = json.loads( + (Path(__file__).parent / "fixtures/frontend_cards.json").read_text() + ) + + registry = ModuleType("wiser_sidebar_test.frontend.registry") + registry.get_manifest = lambda hass: constants.CARD_MANIFEST + self.manifest = constants.CARD_MANIFEST + + with patch.dict( + sys.modules, + { + "homeassistant": ModuleType("homeassistant"), + "homeassistant.components": components, + "wiser_sidebar_test": ModuleType("wiser_sidebar_test"), + "wiser_sidebar_test.frontend": package, + "wiser_sidebar_test.frontend.registry": registry, + "wiser_sidebar_test.const": constants, + }, + ): + self.sidebar = importlib.import_module( + "wiser_sidebar_test.frontend.wiser_sidebar" + ) + self.entry_updates = importlib.import_module( + "wiser_sidebar_test.frontend.entry_updates" + ) + + self.entries = [] + self.hass = SimpleNamespace( + data={"wiser": {}}, + async_add_executor_job=AsyncMock(side_effect=lambda fn, *args: fn(*args)), + config_entries=SimpleNamespace(async_entries=lambda _domain: self.entries), + ) + + def add_hub(self, name, options, loaded=True, disabled=False): + entry = SimpleNamespace( + entry_id=name, + disabled_by=disabled, + options=dict(options), + ) + self.entries.append(entry) + if loaded: + self.hass.data["wiser"][name] = { + "data": SimpleNamespace( + wiserhub=SimpleNamespace(system=SimpleNamespace(name=name)) + ) + } + return entry + + async def test_single_option_registers_one_panel_with_discovered_panel_cards(self): + self.add_hub( + "hub-one", + { + "show_wiser_sidebar": True, + "wiser_panel_config": { + "schedule": {"home_screen": "overview"}, + "zigbee": {"orientation": "pie"}, + }, + }, + ) + self.add_hub("hub-two", {"show_wiser_sidebar": True}) + await self.sidebar.async_update_wiser_panel(self.hass) + + self.custom.async_register_panel.assert_awaited_once() + args = self.custom.async_register_panel.call_args.kwargs + self.assertEqual(args["frontend_url_path"], "wiser-panel") + self.assertEqual(args["webcomponent_name"], "wiser-panel") + self.assertEqual(args["sidebar_icon"], "wiser:wiser") + self.assertEqual( + [panel["id"] for panel in args["config"]["panels"]], + ["schedule", "zigbee", "rooms"], + ) + schedule, zigbee, rooms = args["config"]["panels"] + self.assertEqual(schedule["default_title"], "Schedules") + self.assertEqual(rooms["default_title"], "Rooms") + self.assertEqual(schedule["config"]["hubs"], ["hub-one", "hub-two"]) + self.assertEqual( + rooms["config"]["hub_ids"], + {"hub-one": "hub-one", "hub-two": "hub-two"}, + ) + self.assertEqual( + schedule["config"]["card_configs"]["hub-one"], + {"home_screen": "overview"}, + ) + self.assertEqual( + zigbee["config"]["card_configs"]["hub-one"], + {"orientation": "pie"}, + ) + + async def test_panel_route_stays_outside_static_resources_on_register_and_update(self): + self.add_hub("hub", {}) + await self.sidebar.async_update_wiser_panel(self.hass) + registered = self.custom.async_register_panel.call_args.kwargs + route = "/" + registered["frontend_url_path"] + static_root = self.sidebar.URL_BASE.rstrip("/") + self.assertNotEqual(route, static_root) + self.assertFalse(route.startswith(static_root + "/")) + self.assertTrue(registered["module_url"].startswith(static_root + "/")) + + # Changing enabled hubs refreshes the same UI route, with static URLs intact. + self.add_hub("another-hub", {}) + await self.sidebar.async_update_wiser_panel(self.hass) + updated = self.frontend.async_register_built_in_panel.call_args.kwargs + self.assertEqual(updated["frontend_url_path"], registered["frontend_url_path"]) + self.assertTrue(updated["update"]) + self.assertEqual( + updated["config"]["_panel_custom"]["module_url"], registered["module_url"] + ) + + async def test_sidebar_is_enabled_by_default(self): + self.add_hub("hub", {}) + await self.sidebar.async_update_wiser_panel(self.hass) + self.custom.async_register_panel.assert_awaited_once() + + async def test_saved_tab_order_and_titles_are_applied_to_discovered_panels(self): + self.add_hub( + "hub", + { + "wiser_panel_config": { + "_panel_tabs": [ + {"id": "rooms", "title": "Heating"}, + {"id": "schedule", "title": "Programmes"}, + ] + } + }, + ) + await self.sidebar.async_update_wiser_panel(self.hass) + + panels = self.custom.async_register_panel.call_args.kwargs["config"]["panels"] + self.assertEqual(panels[0]["default_title"], "Rooms") + self.assertEqual( + [(panel["id"], panel["title"]) for panel in panels], + [ + ("rooms", "Heating"), + ("schedule", "Programmes"), + ("zigbee", "Zigbee"), + ], + ) + + async def test_renamed_panel_replaces_stored_legacy_default_title(self): + rooms = next(card for card in self.manifest if card["id"] == "rooms") + rooms.update( + { + "filename": "wiser-controls-card.js", + "component": "wiser-controls-card", + "panel": "wiser-controls-panel", + "legacy_filenames": ["wiser-rooms-card.js"], + } + ) + (Path(self.temporary.name) / "wiser-controls-card.js").write_text( + 'customElements.define("wiser-controls-card", class extends HTMLElement {});' + 'customElements.define("wiser-controls-panel", class extends HTMLElement {})' + ) + self.add_hub( + "hub", + { + "wiser_panel_config": { + "_panel_tabs": [ + {"id": "rooms", "title": "Rooms"}, + {"id": "schedule", "title": "Programmes"}, + ] + } + }, + ) + + await self.sidebar.async_update_wiser_panel(self.hass) + + panels = self.custom.async_register_panel.call_args.kwargs["config"]["panels"] + self.assertEqual(panels[0]["default_title"], "Controls") + self.assertEqual( + [(panel["id"], panel["title"]) for panel in panels], + [ + ("rooms", "Controls"), + ("schedule", "Programmes"), + ("zigbee", "Zigbee"), + ], + ) + + async def test_legacy_sidebar_options_do_not_control_unified_panel(self): + self.add_hub( + "hub", + {"show_schedules_sidebar": False, "show_zigbee_sidebar": False}, + ) + await self.sidebar.async_update_wiser_panel(self.hass) + self.custom.async_register_panel.assert_awaited_once() + + async def test_unified_setting_can_hide_sidebar(self): + self.add_hub("hub", {"show_wiser_sidebar": False}) + await self.sidebar.async_update_wiser_panel(self.hass) + self.custom.async_register_panel.assert_not_awaited() + + async def test_no_enabled_hubs_removes_existing_shared_panel(self): + self.hass.data[self.sidebar.PANEL_STATE] = {"panels": []} + self.add_hub("hub", {"show_wiser_sidebar": False}) + await self.sidebar.async_update_wiser_panel(self.hass) + self.frontend.async_remove_panel.assert_called_once_with(self.hass, "wiser-panel") + self.assertNotIn(self.sidebar.PANEL_STATE, self.hass.data) + + def test_shell_owns_panel_tabs_and_embeds_each_child_panel(self): + source = (ROOT / "frontend/wiser-panel.js").read_text() + self.assertIn('id="panel-tabs"', source) + self.assertIn('sessionStorage.getItem(WISER_ACTIVE_PANEL_KEY)', source) + self.assertIn('sessionStorage.setItem(WISER_ACTIVE_PANEL_KEY, id)', source) + self.assertIn('id="settings"', source) + self.assertIn('src="/wiser/wiser-logo.png"', source) + self.assertIn('alt="Wiser"', source) + self.assertNotIn("

Wiser

", source) + self.assertIn('view.setAttribute("nested", "")', source) + self.assertIn("panel.module_url", source) + self.assertLess( + source.index("view.hass = this._hass"), + source.index("view.panel = { config: panel.config }"), + ) + self.assertIn('tab.addEventListener("dragstart"', source) + self.assertIn('tab.addEventListener("dblclick"', source) + self.assertIn('type: "wiser/panel/configure_tabs"', source) + self.assertIn("_mergeTabPreferences(panel.config)", source) + self.assertIn( + "input.value.trim() || panel.default_title || panel.title", source + ) + + def test_new_panel_websocket_commands_are_registered(self): + source = (ROOT / "websockets.py").read_text() + self.assertIn("async_register_command(hass, websocket_configure_panel)", source) + self.assertNotIn("websocket_configure_rooms_panel", source) + self.assertIn( + "async_register_command(hass, websocket_configure_wiser_panel_tabs)", + source, + ) + + async def test_new_registry_panel_is_discovered_and_saves_generic_settings(self): + entry = self.add_hub("hub", {}) + self.manifest.append({ + "id": "new-registry-id", + "filename": "wiser-energy-card.js", "name": "Wiser Energy Card", + "panel": "wiser-energy-panel", + }) + (Path(self.temporary.name) / "wiser-energy-card.js").write_text( + 'customElements.define("wiser-energy-panel", class extends HTMLElement {})' + ) + await self.sidebar.async_update_wiser_panel(self.hass) + panels = self.custom.async_register_panel.call_args.kwargs["config"]["panels"] + self.assertEqual(panels[-1]["id"], "new-registry-id") + self.assertEqual(panels[-1]["config"]["panel_id"], "new-registry-id") + self.hass.config_entries.async_update_entry = Mock() + self.sidebar.save_panel_config(self.hass, "new-registry-id", {"hub": {"columns": 3}}) + self.hass.config_entries.async_update_entry.assert_called_once_with( + entry, options={"wiser_panel_config": {"new-registry-id": {"columns": 3}}} + ) + self.sidebar.save_wiser_panel_tabs(self.hass, [{"id": "new-registry-id", "title": "Energy"}]) + self.hass.config_entries.async_update_entry.reset_mock() + with self.assertRaises(ValueError): + self.sidebar.save_panel_config(self.hass, "unknown", {"hub": {}}) + self.hass.config_entries.async_update_entry.assert_not_called() + + def test_panel_settings_save_without_standalone_sidebar_modules(self): + self.add_hub("schedule-hub", {}) + self.add_hub("zigbee-hub", {}) + self.hass.config_entries.async_update_entry = Mock() + self.sidebar.save_panel_config( + self.hass, "schedule", + {"schedule-hub": {"type": "custom:wiser-schedule-card", "home_screen": "overview"}}, + ) + self.sidebar.save_panel_config( + self.hass, "zigbee", + {"zigbee-hub": {"hub": "zigbee-hub", "orientation": "pie"}}, + ) + calls = self.hass.config_entries.async_update_entry.call_args_list + self.assertEqual( + calls[0].kwargs["options"], + {"wiser_panel_config": {"schedule": {"home_screen": "overview"}}}, + ) + self.assertEqual( + calls[1].kwargs["options"], + {"wiser_panel_config": {"zigbee": {"orientation": "pie"}}}, + ) + + async def test_all_panel_settings_apply_without_reloading_integration(self): + entry = self.add_hub("hub", {"scan_interval": 30}) + entry.data = {"host": "wiser.local"} + loaded = self.hass.data["wiser"][entry.entry_id] + loaded["reload_settings"] = self.entry_updates.integration_reload_settings(entry) + self.hass.config_entries.async_reload = AsyncMock() + with patch.object(self.entry_updates, "async_update_wiser_panel", AsyncMock()) as refresh: + for panel_id in ("schedule", "zigbee", "rooms", "future-panel"): + entry.options["wiser_panel_config"] = {panel_id: {"columns": 3}} + await self.entry_updates.async_handle_entry_update(self.hass, entry) + self.assertEqual(refresh.await_count, 4) + self.hass.config_entries.async_reload.assert_not_awaited() + entry.options["scan_interval"] = 60 + await self.entry_updates.async_handle_entry_update(self.hass, entry) + self.hass.config_entries.async_reload.assert_awaited_once_with(entry.entry_id) + + async def test_opentherm_detection_change_reloads_once(self): + entry = self.add_hub("hub", {}) + entry.data = {"host": "wiser.local"} + loaded = self.hass.data["wiser"][entry.entry_id] + loaded["reload_settings"] = self.entry_updates.integration_reload_settings(entry) + self.hass.config_entries.async_reload = AsyncMock() + + entry.data["opentherm_ever_connected"] = True + await self.entry_updates.async_handle_entry_update(self.hass, entry) + self.hass.config_entries.async_reload.assert_awaited_once_with(entry.entry_id) + + self.hass.config_entries.async_reload.reset_mock() + loaded["reload_settings"] = self.entry_updates.integration_reload_settings(entry) + with patch.object( + self.entry_updates, "async_update_wiser_panel", AsyncMock() + ) as refresh: + await self.entry_updates.async_handle_entry_update(self.hass, entry) + self.hass.config_entries.async_reload.assert_not_awaited() + refresh.assert_awaited_once_with(self.hass) + + def test_invalid_panel_settings_do_not_partially_save(self): + self.add_hub("first", {}) + self.add_hub("second", {}) + self.hass.config_entries.async_update_entry = Mock() + with self.assertRaises(ValueError): + self.sidebar.save_panel_config( + self.hass, "schedule", + {"first": {"home_screen": "overview"}, "second": {"bad": float("nan")}}, + ) + self.hass.config_entries.async_update_entry.assert_not_called() + + def test_rooms_panel_settings_use_generic_registry_and_drop_hub_filter(self): + entry = self.add_hub( + "hub", + {"wiser_panel_config": {"future": {"enabled": True}}}, + ) + self.hass.config_entries.async_update_entry = Mock() + self.sidebar.save_panel_config( + self.hass, "rooms", + { + "hub": { + "type": "custom:wiser-rooms-card", + "hubs": ["entry"], + "_panel_hide_title": True, + "room_columns": 4, + } + }, + ) + self.hass.config_entries.async_update_entry.assert_called_once_with( + entry, + options={ + "wiser_panel_config": { + "future": {"enabled": True}, + "rooms": {"room_columns": 4}, + } + }, + ) + + def test_panel_tab_layout_is_saved_for_every_enabled_hub(self): + first = self.add_hub( + "first", + {"wiser_panel_config": {"rooms": {"room_columns": 4}}}, + ) + second = self.add_hub("second", {}) + self.hass.config_entries.async_update_entry = Mock() + + tabs = [ + {"id": "rooms", "title": "Heating"}, + {"id": "schedule", "title": "Schedules"}, + {"id": "zigbee", "title": "Devices"}, + ] + self.sidebar.save_wiser_panel_tabs(self.hass, tabs) + + calls = self.hass.config_entries.async_update_entry.call_args_list + self.assertEqual([call.args[0] for call in calls], [first, second]) + self.assertEqual( + calls[0].kwargs["options"]["wiser_panel_config"], + { + "rooms": {"room_columns": 4}, + "_panel_tabs": tabs, + }, + ) + self.assertEqual( + calls[1].kwargs["options"]["wiser_panel_config"], + {"_panel_tabs": tabs}, + ) + + def test_invalid_or_duplicate_panel_tabs_are_rejected(self): + self.add_hub("hub", {}) + self.hass.config_entries.async_update_entry = Mock() + + invalid_tabs = [ + {"id": "rooms", "title": "Heating"}, + {"id": "rooms", "title": "Duplicate"}, + ] + with self.assertRaises(ValueError): + self.sidebar.save_wiser_panel_tabs(self.hass, invalid_tabs) + self.hass.config_entries.async_update_entry.assert_not_called() + + +if __name__ == "__main__": + unittest.main()