From d172ead1c1f56cfafaf08601d2ebb922009a6593 Mon Sep 17 00:00:00 2001 From: David Kebler Date: Tue, 8 Dec 2020 19:58:32 -0800 Subject: [PATCH] reorganization of packages and lovelace - using storage mode with yaml dashboards (working before upgrade to 118.5 which failed) --- .HA_VERSION | 2 +- .gitignore | 6 +- .gitsecret/keys/pubring.kbx | Bin 0 -> 2009 bytes .gitsecret/keys/pubring.kbx~ | Bin 0 -> 32 bytes .gitsecret/keys/trustdb.gpg | Bin 0 -> 1200 bytes .gitsecret/paths/mapping.cfg | 1 + .storage/auth | 1073 + .storage/auth_provider.homeassistant | 24 + .storage/core.area_registry | 20 + .storage/core.config | 14 + .storage/core.config_entries | 221 + .storage/core.device_registry | 177 + .storage/core.entity_registry | 1776 ++ .storage/core.restore_state | 1805 ++ .storage/core.uuid | 7 + .../esphome.28ebec0e77234c51bb89a4a1b26e47b3 | 39 + .../esphome.9436e71647e64581a19a2cc8172f9a35 | 39 + .../esphome.cf5266d3fa4a4824850935818193ac48 | 39 + .../esphome.eeb6ca4d62944fa4b32711e291ba2f63 | 39 + ...user_data_99685dc45d9f40dc8183e11ce5128038 | 9 + .storage/frontend_theme | 8 + .storage/hacs.critical | 12 + .storage/hacs.hacs | 9 + .storage/hacs.repositories | 21929 ++++++++++++++++ .storage/hacs/146194325.hacs | 59 + .storage/hacs/162468030.hacs | 54 + .storage/hacs/172733314.hacs | 63 + .storage/hacs/260526528.hacs | 47 + .storage/hacs/261262884.hacs | 59 + .storage/hassio | 7 + .storage/http | 13 + .storage/lovelace.lovelace_learning | 209 + .storage/lovelace.system_setup | 91 + .storage/lovelace_dashboards | 24 + .storage/lovelace_resources | 23 + .storage/mobile_app | 530 + .storage/onboarding | 11 + .storage/person | 19 + .storage/zone | 7 + .vscode/settings.json | 5 + {configs => archive/configs}/http.yaml | 0 {configs => archive/configs}/mqtt.yaml | 1 + {configs => archive/configs}/panel.yaml | 0 {configs => archive/configs}/scripts.yaml | 0 archive/lovelace/bak.irrigation.yaml. | 18 + archive/lovelace/button-card.yaml.j2 | 8 + {lovelace => archive/lovelace}/cameras.yaml | 0 {lovelace => archive/lovelace}/fio.yaml | 0 .../gios.yaml => archive/lovelace/gpios.yaml | 2 +- .../lovelace/irrigation-buttons-old.yaml.j2 | 34 + archive/lovelace/irrigation-buttons.yaml.j2 | 34 + archive/lovelace/irrigation.yaml.j2 | 70 + archive/lovelace/irrigation.yaml.j2.old | 19 + archive/lovelace/irrigation0.yaml.j2 | 30 + archive/lovelace/layout-testing.yaml | 29 + archive/lovelace/layout.yaml | 58 + archive/lovelace/node-red-scheduler.yaml | 6 + .../lovelace}/node_testing.yaml | 0 {lovelace => archive/lovelace}/scheduler.yaml | 3 + {lovelace => archive/lovelace}/timer.yaml | 0 .../lovelace}/uci-lighting.yaml | 0 archive/lovelace/zone_1.yaml | 43 + archive/packages/alarm.yaml.off | 76 + .../packages}/cameras.yaml.off | 0 archive/packages/irrigation.yaml | 78 + .../packages/irritation-old-extras.yaml.off | 165 + {packages => archive/packages}/node_test.yaml | 0 archive/packages/sprinklers.yaml.off | 47 + archive/packages/temp.yaml.off | 131 + .../packages/test.yaml | 0 {packages => archive/packages}/timer.yaml | 0 .../packages}/uci_base_fio.yaml | 0 .../packages}/uci_gpio_example.yaml | 0 .../packages}/uci_lights.yaml | 16 +- archive/packages/weatherunderground.yaml | 20 + {packages => archive/packages}/zone1.yaml | 0 archive/packages/zone1.yaml.org | 262 + configs/auth.yaml | 3 +- configs/automations.yaml | 34 +- configs/customize.yaml | 6 +- configs/notify.yaml | 4 + configs/secrets.yaml | 4 - configuration.yaml | 54 +- custom_components/browser_mod/__init__.py | 52 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1227 bytes .../__pycache__/binary_sensor.cpython-38.pyc | Bin 0 -> 2137 bytes .../__pycache__/camera.cpython-38.pyc | Bin 0 -> 1736 bytes .../__pycache__/connection.cpython-38.pyc | Bin 0 -> 3325 bytes .../__pycache__/const.cpython-38.pyc | Bin 0 -> 840 bytes .../__pycache__/helpers.cpython-38.pyc | Bin 0 -> 3176 bytes .../__pycache__/light.cpython-38.pyc | Bin 0 -> 2516 bytes .../__pycache__/media_player.cpython-38.pyc | Bin 0 -> 3664 bytes .../__pycache__/mod_view.cpython-38.pyc | Bin 0 -> 1436 bytes .../__pycache__/sensor.cpython-38.pyc | Bin 0 -> 1560 bytes .../__pycache__/service.cpython-38.pyc | Bin 0 -> 1414 bytes .../browser_mod/binary_sensor.py | 50 + custom_components/browser_mod/browser_mod.js | 149 + custom_components/browser_mod/camera.py | 37 + custom_components/browser_mod/connection.py | 125 + custom_components/browser_mod/const.py | 35 + custom_components/browser_mod/helpers.py | 96 + custom_components/browser_mod/light.py | 59 + custom_components/browser_mod/manifest.json | 8 + custom_components/browser_mod/media_player.py | 82 + custom_components/browser_mod/mod_view.py | 36 + custom_components/browser_mod/sensor.py | 37 + custom_components/browser_mod/service.py | 37 + custom_components/browser_mod/services.yaml | 85 + custom_components/hacs/.translations/da.json | 191 - custom_components/hacs/.translations/de.json | 199 - custom_components/hacs/.translations/en.json | 199 - custom_components/hacs/.translations/es.json | 198 - custom_components/hacs/.translations/fr.json | 198 - custom_components/hacs/.translations/hu.json | 194 - custom_components/hacs/.translations/it.json | 198 - custom_components/hacs/.translations/nb.json | 197 - custom_components/hacs/.translations/nl.json | 197 - custom_components/hacs/.translations/pl.json | 198 - .../hacs/.translations/pt-BR.json | 182 - custom_components/hacs/.translations/ru.json | 193 - .../hacs/.translations/zh-Hans.json | 198 - custom_components/hacs/__init__.py | 254 +- .../hacs/__pycache__/__init__.cpython-37.pyc | Bin 6129 -> 0 bytes .../hacs/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 969 bytes .../hacs/__pycache__/base.cpython-38.pyc | Bin 0 -> 3818 bytes .../__pycache__/config_flow.cpython-37.pyc | Bin 4016 -> 0 bytes .../__pycache__/config_flow.cpython-38.pyc | Bin 0 -> 4672 bytes .../configuration_schema.cpython-37.pyc | Bin 1874 -> 0 bytes .../hacs/__pycache__/const.cpython-37.pyc | Bin 3235 -> 0 bytes .../hacs/__pycache__/const.cpython-38.pyc | Bin 0 -> 3411 bytes .../__pycache__/constrains.cpython-37.pyc | Bin 2425 -> 0 bytes .../hacs/__pycache__/enums.cpython-38.pyc | Bin 0 -> 1398 bytes .../hacs/__pycache__/globals.cpython-37.pyc | Bin 1089 -> 0 bytes .../hacs/__pycache__/http.cpython-37.pyc | Bin 2895 -> 0 bytes .../hacs/__pycache__/sensor.cpython-37.pyc | Bin 3197 -> 0 bytes .../hacs/__pycache__/sensor.cpython-38.pyc | Bin 0 -> 4186 bytes .../hacs/__pycache__/setup.cpython-37.pyc | Bin 3130 -> 0 bytes .../hacs/__pycache__/share.cpython-38.pyc | Bin 0 -> 1968 bytes .../hacs/__pycache__/store.cpython-37.pyc | Bin 909 -> 0 bytes .../ws_api_handlers.cpython-37.pyc | Bin 8698 -> 0 bytes custom_components/hacs/api/__init__.py | 1 + .../api/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 168 bytes ...owledge_critical_repository.cpython-38.pyc | Bin 0 -> 1054 bytes .../check_local_path.cpython-38.pyc | Bin 0 -> 936 bytes .../get_critical_repositories.cpython-38.pyc | Bin 0 -> 781 bytes .../__pycache__/hacs_config.cpython-38.pyc | Bin 0 -> 1097 bytes .../__pycache__/hacs_removed.cpython-38.pyc | Bin 0 -> 752 bytes .../hacs_repositories.cpython-38.pyc | Bin 0 -> 1986 bytes .../hacs_repository.cpython-38.pyc | Bin 0 -> 2756 bytes .../hacs_repository_data.cpython-38.pyc | Bin 0 -> 2845 bytes .../__pycache__/hacs_settings.cpython-38.pyc | Bin 0 -> 1607 bytes .../__pycache__/hacs_status.cpython-38.pyc | Bin 0 -> 924 bytes .../api/acknowledge_critical_repository.py | 25 + .../hacs/api/check_local_path.py | 24 + .../hacs/api/get_critical_repositories.py | 15 + custom_components/hacs/api/hacs_config.py | 28 + custom_components/hacs/api/hacs_removed.py | 15 + .../hacs/api/hacs_repositories.py | 62 + custom_components/hacs/api/hacs_repository.py | 113 + .../hacs/api/hacs_repository_data.py | 121 + custom_components/hacs/api/hacs_settings.py | 54 + custom_components/hacs/api/hacs_status.py | 23 + custom_components/hacs/base.py | 108 + custom_components/hacs/config_flow.py | 246 +- custom_components/hacs/const.py | 592 +- custom_components/hacs/constrains.py | 99 - custom_components/hacs/enums.py | 39 + custom_components/hacs/globals.py | 29 - custom_components/hacs/hacsbase/__init__.py | 355 - .../__pycache__/__init__.cpython-37.pyc | Bin 10796 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 142 bytes .../__pycache__/backup.cpython-37.pyc | Bin 3631 -> 0 bytes .../__pycache__/configuration.cpython-37.pyc | Bin 2409 -> 0 bytes .../__pycache__/configuration.cpython-38.pyc | Bin 0 -> 2533 bytes .../hacsbase/__pycache__/const.cpython-37.pyc | Bin 530 -> 0 bytes .../hacsbase/__pycache__/data.cpython-37.pyc | Bin 4265 -> 0 bytes .../hacsbase/__pycache__/data.cpython-38.pyc | Bin 0 -> 5419 bytes .../__pycache__/exceptions.cpython-37.pyc | Bin 546 -> 0 bytes .../hacsbase/__pycache__/hacs.cpython-38.pyc | Bin 0 -> 10766 bytes .../__pycache__/task_factory.cpython-37.pyc | Bin 2564 -> 0 bytes .../hacs/hacsbase/configuration.py | 150 +- custom_components/hacs/hacsbase/const.py | 10 - custom_components/hacs/hacsbase/data.py | 355 +- custom_components/hacs/hacsbase/hacs.py | 361 + .../hacs/hacsbase/task_factory.py | 75 - custom_components/hacs/handler/__init__.py | 1 - .../__pycache__/__init__.cpython-37.pyc | Bin 169 -> 0 bytes .../__pycache__/download.cpython-37.pyc | Bin 2115 -> 0 bytes .../__pycache__/template.cpython-37.pyc | Bin 849 -> 0 bytes custom_components/hacs/handler/download.py | 90 - custom_components/hacs/helpers/__init__.py | 17 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 719 bytes .../__pycache__/download.cpython-37.pyc | Bin 4121 -> 0 bytes .../__pycache__/filters.cpython-37.pyc | Bin 1274 -> 0 bytes .../__pycache__/get_defaults.cpython-37.pyc | Bin 1399 -> 0 bytes .../__pycache__/information.cpython-37.pyc | Bin 5163 -> 0 bytes .../__pycache__/install.cpython-37.pyc | Bin 3194 -> 0 bytes .../helpers/__pycache__/misc.cpython-37.pyc | Bin 980 -> 0 bytes .../__pycache__/network.cpython-37.pyc | Bin 431 -> 0 bytes .../register_repository.cpython-37.pyc | Bin 1455 -> 0 bytes .../validate_repository.cpython-37.pyc | Bin 2498 -> 0 bytes .../hacs/helpers/classes/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 149 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 548 bytes .../__pycache__/frontend_view.cpython-38.pyc | Bin 0 -> 1484 bytes .../__pycache__/manifest.cpython-38.pyc | Bin 0 -> 1386 bytes .../__pycache__/removed.cpython-38.pyc | Bin 0 -> 1026 bytes .../__pycache__/repository.cpython-38.pyc | Bin 0 -> 12467 bytes .../__pycache__/repositorydata.cpython-38.pyc | Bin 0 -> 3523 bytes .../__pycache__/validate.cpython-38.pyc | Bin 0 -> 534 bytes .../classes}/exceptions.py | 18 +- .../hacs/helpers/classes/frontend_view.py | 40 + .../classes}/manifest.py | 85 +- .../classes}/removed.py | 4 + .../classes}/repository.py | 860 +- .../classes}/repositorydata.py | 108 +- .../hacs/helpers/classes/validate.py | 11 + .../hacs/helpers/functions/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 151 bytes .../configuration_schema.cpython-38.pyc | Bin 0 -> 1890 bytes .../__pycache__/constrains.cpython-38.pyc | Bin 0 -> 1319 bytes .../__pycache__/download.cpython-38.pyc | Bin 0 -> 5669 bytes .../__pycache__/filters.cpython-38.pyc | Bin 0 -> 1296 bytes .../get_list_from_default.cpython-38.pyc | Bin 0 -> 1266 bytes .../__pycache__/information.cpython-38.pyc | Bin 0 -> 6164 bytes .../is_safe_to_remove.cpython-38.pyc | Bin 0 -> 808 bytes .../__pycache__/logger.cpython-38.pyc | Bin 0 -> 633 bytes .../functions/__pycache__/misc.cpython-38.pyc | Bin 0 -> 1480 bytes .../__pycache__/path_exsist.cpython-38.pyc | Bin 0 -> 571 bytes .../register_repository.cpython-38.pyc | Bin 0 -> 1941 bytes .../remaining_github_calls.cpython-38.pyc | Bin 0 -> 988 bytes .../functions/__pycache__/save.cpython-38.pyc | Bin 0 -> 1340 bytes .../__pycache__/store.cpython-38.pyc | Bin 0 -> 1320 bytes .../__pycache__/template.cpython-38.pyc | Bin 0 -> 927 bytes .../validate_repository.cpython-38.pyc | Bin 0 -> 2799 bytes .../version_to_install.cpython-38.pyc | Bin 0 -> 613 bytes .../functions}/configuration_schema.py | 55 +- .../hacs/helpers/functions/constrains.py | 43 + .../hacs/helpers/{ => functions}/download.py | 167 +- .../hacs/helpers/{ => functions}/filters.py | 0 .../functions/get_list_from_default.py | 35 + .../helpers/{ => functions}/information.py | 69 +- .../helpers/functions/is_safe_to_remove.py | 20 + .../hacs/helpers/functions/logger.py | 19 + .../hacs/helpers/{ => functions}/misc.py | 15 + .../hacs/helpers/functions/path_exsist.py | 13 + .../helpers/functions/register_repository.py | 70 + .../functions/remaining_github_calls.py | 32 + .../hacs/helpers/functions/save.py | 52 + .../hacs/helpers/functions/store.py | 34 + .../functions}/template.py | 61 +- .../helpers/functions/validate_repository.py | 98 + .../helpers/functions/version_to_install.py | 20 + .../hacs/helpers/get_defaults.py | 44 - custom_components/hacs/helpers/install.py | 128 - .../hacs/helpers/methods/__init__.py | 30 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1078 bytes .../__pycache__/installation.cpython-38.pyc | Bin 0 -> 3840 bytes .../__pycache__/registration.cpython-38.pyc | Bin 0 -> 1814 bytes .../reinstall_if_needed.cpython-38.pyc | Bin 0 -> 807 bytes .../hacs/helpers/methods/installation.py | 113 + .../hacs/helpers/methods/registration.py | 43 + .../helpers/methods/reinstall_if_needed.py | 12 + custom_components/hacs/helpers/network.py | 8 - .../hacs/helpers/properties/__init__.py | 16 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 663 bytes .../can_be_installed.cpython-38.pyc | Bin 0 -> 974 bytes .../__pycache__/custom.cpython-38.pyc | Bin 0 -> 657 bytes .../__pycache__/pending_update.cpython-38.pyc | Bin 0 -> 1010 bytes .../helpers/properties/can_be_installed.py | 21 + .../hacs/helpers/properties/custom.py | 13 + .../hacs/helpers/properties/pending_update.py | 23 + .../hacs/helpers/register_repository.py | 49 - .../hacs/helpers/validate_repository.py | 90 - custom_components/hacs/http.py | 83 - custom_components/hacs/iconset.js | 34 - custom_components/hacs/manifest.json | 45 +- custom_components/hacs/models/__init__.py | 1 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 164 bytes .../models/__pycache__/core.cpython-38.pyc | Bin 0 -> 554 bytes .../__pycache__/frontend.cpython-38.pyc | Bin 0 -> 510 bytes .../models/__pycache__/system.cpython-38.pyc | Bin 0 -> 645 bytes custom_components/hacs/models/core.py | 14 + custom_components/hacs/models/frontend.py | 10 + custom_components/hacs/models/system.py | 15 + .../hacs/operational/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 145 bytes .../__pycache__/backup.cpython-38.pyc | Bin 0 -> 3611 bytes .../__pycache__/factory.cpython-38.pyc | Bin 0 -> 1939 bytes .../__pycache__/reload.cpython-38.pyc | Bin 0 -> 522 bytes .../__pycache__/remove.cpython-38.pyc | Bin 0 -> 917 bytes .../__pycache__/setup.cpython-38.pyc | Bin 0 -> 5774 bytes .../hacs/{hacsbase => operational}/backup.py | 241 +- custom_components/hacs/operational/factory.py | 50 + custom_components/hacs/operational/reload.py | 10 + custom_components/hacs/operational/remove.py | 24 + custom_components/hacs/operational/runtime.py | 1 + custom_components/hacs/operational/setup.py | 209 + .../operational/setup_actions/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 159 bytes .../__pycache__/categories.cpython-38.pyc | Bin 0 -> 1535 bytes .../__pycache__/clear_storage.cpython-38.pyc | Bin 0 -> 977 bytes .../__pycache__/frontend.cpython-38.pyc | Bin 0 -> 1494 bytes .../load_hacs_repository.cpython-38.pyc | Bin 0 -> 1469 bytes .../__pycache__/sensor.cpython-38.pyc | Bin 0 -> 920 bytes .../__pycache__/websocket_api.cpython-38.pyc | Bin 0 -> 1714 bytes .../operational/setup_actions/categories.py | 42 + .../setup_actions/clear_storage.py | 23 + .../operational/setup_actions/frontend.py | 46 + .../setup_actions/load_hacs_repository.py | 37 + .../hacs/operational/setup_actions/sensor.py | 24 + .../setup_actions/websocket_api.py | 35 + .../hacs/repositories/__init__.py | 32 +- .../__pycache__/__init__.cpython-37.pyc | Bin 796 -> 0 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 802 bytes .../__pycache__/appdaemon.cpython-37.pyc | Bin 2631 -> 0 bytes .../__pycache__/appdaemon.cpython-38.pyc | Bin 0 -> 2464 bytes .../__pycache__/integration.cpython-37.pyc | Bin 3217 -> 0 bytes .../__pycache__/integration.cpython-38.pyc | Bin 0 -> 3510 bytes .../__pycache__/manifest.cpython-37.pyc | Bin 1366 -> 0 bytes .../__pycache__/netdaemon.cpython-37.pyc | Bin 2650 -> 0 bytes .../__pycache__/netdaemon.cpython-38.pyc | Bin 0 -> 2916 bytes .../__pycache__/plugin.cpython-37.pyc | Bin 3095 -> 0 bytes .../__pycache__/plugin.cpython-38.pyc | Bin 0 -> 2643 bytes .../__pycache__/python_script.cpython-37.pyc | Bin 2440 -> 0 bytes .../__pycache__/python_script.cpython-38.pyc | Bin 0 -> 2688 bytes .../__pycache__/removed.cpython-37.pyc | Bin 829 -> 0 bytes .../__pycache__/repository.cpython-37.pyc | Bin 11706 -> 0 bytes .../__pycache__/repositorydata.cpython-37.pyc | Bin 2578 -> 0 bytes .../__pycache__/theme.cpython-37.pyc | Bin 2335 -> 0 bytes .../__pycache__/theme.cpython-38.pyc | Bin 0 -> 2835 bytes .../hacs/repositories/appdaemon.py | 157 +- .../hacs/repositories/integration.py | 190 +- .../hacs/repositories/netdaemon.py | 177 +- custom_components/hacs/repositories/plugin.py | 184 +- .../hacs/repositories/python_script.py | 170 +- custom_components/hacs/repositories/theme.py | 148 +- custom_components/hacs/sensor.py | 218 +- custom_components/hacs/services.yaml | 15 - custom_components/hacs/setup.py | 110 - custom_components/hacs/share.py | 68 + custom_components/hacs/store.py | 19 - custom_components/hacs/translations/cs.json | 358 + custom_components/hacs/translations/da.json | 345 + custom_components/hacs/translations/de.json | 383 + .../{.translations => translations}/el.json | 41 +- custom_components/hacs/translations/en.json | 384 + custom_components/hacs/translations/es.json | 376 + .../hacs/translations/et_EE.json | 358 + custom_components/hacs/translations/fi.json | 242 + custom_components/hacs/translations/fr.json | 383 + custom_components/hacs/translations/hu.json | 378 + custom_components/hacs/translations/it.json | 366 + custom_components/hacs/translations/nb.json | 383 + custom_components/hacs/translations/nl.json | 346 + .../{.translations => translations}/nn.json | 68 +- custom_components/hacs/translations/pl.json | 383 + .../hacs/translations/pt-BR.json | 356 + custom_components/hacs/translations/pt.json | 355 + .../{.translations => translations}/ro.json | 47 +- custom_components/hacs/translations/ru.json | 356 + .../{.translations => translations}/sl.json | 35 +- .../{.translations => translations}/sv.json | 50 +- custom_components/hacs/translations/vi.json | 345 + .../hacs/translations/zh-Hans.json | 383 + custom_components/hacs/validate/README.md | 38 + custom_components/hacs/validate/__init__.py | 51 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1954 bytes .../validate/__pycache__/base.cpython-38.pyc | Bin 0 -> 2261 bytes custom_components/hacs/validate/base.py | 48 + .../__pycache__/hacs_manifest.cpython-38.pyc | Bin 0 -> 799 bytes .../repository_description.cpython-38.pyc | Bin 0 -> 657 bytes ...repository_information_file.cpython-38.pyc | Bin 0 -> 1008 bytes .../repository_topics.cpython-38.pyc | Bin 0 -> 632 bytes .../hacs/validate/common/hacs_manifest.py | 10 + .../validate/common/repository_description.py | 10 + .../common/repository_information_file.py | 19 + .../hacs/validate/common/repository_topics.py | 10 + .../integration_manifest.cpython-38.pyc | Bin 0 -> 865 bytes .../integration/integration_manifest.py | 10 + .../hacs/webresponses/__init__.py | 1 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 187 bytes .../__pycache__/category.cpython-38.pyc | Bin 0 -> 1254 bytes .../__pycache__/frontend.cpython-38.pyc | Bin 0 -> 1481 bytes .../__pycache__/iconset.cpython-38.pyc | Bin 0 -> 3885 bytes .../hacs/webresponses/category.py | 39 + .../hacs/webresponses/frontend.py | 50 + .../hacs/webresponses/iconset.py | 15 + custom_components/hacs/ws_api_handlers.py | 379 - .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 5597 bytes custom_components/lovelace_gen/__init__.py | 97 + .../__pycache__/__init__.cpython-37.pyc | Bin 0 -> 3000 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 3052 bytes custom_components/lovelace_gen/manifest.json | 8 + custom_components/nodered/__init__.py | 258 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 7642 bytes .../__pycache__/config_flow.cpython-38.pyc | Bin 0 -> 1290 bytes .../nodered/__pycache__/const.cpython-38.pyc | Bin 0 -> 1497 bytes .../__pycache__/discovery.cpython-38.pyc | Bin 0 -> 2609 bytes .../__pycache__/websocket.cpython-38.pyc | Bin 0 -> 3982 bytes custom_components/nodered/binary_sensor.py | 74 + custom_components/nodered/config_flow.py | 31 + custom_components/nodered/const.py | 51 + custom_components/nodered/discovery.py | 107 + custom_components/nodered/manifest.json | 14 + custom_components/nodered/sensor.py | 37 + custom_components/nodered/services.yaml | 17 + custom_components/nodered/switch.py | 153 + .../nodered/translations/en.json | 14 + custom_components/nodered/websocket.py | 151 + custom_components/variable/__init__.py | 174 +- .../__pycache__/__init__.cpython-37.pyc | Bin 5490 -> 5507 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 5642 bytes custom_components/wundergroundpws/__init__.py | 1 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 186 bytes .../__pycache__/sensor.cpython-38.pyc | Bin 0 -> 19085 bytes .../wundergroundpws/manifest.json | 8 + custom_components/wundergroundpws/sensor.py | 575 + .../sensor.wundergroundpws.markdown | 198 + esphome/inside_temp_humd.yaml | 33 + ...-temp-humd.yaml => outside_temp_humd.yaml} | 29 +- hass.code-workspace | 12 + lovelace/{ => _manual}/closet.yaml | 0 lovelace/{ => _manual}/lights.yaml | 0 lovelace/_manual/temps.yaml | 15 + lovelace/_manual/weather.yaml | 45 + lovelace/dashboards.yaml | 12 + lovelace/irrigation.yaml | 43 - lovelace/manual.yaml | 23 + lovelace/panel.yaml | 13 + lovelace/temps.yaml | 24 - packages/aa_system.yaml | 7 +- packages/darksky_weather.yaml | 42 + packages/indoor_security_light.yaml | 2 +- packages/irrigation/common.yaml | 39 + packages/irrigation/zone1.yaml | 57 + packages/irrigation/zone2.yaml | 57 + packages/irrigation/zone3.yaml | 57 + packages/irrigation/zone4.yaml | 57 + packages/irrigation/zone5.yaml | 57 + packages/irrigation/zone6.yaml | 57 + packages/irrigation/zone7.yaml | 57 + packages/outside.yaml | 57 + packages/weather.yaml | 34 - python_scripts/state_change.py | 31 + python_scripts/states_change.py | 49 + templates/button-card-templates/default.yaml | 38 + .../button-card-templates/large_value.yaml | 30 + themes/clear/clear.yaml | 60 + themes/dark_themes/dark_themes.yaml | 269 + ui-lovelace.yaml | 13 - www/community/banner-card/.gitignore | 1 - www/community/banner-card/banner-card.js | 4 - www/community/button-card/.gitignore | 1 - www/community/button-card/button-card.js | 1562 +- .../lovelace-darksky-card.js | 787 + .../time-picker-card.js | 528 + www/icons/weather_icons/LICENSE | 6 + .../weather_icons/animated/cloudy-day-1.svg | 175 + .../weather_icons/animated/cloudy-day-2.svg | 176 + .../weather_icons/animated/cloudy-day-3.svg | 175 + .../weather_icons/animated/cloudy-night-1.svg | 198 + .../weather_icons/animated/cloudy-night-2.svg | 198 + .../weather_icons/animated/cloudy-night-3.svg | 198 + www/icons/weather_icons/animated/cloudy.svg | 500 + www/icons/weather_icons/animated/day.svg | 521 + www/icons/weather_icons/animated/night.svg | 503 + www/icons/weather_icons/animated/rainy-1.svg | 157 + www/icons/weather_icons/animated/rainy-2.svg | 133 + www/icons/weather_icons/animated/rainy-3.svg | 157 + www/icons/weather_icons/animated/rainy-4.svg | 66 + www/icons/weather_icons/animated/rainy-5.svg | 90 + www/icons/weather_icons/animated/rainy-6.svg | 91 + www/icons/weather_icons/animated/rainy-7.svg | 91 + www/icons/weather_icons/animated/snowy-1.svg | 230 + www/icons/weather_icons/animated/snowy-2.svg | 237 + www/icons/weather_icons/animated/snowy-3.svg | 268 + www/icons/weather_icons/animated/snowy-4.svg | 94 + www/icons/weather_icons/animated/snowy-5.svg | 166 + www/icons/weather_icons/animated/snowy-6.svg | 225 + www/icons/weather_icons/animated/thunder.svg | 268 + .../weather_icons/animated/weather-sprite.svg | 1245 + www/icons/weather_icons/animated/weather.svg | 1245 + .../animated/weather_sagittarius.svg | 9 + .../weather_icons/animated/weather_sunset.svg | 14 + .../weather_icons/static/cloudy-day-1.svg | 59 + .../weather_icons/static/cloudy-day-2.svg | 60 + .../weather_icons/static/cloudy-day-3.svg | 59 + .../weather_icons/static/cloudy-night-1.svg | 41 + .../weather_icons/static/cloudy-night-2.svg | 41 + .../weather_icons/static/cloudy-night-3.svg | 41 + www/icons/weather_icons/static/cloudy.svg | 33 + www/icons/weather_icons/static/day.svg | 54 + www/icons/weather_icons/static/night.svg | 36 + www/icons/weather_icons/static/rainy-1.svg | 63 + www/icons/weather_icons/static/rainy-2.svg | 62 + www/icons/weather_icons/static/rainy-3.svg | 63 + www/icons/weather_icons/static/rainy-4.svg | 33 + www/icons/weather_icons/static/rainy-5.svg | 34 + www/icons/weather_icons/static/rainy-6.svg | 35 + www/icons/weather_icons/static/rainy-7.svg | 35 + www/icons/weather_icons/static/snowy-1.svg | 77 + www/icons/weather_icons/static/snowy-2.svg | 67 + www/icons/weather_icons/static/snowy-3.svg | 75 + www/icons/weather_icons/static/snowy-4.svg | 38 + www/icons/weather_icons/static/snowy-5.svg | 46 + www/icons/weather_icons/static/snowy-6.svg | 54 + www/icons/weather_icons/static/thunder.svg | 36 + .../weather_icons/static/weather-sprite.svg | 1245 + www/icons/weather_icons/static/weather.svg | 622 + .../static/weather_sagittarius.svg | 9 + .../weather_icons/static/weather_sunset.svg | 14 + www/panels.yaml | 10 + www/sidebar-order.yaml | 35 + www/wupws_icons/00.png | Bin 0 -> 2090 bytes www/wupws_icons/01.png | Bin 0 -> 2516 bytes www/wupws_icons/02.png | Bin 0 -> 2516 bytes www/wupws_icons/03.png | Bin 0 -> 3279 bytes www/wupws_icons/04.png | Bin 0 -> 3279 bytes www/wupws_icons/05.png | Bin 0 -> 3478 bytes www/wupws_icons/06.png | Bin 0 -> 3324 bytes www/wupws_icons/07.png | Bin 0 -> 3478 bytes www/wupws_icons/08.png | Bin 0 -> 3011 bytes www/wupws_icons/09.png | Bin 0 -> 2698 bytes www/wupws_icons/10.png | Bin 0 -> 3324 bytes www/wupws_icons/11.png | Bin 0 -> 2698 bytes www/wupws_icons/12.png | Bin 0 -> 2784 bytes www/wupws_icons/13.png | Bin 0 -> 3204 bytes www/wupws_icons/14.png | Bin 0 -> 3968 bytes www/wupws_icons/15.png | Bin 0 -> 3830 bytes www/wupws_icons/16.png | Bin 0 -> 3968 bytes www/wupws_icons/17.png | Bin 0 -> 3015 bytes www/wupws_icons/18.png | Bin 0 -> 3015 bytes www/wupws_icons/19.png | Bin 0 -> 4055 bytes www/wupws_icons/20.png | Bin 0 -> 4055 bytes www/wupws_icons/21.png | Bin 0 -> 4055 bytes www/wupws_icons/22.png | Bin 0 -> 4055 bytes www/wupws_icons/23.png | Bin 0 -> 2477 bytes www/wupws_icons/24.png | Bin 0 -> 2477 bytes www/wupws_icons/25.png | Bin 0 -> 3830 bytes www/wupws_icons/26.png | Bin 0 -> 1756 bytes www/wupws_icons/27.png | Bin 0 -> 3901 bytes www/wupws_icons/28.png | Bin 0 -> 4267 bytes www/wupws_icons/29.png | Bin 0 -> 3037 bytes www/wupws_icons/30.png | Bin 0 -> 3720 bytes www/wupws_icons/31.png | Bin 0 -> 3290 bytes www/wupws_icons/32.png | Bin 0 -> 5190 bytes www/wupws_icons/33.png | Bin 0 -> 3470 bytes www/wupws_icons/34.png | Bin 0 -> 4791 bytes www/wupws_icons/35.png | Bin 0 -> 3324 bytes www/wupws_icons/36.png | Bin 0 -> 5190 bytes www/wupws_icons/37.png | Bin 0 -> 4910 bytes www/wupws_icons/38.png | Bin 0 -> 4621 bytes www/wupws_icons/39.png | Bin 0 -> 2698 bytes www/wupws_icons/40.png | Bin 0 -> 3386 bytes www/wupws_icons/41.png | Bin 0 -> 5854 bytes www/wupws_icons/42.png | Bin 0 -> 4588 bytes www/wupws_icons/43.png | Bin 0 -> 4588 bytes www/wupws_icons/44.png | Bin 0 -> 2792 bytes www/wupws_icons/45.png | Bin 0 -> 4546 bytes www/wupws_icons/46.png | Bin 0 -> 5098 bytes www/wupws_icons/47.png | Bin 0 -> 3933 bytes www/wupws_icons/na.png | Bin 0 -> 2792 bytes 563 files changed, 57328 insertions(+), 8124 deletions(-) create mode 100644 .gitsecret/keys/pubring.kbx create mode 100644 .gitsecret/keys/pubring.kbx~ create mode 100644 .gitsecret/keys/trustdb.gpg create mode 100644 .gitsecret/paths/mapping.cfg create mode 100644 .storage/auth create mode 100644 .storage/auth_provider.homeassistant create mode 100644 .storage/core.area_registry create mode 100644 .storage/core.config create mode 100644 .storage/core.config_entries create mode 100644 .storage/core.device_registry create mode 100644 .storage/core.entity_registry create mode 100644 .storage/core.restore_state create mode 100644 .storage/core.uuid create mode 100644 .storage/esphome.28ebec0e77234c51bb89a4a1b26e47b3 create mode 100644 .storage/esphome.9436e71647e64581a19a2cc8172f9a35 create mode 100644 .storage/esphome.cf5266d3fa4a4824850935818193ac48 create mode 100644 .storage/esphome.eeb6ca4d62944fa4b32711e291ba2f63 create mode 100644 .storage/frontend.user_data_99685dc45d9f40dc8183e11ce5128038 create mode 100644 .storage/frontend_theme create mode 100644 .storage/hacs.critical create mode 100644 .storage/hacs.hacs create mode 100644 .storage/hacs.repositories create mode 100644 .storage/hacs/146194325.hacs create mode 100644 .storage/hacs/162468030.hacs create mode 100644 .storage/hacs/172733314.hacs create mode 100644 .storage/hacs/260526528.hacs create mode 100644 .storage/hacs/261262884.hacs create mode 100644 .storage/hassio create mode 100644 .storage/http create mode 100644 .storage/lovelace.lovelace_learning create mode 100644 .storage/lovelace.system_setup create mode 100644 .storage/lovelace_dashboards create mode 100644 .storage/lovelace_resources create mode 100644 .storage/mobile_app create mode 100644 .storage/onboarding create mode 100644 .storage/person create mode 100644 .storage/zone create mode 100644 .vscode/settings.json rename {configs => archive/configs}/http.yaml (100%) rename {configs => archive/configs}/mqtt.yaml (96%) rename {configs => archive/configs}/panel.yaml (100%) rename {configs => archive/configs}/scripts.yaml (100%) create mode 100644 archive/lovelace/bak.irrigation.yaml. create mode 100644 archive/lovelace/button-card.yaml.j2 rename {lovelace => archive/lovelace}/cameras.yaml (100%) rename {lovelace => archive/lovelace}/fio.yaml (100%) rename lovelace/gios.yaml => archive/lovelace/gpios.yaml (97%) create mode 100644 archive/lovelace/irrigation-buttons-old.yaml.j2 create mode 100644 archive/lovelace/irrigation-buttons.yaml.j2 create mode 100644 archive/lovelace/irrigation.yaml.j2 create mode 100644 archive/lovelace/irrigation.yaml.j2.old create mode 100644 archive/lovelace/irrigation0.yaml.j2 create mode 100644 archive/lovelace/layout-testing.yaml create mode 100644 archive/lovelace/layout.yaml create mode 100644 archive/lovelace/node-red-scheduler.yaml rename {lovelace => archive/lovelace}/node_testing.yaml (100%) rename {lovelace => archive/lovelace}/scheduler.yaml (95%) rename {lovelace => archive/lovelace}/timer.yaml (100%) rename {lovelace => archive/lovelace}/uci-lighting.yaml (100%) create mode 100644 archive/lovelace/zone_1.yaml create mode 100644 archive/packages/alarm.yaml.off rename {packages => archive/packages}/cameras.yaml.off (100%) create mode 100644 archive/packages/irrigation.yaml create mode 100644 archive/packages/irritation-old-extras.yaml.off rename {packages => archive/packages}/node_test.yaml (100%) create mode 100644 archive/packages/sprinklers.yaml.off create mode 100644 archive/packages/temp.yaml.off rename packages/scheduler.yaml => archive/packages/test.yaml (100%) rename {packages => archive/packages}/timer.yaml (100%) rename {packages => archive/packages}/uci_base_fio.yaml (100%) rename {packages => archive/packages}/uci_gpio_example.yaml (100%) rename {packages => archive/packages}/uci_lights.yaml (73%) create mode 100644 archive/packages/weatherunderground.yaml rename {packages => archive/packages}/zone1.yaml (100%) create mode 100644 archive/packages/zone1.yaml.org create mode 100644 configs/notify.yaml delete mode 100644 configs/secrets.yaml create mode 100644 custom_components/browser_mod/__init__.py create mode 100644 custom_components/browser_mod/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/binary_sensor.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/camera.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/connection.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/const.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/helpers.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/light.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/media_player.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/mod_view.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/sensor.cpython-38.pyc create mode 100644 custom_components/browser_mod/__pycache__/service.cpython-38.pyc create mode 100644 custom_components/browser_mod/binary_sensor.py create mode 100644 custom_components/browser_mod/browser_mod.js create mode 100644 custom_components/browser_mod/camera.py create mode 100644 custom_components/browser_mod/connection.py create mode 100644 custom_components/browser_mod/const.py create mode 100644 custom_components/browser_mod/helpers.py create mode 100644 custom_components/browser_mod/light.py create mode 100644 custom_components/browser_mod/manifest.json create mode 100644 custom_components/browser_mod/media_player.py create mode 100644 custom_components/browser_mod/mod_view.py create mode 100644 custom_components/browser_mod/sensor.py create mode 100644 custom_components/browser_mod/service.py create mode 100644 custom_components/browser_mod/services.yaml delete mode 100644 custom_components/hacs/.translations/da.json delete mode 100644 custom_components/hacs/.translations/de.json delete mode 100644 custom_components/hacs/.translations/en.json delete mode 100644 custom_components/hacs/.translations/es.json delete mode 100644 custom_components/hacs/.translations/fr.json delete mode 100644 custom_components/hacs/.translations/hu.json delete mode 100644 custom_components/hacs/.translations/it.json delete mode 100644 custom_components/hacs/.translations/nb.json delete mode 100644 custom_components/hacs/.translations/nl.json delete mode 100644 custom_components/hacs/.translations/pl.json delete mode 100644 custom_components/hacs/.translations/pt-BR.json delete mode 100644 custom_components/hacs/.translations/ru.json delete mode 100644 custom_components/hacs/.translations/zh-Hans.json delete mode 100644 custom_components/hacs/__pycache__/__init__.cpython-37.pyc create mode 100644 custom_components/hacs/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/__pycache__/base.cpython-38.pyc delete mode 100644 custom_components/hacs/__pycache__/config_flow.cpython-37.pyc create mode 100644 custom_components/hacs/__pycache__/config_flow.cpython-38.pyc delete mode 100644 custom_components/hacs/__pycache__/configuration_schema.cpython-37.pyc delete mode 100644 custom_components/hacs/__pycache__/const.cpython-37.pyc create mode 100644 custom_components/hacs/__pycache__/const.cpython-38.pyc delete mode 100644 custom_components/hacs/__pycache__/constrains.cpython-37.pyc create mode 100644 custom_components/hacs/__pycache__/enums.cpython-38.pyc delete mode 100644 custom_components/hacs/__pycache__/globals.cpython-37.pyc delete mode 100644 custom_components/hacs/__pycache__/http.cpython-37.pyc delete mode 100644 custom_components/hacs/__pycache__/sensor.cpython-37.pyc create mode 100644 custom_components/hacs/__pycache__/sensor.cpython-38.pyc delete mode 100644 custom_components/hacs/__pycache__/setup.cpython-37.pyc create mode 100644 custom_components/hacs/__pycache__/share.cpython-38.pyc delete mode 100644 custom_components/hacs/__pycache__/store.cpython-37.pyc delete mode 100644 custom_components/hacs/__pycache__/ws_api_handlers.cpython-37.pyc create mode 100644 custom_components/hacs/api/__init__.py create mode 100644 custom_components/hacs/api/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/acknowledge_critical_repository.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/check_local_path.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/get_critical_repositories.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/hacs_config.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/hacs_removed.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/hacs_repositories.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/hacs_repository.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/hacs_repository_data.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/hacs_settings.cpython-38.pyc create mode 100644 custom_components/hacs/api/__pycache__/hacs_status.cpython-38.pyc create mode 100644 custom_components/hacs/api/acknowledge_critical_repository.py create mode 100644 custom_components/hacs/api/check_local_path.py create mode 100644 custom_components/hacs/api/get_critical_repositories.py create mode 100644 custom_components/hacs/api/hacs_config.py create mode 100644 custom_components/hacs/api/hacs_removed.py create mode 100644 custom_components/hacs/api/hacs_repositories.py create mode 100644 custom_components/hacs/api/hacs_repository.py create mode 100644 custom_components/hacs/api/hacs_repository_data.py create mode 100644 custom_components/hacs/api/hacs_settings.py create mode 100644 custom_components/hacs/api/hacs_status.py create mode 100644 custom_components/hacs/base.py delete mode 100644 custom_components/hacs/constrains.py create mode 100644 custom_components/hacs/enums.py delete mode 100644 custom_components/hacs/globals.py delete mode 100644 custom_components/hacs/hacsbase/__pycache__/__init__.cpython-37.pyc create mode 100644 custom_components/hacs/hacsbase/__pycache__/__init__.cpython-38.pyc delete mode 100644 custom_components/hacs/hacsbase/__pycache__/backup.cpython-37.pyc delete mode 100644 custom_components/hacs/hacsbase/__pycache__/configuration.cpython-37.pyc create mode 100644 custom_components/hacs/hacsbase/__pycache__/configuration.cpython-38.pyc delete mode 100644 custom_components/hacs/hacsbase/__pycache__/const.cpython-37.pyc delete mode 100644 custom_components/hacs/hacsbase/__pycache__/data.cpython-37.pyc create mode 100644 custom_components/hacs/hacsbase/__pycache__/data.cpython-38.pyc delete mode 100644 custom_components/hacs/hacsbase/__pycache__/exceptions.cpython-37.pyc create mode 100644 custom_components/hacs/hacsbase/__pycache__/hacs.cpython-38.pyc delete mode 100644 custom_components/hacs/hacsbase/__pycache__/task_factory.cpython-37.pyc delete mode 100644 custom_components/hacs/hacsbase/const.py create mode 100644 custom_components/hacs/hacsbase/hacs.py delete mode 100644 custom_components/hacs/hacsbase/task_factory.py delete mode 100644 custom_components/hacs/handler/__init__.py delete mode 100644 custom_components/hacs/handler/__pycache__/__init__.cpython-37.pyc delete mode 100644 custom_components/hacs/handler/__pycache__/download.cpython-37.pyc delete mode 100644 custom_components/hacs/handler/__pycache__/template.cpython-37.pyc delete mode 100644 custom_components/hacs/handler/download.py create mode 100644 custom_components/hacs/helpers/__init__.py create mode 100644 custom_components/hacs/helpers/__pycache__/__init__.cpython-38.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/download.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/filters.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/get_defaults.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/information.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/install.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/misc.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/network.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/register_repository.cpython-37.pyc delete mode 100644 custom_components/hacs/helpers/__pycache__/validate_repository.cpython-37.pyc create mode 100644 custom_components/hacs/helpers/classes/__init__.py create mode 100644 custom_components/hacs/helpers/classes/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/classes/__pycache__/exceptions.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/classes/__pycache__/frontend_view.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/classes/__pycache__/manifest.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/classes/__pycache__/removed.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/classes/__pycache__/repository.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/classes/__pycache__/repositorydata.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/classes/__pycache__/validate.cpython-38.pyc rename custom_components/hacs/{hacsbase => helpers/classes}/exceptions.py (94%) create mode 100644 custom_components/hacs/helpers/classes/frontend_view.py rename custom_components/hacs/{repositories => helpers/classes}/manifest.py (89%) rename custom_components/hacs/{repositories => helpers/classes}/removed.py (80%) rename custom_components/hacs/{repositories => helpers/classes}/repository.py (53%) rename custom_components/hacs/{repositories => helpers/classes}/repositorydata.py (52%) create mode 100644 custom_components/hacs/helpers/classes/validate.py create mode 100644 custom_components/hacs/helpers/functions/__init__.py create mode 100644 custom_components/hacs/helpers/functions/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/configuration_schema.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/constrains.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/download.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/filters.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/get_list_from_default.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/information.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/is_safe_to_remove.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/logger.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/misc.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/path_exsist.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/register_repository.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/remaining_github_calls.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/save.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/store.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/template.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/validate_repository.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/functions/__pycache__/version_to_install.cpython-38.pyc rename custom_components/hacs/{ => helpers/functions}/configuration_schema.py (53%) create mode 100644 custom_components/hacs/helpers/functions/constrains.py rename custom_components/hacs/helpers/{ => functions}/download.py (51%) rename custom_components/hacs/helpers/{ => functions}/filters.py (100%) create mode 100644 custom_components/hacs/helpers/functions/get_list_from_default.py rename custom_components/hacs/helpers/{ => functions}/information.py (71%) create mode 100644 custom_components/hacs/helpers/functions/is_safe_to_remove.py create mode 100644 custom_components/hacs/helpers/functions/logger.py rename custom_components/hacs/helpers/{ => functions}/misc.py (70%) create mode 100644 custom_components/hacs/helpers/functions/path_exsist.py create mode 100644 custom_components/hacs/helpers/functions/register_repository.py create mode 100644 custom_components/hacs/helpers/functions/remaining_github_calls.py create mode 100644 custom_components/hacs/helpers/functions/save.py create mode 100644 custom_components/hacs/helpers/functions/store.py rename custom_components/hacs/{handler => helpers/functions}/template.py (72%) create mode 100644 custom_components/hacs/helpers/functions/validate_repository.py create mode 100644 custom_components/hacs/helpers/functions/version_to_install.py delete mode 100644 custom_components/hacs/helpers/get_defaults.py delete mode 100644 custom_components/hacs/helpers/install.py create mode 100644 custom_components/hacs/helpers/methods/__init__.py create mode 100644 custom_components/hacs/helpers/methods/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/methods/__pycache__/installation.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/methods/__pycache__/registration.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/methods/__pycache__/reinstall_if_needed.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/methods/installation.py create mode 100644 custom_components/hacs/helpers/methods/registration.py create mode 100644 custom_components/hacs/helpers/methods/reinstall_if_needed.py delete mode 100644 custom_components/hacs/helpers/network.py create mode 100644 custom_components/hacs/helpers/properties/__init__.py create mode 100644 custom_components/hacs/helpers/properties/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/properties/__pycache__/can_be_installed.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/properties/__pycache__/custom.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/properties/__pycache__/pending_update.cpython-38.pyc create mode 100644 custom_components/hacs/helpers/properties/can_be_installed.py create mode 100644 custom_components/hacs/helpers/properties/custom.py create mode 100644 custom_components/hacs/helpers/properties/pending_update.py delete mode 100644 custom_components/hacs/helpers/register_repository.py delete mode 100644 custom_components/hacs/helpers/validate_repository.py delete mode 100644 custom_components/hacs/http.py delete mode 100644 custom_components/hacs/iconset.js create mode 100644 custom_components/hacs/models/__init__.py create mode 100644 custom_components/hacs/models/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/models/__pycache__/core.cpython-38.pyc create mode 100644 custom_components/hacs/models/__pycache__/frontend.cpython-38.pyc create mode 100644 custom_components/hacs/models/__pycache__/system.cpython-38.pyc create mode 100644 custom_components/hacs/models/core.py create mode 100644 custom_components/hacs/models/frontend.py create mode 100644 custom_components/hacs/models/system.py create mode 100644 custom_components/hacs/operational/__init__.py create mode 100644 custom_components/hacs/operational/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/operational/__pycache__/backup.cpython-38.pyc create mode 100644 custom_components/hacs/operational/__pycache__/factory.cpython-38.pyc create mode 100644 custom_components/hacs/operational/__pycache__/reload.cpython-38.pyc create mode 100644 custom_components/hacs/operational/__pycache__/remove.cpython-38.pyc create mode 100644 custom_components/hacs/operational/__pycache__/setup.cpython-38.pyc rename custom_components/hacs/{hacsbase => operational}/backup.py (81%) create mode 100644 custom_components/hacs/operational/factory.py create mode 100644 custom_components/hacs/operational/reload.py create mode 100644 custom_components/hacs/operational/remove.py create mode 100644 custom_components/hacs/operational/runtime.py create mode 100644 custom_components/hacs/operational/setup.py create mode 100644 custom_components/hacs/operational/setup_actions/__init__.py create mode 100644 custom_components/hacs/operational/setup_actions/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/operational/setup_actions/__pycache__/categories.cpython-38.pyc create mode 100644 custom_components/hacs/operational/setup_actions/__pycache__/clear_storage.cpython-38.pyc create mode 100644 custom_components/hacs/operational/setup_actions/__pycache__/frontend.cpython-38.pyc create mode 100644 custom_components/hacs/operational/setup_actions/__pycache__/load_hacs_repository.cpython-38.pyc create mode 100644 custom_components/hacs/operational/setup_actions/__pycache__/sensor.cpython-38.pyc create mode 100644 custom_components/hacs/operational/setup_actions/__pycache__/websocket_api.cpython-38.pyc create mode 100644 custom_components/hacs/operational/setup_actions/categories.py create mode 100644 custom_components/hacs/operational/setup_actions/clear_storage.py create mode 100644 custom_components/hacs/operational/setup_actions/frontend.py create mode 100644 custom_components/hacs/operational/setup_actions/load_hacs_repository.py create mode 100644 custom_components/hacs/operational/setup_actions/sensor.py create mode 100644 custom_components/hacs/operational/setup_actions/websocket_api.py delete mode 100644 custom_components/hacs/repositories/__pycache__/__init__.cpython-37.pyc create mode 100644 custom_components/hacs/repositories/__pycache__/__init__.cpython-38.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/appdaemon.cpython-37.pyc create mode 100644 custom_components/hacs/repositories/__pycache__/appdaemon.cpython-38.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/integration.cpython-37.pyc create mode 100644 custom_components/hacs/repositories/__pycache__/integration.cpython-38.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/manifest.cpython-37.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/netdaemon.cpython-37.pyc create mode 100644 custom_components/hacs/repositories/__pycache__/netdaemon.cpython-38.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/plugin.cpython-37.pyc create mode 100644 custom_components/hacs/repositories/__pycache__/plugin.cpython-38.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/python_script.cpython-37.pyc create mode 100644 custom_components/hacs/repositories/__pycache__/python_script.cpython-38.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/removed.cpython-37.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/repository.cpython-37.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/repositorydata.cpython-37.pyc delete mode 100644 custom_components/hacs/repositories/__pycache__/theme.cpython-37.pyc create mode 100644 custom_components/hacs/repositories/__pycache__/theme.cpython-38.pyc delete mode 100644 custom_components/hacs/services.yaml delete mode 100644 custom_components/hacs/setup.py create mode 100644 custom_components/hacs/share.py delete mode 100644 custom_components/hacs/store.py create mode 100644 custom_components/hacs/translations/cs.json create mode 100644 custom_components/hacs/translations/da.json create mode 100644 custom_components/hacs/translations/de.json rename custom_components/hacs/{.translations => translations}/el.json (85%) create mode 100644 custom_components/hacs/translations/en.json create mode 100644 custom_components/hacs/translations/es.json create mode 100644 custom_components/hacs/translations/et_EE.json create mode 100644 custom_components/hacs/translations/fi.json create mode 100644 custom_components/hacs/translations/fr.json create mode 100644 custom_components/hacs/translations/hu.json create mode 100644 custom_components/hacs/translations/it.json create mode 100644 custom_components/hacs/translations/nb.json create mode 100644 custom_components/hacs/translations/nl.json rename custom_components/hacs/{.translations => translations}/nn.json (74%) create mode 100644 custom_components/hacs/translations/pl.json create mode 100644 custom_components/hacs/translations/pt-BR.json create mode 100644 custom_components/hacs/translations/pt.json rename custom_components/hacs/{.translations => translations}/ro.json (74%) create mode 100644 custom_components/hacs/translations/ru.json rename custom_components/hacs/{.translations => translations}/sl.json (85%) rename custom_components/hacs/{.translations => translations}/sv.json (75%) create mode 100644 custom_components/hacs/translations/vi.json create mode 100644 custom_components/hacs/translations/zh-Hans.json create mode 100644 custom_components/hacs/validate/README.md create mode 100644 custom_components/hacs/validate/__init__.py create mode 100644 custom_components/hacs/validate/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/validate/__pycache__/base.cpython-38.pyc create mode 100644 custom_components/hacs/validate/base.py create mode 100644 custom_components/hacs/validate/common/__pycache__/hacs_manifest.cpython-38.pyc create mode 100644 custom_components/hacs/validate/common/__pycache__/repository_description.cpython-38.pyc create mode 100644 custom_components/hacs/validate/common/__pycache__/repository_information_file.cpython-38.pyc create mode 100644 custom_components/hacs/validate/common/__pycache__/repository_topics.cpython-38.pyc create mode 100644 custom_components/hacs/validate/common/hacs_manifest.py create mode 100644 custom_components/hacs/validate/common/repository_description.py create mode 100644 custom_components/hacs/validate/common/repository_information_file.py create mode 100644 custom_components/hacs/validate/common/repository_topics.py create mode 100644 custom_components/hacs/validate/integration/__pycache__/integration_manifest.cpython-38.pyc create mode 100644 custom_components/hacs/validate/integration/integration_manifest.py create mode 100644 custom_components/hacs/webresponses/__init__.py create mode 100644 custom_components/hacs/webresponses/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/hacs/webresponses/__pycache__/category.cpython-38.pyc create mode 100644 custom_components/hacs/webresponses/__pycache__/frontend.cpython-38.pyc create mode 100644 custom_components/hacs/webresponses/__pycache__/iconset.cpython-38.pyc create mode 100644 custom_components/hacs/webresponses/category.py create mode 100644 custom_components/hacs/webresponses/frontend.py create mode 100644 custom_components/hacs/webresponses/iconset.py delete mode 100644 custom_components/hacs/ws_api_handlers.py create mode 100644 custom_components/input_select/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/lovelace_gen/__init__.py create mode 100644 custom_components/lovelace_gen/__pycache__/__init__.cpython-37.pyc create mode 100644 custom_components/lovelace_gen/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/lovelace_gen/manifest.json create mode 100644 custom_components/nodered/__init__.py create mode 100644 custom_components/nodered/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/nodered/__pycache__/config_flow.cpython-38.pyc create mode 100644 custom_components/nodered/__pycache__/const.cpython-38.pyc create mode 100644 custom_components/nodered/__pycache__/discovery.cpython-38.pyc create mode 100644 custom_components/nodered/__pycache__/websocket.cpython-38.pyc create mode 100644 custom_components/nodered/binary_sensor.py create mode 100644 custom_components/nodered/config_flow.py create mode 100644 custom_components/nodered/const.py create mode 100644 custom_components/nodered/discovery.py create mode 100644 custom_components/nodered/manifest.json create mode 100644 custom_components/nodered/sensor.py create mode 100644 custom_components/nodered/services.yaml create mode 100644 custom_components/nodered/switch.py create mode 100644 custom_components/nodered/translations/en.json create mode 100644 custom_components/nodered/websocket.py create mode 100644 custom_components/variable/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/wundergroundpws/__init__.py create mode 100644 custom_components/wundergroundpws/__pycache__/__init__.cpython-38.pyc create mode 100644 custom_components/wundergroundpws/__pycache__/sensor.cpython-38.pyc create mode 100644 custom_components/wundergroundpws/manifest.json create mode 100644 custom_components/wundergroundpws/sensor.py create mode 100644 custom_components/wundergroundpws/sensor.wundergroundpws.markdown create mode 100644 esphome/inside_temp_humd.yaml rename esphome/{outside-temp-humd.yaml => outside_temp_humd.yaml} (75%) create mode 100644 hass.code-workspace rename lovelace/{ => _manual}/closet.yaml (100%) rename lovelace/{ => _manual}/lights.yaml (100%) create mode 100644 lovelace/_manual/temps.yaml create mode 100644 lovelace/_manual/weather.yaml create mode 100644 lovelace/dashboards.yaml delete mode 100644 lovelace/irrigation.yaml create mode 100644 lovelace/manual.yaml create mode 100644 lovelace/panel.yaml delete mode 100644 lovelace/temps.yaml create mode 100644 packages/darksky_weather.yaml create mode 100644 packages/irrigation/common.yaml create mode 100644 packages/irrigation/zone1.yaml create mode 100644 packages/irrigation/zone2.yaml create mode 100644 packages/irrigation/zone3.yaml create mode 100644 packages/irrigation/zone4.yaml create mode 100644 packages/irrigation/zone5.yaml create mode 100644 packages/irrigation/zone6.yaml create mode 100644 packages/irrigation/zone7.yaml create mode 100644 packages/outside.yaml delete mode 100644 packages/weather.yaml create mode 100644 python_scripts/state_change.py create mode 100644 python_scripts/states_change.py create mode 100644 templates/button-card-templates/default.yaml create mode 100644 templates/button-card-templates/large_value.yaml create mode 100644 themes/clear/clear.yaml create mode 100644 themes/dark_themes/dark_themes.yaml delete mode 100644 ui-lovelace.yaml delete mode 100644 www/community/banner-card/.gitignore delete mode 100644 www/community/banner-card/banner-card.js delete mode 100644 www/community/button-card/.gitignore create mode 100644 www/community/lovelace-darksky-card/lovelace-darksky-card.js create mode 100644 www/community/lovelace-time-picker-card/time-picker-card.js create mode 100644 www/icons/weather_icons/LICENSE create mode 100644 www/icons/weather_icons/animated/cloudy-day-1.svg create mode 100644 www/icons/weather_icons/animated/cloudy-day-2.svg create mode 100644 www/icons/weather_icons/animated/cloudy-day-3.svg create mode 100644 www/icons/weather_icons/animated/cloudy-night-1.svg create mode 100644 www/icons/weather_icons/animated/cloudy-night-2.svg create mode 100644 www/icons/weather_icons/animated/cloudy-night-3.svg create mode 100644 www/icons/weather_icons/animated/cloudy.svg create mode 100644 www/icons/weather_icons/animated/day.svg create mode 100644 www/icons/weather_icons/animated/night.svg create mode 100644 www/icons/weather_icons/animated/rainy-1.svg create mode 100644 www/icons/weather_icons/animated/rainy-2.svg create mode 100644 www/icons/weather_icons/animated/rainy-3.svg create mode 100644 www/icons/weather_icons/animated/rainy-4.svg create mode 100644 www/icons/weather_icons/animated/rainy-5.svg create mode 100644 www/icons/weather_icons/animated/rainy-6.svg create mode 100644 www/icons/weather_icons/animated/rainy-7.svg create mode 100644 www/icons/weather_icons/animated/snowy-1.svg create mode 100644 www/icons/weather_icons/animated/snowy-2.svg create mode 100644 www/icons/weather_icons/animated/snowy-3.svg create mode 100644 www/icons/weather_icons/animated/snowy-4.svg create mode 100644 www/icons/weather_icons/animated/snowy-5.svg create mode 100644 www/icons/weather_icons/animated/snowy-6.svg create mode 100644 www/icons/weather_icons/animated/thunder.svg create mode 100644 www/icons/weather_icons/animated/weather-sprite.svg create mode 100644 www/icons/weather_icons/animated/weather.svg create mode 100644 www/icons/weather_icons/animated/weather_sagittarius.svg create mode 100644 www/icons/weather_icons/animated/weather_sunset.svg create mode 100644 www/icons/weather_icons/static/cloudy-day-1.svg create mode 100644 www/icons/weather_icons/static/cloudy-day-2.svg create mode 100644 www/icons/weather_icons/static/cloudy-day-3.svg create mode 100644 www/icons/weather_icons/static/cloudy-night-1.svg create mode 100644 www/icons/weather_icons/static/cloudy-night-2.svg create mode 100644 www/icons/weather_icons/static/cloudy-night-3.svg create mode 100644 www/icons/weather_icons/static/cloudy.svg create mode 100644 www/icons/weather_icons/static/day.svg create mode 100644 www/icons/weather_icons/static/night.svg create mode 100644 www/icons/weather_icons/static/rainy-1.svg create mode 100644 www/icons/weather_icons/static/rainy-2.svg create mode 100644 www/icons/weather_icons/static/rainy-3.svg create mode 100644 www/icons/weather_icons/static/rainy-4.svg create mode 100644 www/icons/weather_icons/static/rainy-5.svg create mode 100644 www/icons/weather_icons/static/rainy-6.svg create mode 100644 www/icons/weather_icons/static/rainy-7.svg create mode 100644 www/icons/weather_icons/static/snowy-1.svg create mode 100644 www/icons/weather_icons/static/snowy-2.svg create mode 100644 www/icons/weather_icons/static/snowy-3.svg create mode 100644 www/icons/weather_icons/static/snowy-4.svg create mode 100644 www/icons/weather_icons/static/snowy-5.svg create mode 100644 www/icons/weather_icons/static/snowy-6.svg create mode 100644 www/icons/weather_icons/static/thunder.svg create mode 100644 www/icons/weather_icons/static/weather-sprite.svg create mode 100644 www/icons/weather_icons/static/weather.svg create mode 100644 www/icons/weather_icons/static/weather_sagittarius.svg create mode 100644 www/icons/weather_icons/static/weather_sunset.svg create mode 100644 www/panels.yaml create mode 100644 www/sidebar-order.yaml create mode 100644 www/wupws_icons/00.png create mode 100644 www/wupws_icons/01.png create mode 100644 www/wupws_icons/02.png create mode 100644 www/wupws_icons/03.png create mode 100644 www/wupws_icons/04.png create mode 100644 www/wupws_icons/05.png create mode 100644 www/wupws_icons/06.png create mode 100644 www/wupws_icons/07.png create mode 100644 www/wupws_icons/08.png create mode 100644 www/wupws_icons/09.png create mode 100644 www/wupws_icons/10.png create mode 100644 www/wupws_icons/11.png create mode 100644 www/wupws_icons/12.png create mode 100644 www/wupws_icons/13.png create mode 100644 www/wupws_icons/14.png create mode 100644 www/wupws_icons/15.png create mode 100644 www/wupws_icons/16.png create mode 100644 www/wupws_icons/17.png create mode 100644 www/wupws_icons/18.png create mode 100644 www/wupws_icons/19.png create mode 100644 www/wupws_icons/20.png create mode 100644 www/wupws_icons/21.png create mode 100644 www/wupws_icons/22.png create mode 100644 www/wupws_icons/23.png create mode 100644 www/wupws_icons/24.png create mode 100644 www/wupws_icons/25.png create mode 100644 www/wupws_icons/26.png create mode 100644 www/wupws_icons/27.png create mode 100644 www/wupws_icons/28.png create mode 100644 www/wupws_icons/29.png create mode 100644 www/wupws_icons/30.png create mode 100644 www/wupws_icons/31.png create mode 100644 www/wupws_icons/32.png create mode 100644 www/wupws_icons/33.png create mode 100644 www/wupws_icons/34.png create mode 100644 www/wupws_icons/35.png create mode 100644 www/wupws_icons/36.png create mode 100644 www/wupws_icons/37.png create mode 100644 www/wupws_icons/38.png create mode 100644 www/wupws_icons/39.png create mode 100644 www/wupws_icons/40.png create mode 100644 www/wupws_icons/41.png create mode 100644 www/wupws_icons/42.png create mode 100644 www/wupws_icons/43.png create mode 100644 www/wupws_icons/44.png create mode 100644 www/wupws_icons/45.png create mode 100644 www/wupws_icons/46.png create mode 100644 www/wupws_icons/47.png create mode 100644 www/wupws_icons/na.png diff --git a/.HA_VERSION b/.HA_VERSION index 8c53e4e..c107ae9 100644 --- a/.HA_VERSION +++ b/.HA_VERSION @@ -1 +1 @@ -0.107.7 \ No newline at end of file +0.118.4 \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5c8486c..f106a7f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -/.storage/ -/.vscode/ /.cloud/ -*.code-workspace *.js.gz *.log *.db +.gitsecret/keys/random_seed +!*.secret +secrets.yaml diff --git a/.gitsecret/keys/pubring.kbx b/.gitsecret/keys/pubring.kbx new file mode 100644 index 0000000000000000000000000000000000000000..c207002da4501044a4df7740031a4d14b56eb107 GIT binary patch literal 2009 zcmaiz2{hYV9>@P#kc2VC*v1wu39(gMOYP6Dc$k*ZXA6xK5lbkF)Y8&Wicm|us6tlm=A8MRbMO7$`}_Uwx#!%^7XSbx2n2wwjh%gf z!|-@nGk!FV^!MOt0E50M3IOn+0Wcu>AX#LWv`@kCjLM(3gfHEW#C1d;Rv?e6Fd=pC z4abkA4nl5bE;Hh6QH>;_!(+Xp++lzKJ^%nqA1b?VdoTd~w*J@j;{|!3RH%DL1{=f& z3{f2Nwp4|7RhD9~{Z@(9loXg1qvy27MAF4o`USuqt@n?l12HsKw{YYAbu}cDw9x(A z-r^=qEz5^>EW=wak}Bu<*3!xdzA7+~v$9)|m`kZFo?`w%$x+sf(7jj8>bbJHX5SxW zxkM(T=UChMn%$KfI_KvD@5#8Fm|pQ4TwbvV)^*`TIy=3QlIzSglMH}34fPuHER>}l z$S!2Ye6VkNnq0Va`)@{&>PDJJ_O|16Uv{t+v~oAk1SuSCZdY-6J*hA!OKSC}@&INy zTm#pjEhT#SYr{sJ!}OVVhI``;P?Ywy_=m#}h=bCzxkbk;h;Hjsn*Ho4n|lb81v+6Z zUIa%fT_sZ3?Z!Czos?q~oXiUg@6-ziJ>q_5`Sb zi^#8Ww0476={>$;RaJCg>59;pN+kRfM?C5zpUSF?;%tZEHFawdqOjGwrIfh0T>N4v z<^>3CTs$c{53lW>P7X(>Z_@w)5CEz={5?Vt;X4mNtsOa%jJp)zOG5hNy@>b_BnclH z9vI?}RPYH5L57j>NIc0qBr+(}mqb7kd_$3Bymtsbl&px<``(~>*kD8m!1)qYy#oUb z{&hY0I}KFO3#bqX0ceZJLjMg%q@(7!+60^5ikS98HUr_Bsz4O4f#(EbjLtcs6(rG48@z*D2v_kF@Z1;rH+hkgH~sbRM9g#khD|$ewtyNQ`79E zVom3%6TKiZzsN>0aer1R>^Vdh)T6Ib@w-bJ;?~GNE2T3mmL^Ym!=7ElbGQmB<$ra7gez_pL$7KolMq^aGbRcS7!cDEp+F1C2TzR znUR>yp6bjj+9T`Q`L)ye{`3&uPo`STMWA#LC-1Bi-pxo4-uP08co=ueL$p%%IRPzP z%J!e*PPUO_89GGsosBem7+d)`D+RNO%3#`PY=xi44zj$mj;|i1X)w!su3NoFpFoRp zI9WLP<)Z%h*xNcubMPE09-W{TlZ{c1=$%^^ zmup%mPENa#V6=FYJvQOBb**bN_NEvy;?OZ-Sz$!t$bV0ws?)(%5M%|ueZsZd zQn+8uX|%f(Gn-F}-}UL$mJe}B{M`6)RG)d;UG6|bW`z0xilhswaO+C6h)emzq7dgmI|?LMY5 zXf7nPKFjD;2(y`g?{ zH5K#}Dt2fzpoZgq2a~Nzz;i+ z=TE<8r=*=nG`zGRQjadTJ^`v_hztGolOmKkj z;|)>yPPXTjvu_8O7*lp(VT7_gCFgbVy8Wo8jd{zf>N7XJLBzg*$IoUldaOZrw|U5t zIQ^Ztx}Z+4z&Tz&QB&&r=gNuG%P562G4wvfqrKq%DfHqE=`x9li`v>JJ>55FE18oM zV721($X^n2eNfdf1})Q{h8})J73Zn0f{yh?e8gcb{CxxmTeZJ_{CsUfWayPbxfDq7 zrLuBg&Z^SI=}CvJ+6XNtoO2p`uaR^2IA+", + "type": "sensor", + "unique_id": "wifi_connection", + "webhook_id": "972369ddfba8b95dfc4b3ab68f74f19e19227919102c4720d1fc38d9fd45a0b4" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_battery_level": { + "added": true, + "attributes": {}, + "device_class": "battery", + "icon": "mdi:battery-50", + "name": "Battery Level", + "state": 58, + "type": "sensor", + "unique_id": "battery_level", + "unit_of_measurement": "%", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_battery_state": { + "added": true, + "attributes": { + "battery_health": "good", + "charger_type": "unknown", + "is_charging": false + }, + "device_class": "battery", + "icon": "mdi:battery-50", + "name": "Battery State", + "state": "discharging", + "type": "sensor", + "unique_id": "battery_state", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_bluetooth_connection": { + "added": true, + "attributes": { + "connected_not_paired_devices": [], + "connected_paired_devices": [], + "is_bt_on": false, + "paired_devices": "" + }, + "icon": "mdi:bluetooth", + "name": "Bluetooth Connection", + "state": 0, + "type": "sensor", + "unique_id": "bluetooth_connection", + "unit_of_measurement": "connection(s)", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_geocoded_location": { + "added": true, + "attributes": { + "Administrative Area": "Oregon", + "Country": "United States", + "ISO Country Code": "US", + "Locality": "Prairie City", + "Location": [ + 44.4609349, + -118.71165899999998 + ], + "Postal Code": "97869", + "Sub Administrative Area": "Grant County", + "Sub Locality": null, + "Sub Thoroughfare": "258", + "Thoroughfare": "South McHaley Avenue" + }, + "icon": "mdi:map", + "name": "Geocoded Location", + "state": "258 S McHaley Ave, Prairie City, OR 97869, USA", + "type": "sensor", + "unique_id": "geocoded_location", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_light_sensor": { + "added": true, + "attributes": {}, + "device_class": "illuminance", + "icon": "mdi:brightness-5", + "name": "Light Sensor", + "state": "unavailable", + "type": "sensor", + "unique_id": "light_sensor", + "unit_of_measurement": "lx", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_next_alarm": { + "added": true, + "attributes": { + "Local Time": "", + "Package": "", + "Time in Milliseconds": 0 + }, + "device_class": "timestamp", + "icon": "mdi:alarm", + "name": "Next Alarm", + "state": "unavailable", + "type": "sensor", + "unique_id": "next_alarm", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_storage_sensor": { + "added": true, + "attributes": { + "Free external storage": "17GB", + "Free internal storage": "0GB", + "Total external storage": "29GB", + "Total internal storage": "24GB" + }, + "icon": "mdi:harddisk", + "name": "Storage Sensor", + "state": 3, + "type": "sensor", + "unique_id": "storage_sensor", + "unit_of_measurement": "%", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0_wifi_connection": { + "added": true, + "attributes": { + "bssid": "0c:80:63:91:4c:91", + "frequency": 5180, + "ip_address": "10.0.0.81", + "is_hidden": false, + "is_wifi_on": true, + "link_speed": 390, + "signal_level": -63 + }, + "icon": "mdi:wifi-strength-2", + "name": "Wifi Connection", + "state": "zoesplace-outside", + "type": "sensor", + "unique_id": "wifi_connection", + "webhook_id": "b0bd2f0affedb208d53f95e0f264ec0ba55e5c867ee3a864dd94e29c3f8a8db0" + }, + "e689e9489fb188efebd2131925467e20069b7725bbd125ef8f8765986e124d0f_battery_level": { + "added": true, + "attributes": { + "charger_type": "AC", + "is_charging": true + }, + "device_class": "battery", + "icon": "mdi:battery-charging", + "name": "Battery Level", + "state": 100, + "type": "sensor", + "unique_id": "battery_level", + "unit_of_measurement": "%", + "webhook_id": "e689e9489fb188efebd2131925467e20069b7725bbd125ef8f8765986e124d0f" + }, + "e689e9489fb188efebd2131925467e20069b7725bbd125ef8f8765986e124d0f_geocoded_location": { + "added": true, + "attributes": { + "Administrative Area": "Oregon", + "Country": "United States", + "ISO Country Code": "US", + "Locality": "Prairie City", + "Location": [ + 44.4611229, + -118.71172790000001 + ], + "Postal Code": "97869", + "Sub Administrative Area": "Grant County", + "Sub Locality": null, + "Sub Thoroughfare": "238", + "Thoroughfare": "North McHaley Street" + }, + "icon": "mdi:map", + "name": "Geocoded Location", + "state": "238 N McHaley St, Prairie City, OR 97869, USA", + "type": "sensor", + "unique_id": "geocoded_location", + "webhook_id": "e689e9489fb188efebd2131925467e20069b7725bbd125ef8f8765986e124d0f" + }, + "e689e9489fb188efebd2131925467e20069b7725bbd125ef8f8765986e124d0f_wifi_connection": { + "added": true, + "attributes": { + "bssid": "0c:80:63:91:4c:90", + "frequency": 2437, + "ip_address": "10.0.0.182", + "is_hidden": false, + "link_speed": 72, + "signal_level": -39 + }, + "icon": "mdi:wifi-strength-3", + "name": "Wifi Connection", + "state": "zoesplace-outside", + "type": "sensor", + "unique_id": "wifi_connection", + "webhook_id": "e689e9489fb188efebd2131925467e20069b7725bbd125ef8f8765986e124d0f" + } + } + } +} \ No newline at end of file diff --git a/.storage/onboarding b/.storage/onboarding new file mode 100644 index 0000000..691451b --- /dev/null +++ b/.storage/onboarding @@ -0,0 +1,11 @@ +{ + "data": { + "done": [ + "user", + "core_config", + "integration" + ] + }, + "key": "onboarding", + "version": 3 +} \ No newline at end of file diff --git a/.storage/person b/.storage/person new file mode 100644 index 0000000..6bdb79a --- /dev/null +++ b/.storage/person @@ -0,0 +1,19 @@ +{ + "data": { + "items": [ + { + "device_trackers": [ + "device_tracker.700t1c", + "device_tracker.sm_g935r4", + "device_tracker.xt1064_2", + "device_tracker.lgus215" + ], + "id": "sysadmin", + "name": "sysadmin", + "user_id": "99685dc45d9f40dc8183e11ce5128038" + } + ] + }, + "key": "person", + "version": 2 +} \ No newline at end of file diff --git a/.storage/zone b/.storage/zone new file mode 100644 index 0000000..de7b529 --- /dev/null +++ b/.storage/zone @@ -0,0 +1,7 @@ +{ + "data": { + "items": [] + }, + "key": "zone", + "version": 1 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a04b218 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "*.yaml": "home-assistant" + } +} \ No newline at end of file diff --git a/configs/http.yaml b/archive/configs/http.yaml similarity index 100% rename from configs/http.yaml rename to archive/configs/http.yaml diff --git a/configs/mqtt.yaml b/archive/configs/mqtt.yaml similarity index 96% rename from configs/mqtt.yaml rename to archive/configs/mqtt.yaml index 8708d3c..7c134c1 100644 --- a/configs/mqtt.yaml +++ b/archive/configs/mqtt.yaml @@ -6,6 +6,7 @@ #port: 1883 #username: !secret MQTT_username #password: !secret MQTT_password +#discovery: true birth_message: topic: "status/ha" payload: "online" diff --git a/configs/panel.yaml b/archive/configs/panel.yaml similarity index 100% rename from configs/panel.yaml rename to archive/configs/panel.yaml diff --git a/configs/scripts.yaml b/archive/configs/scripts.yaml similarity index 100% rename from configs/scripts.yaml rename to archive/configs/scripts.yaml diff --git a/archive/lovelace/bak.irrigation.yaml. b/archive/lovelace/bak.irrigation.yaml. new file mode 100644 index 0000000..e4c2cf2 --- /dev/null +++ b/archive/lovelace/bak.irrigation.yaml. @@ -0,0 +1,18 @@ +# lovelace_gen + +# {% zones = [ '1':'Front North', '2':'Front South', '3':'Front Beds'} %} +icon: mdi:water +cards: + - type: vertical-stack + cards: + - type: markdown + content: > + # Irrigation + # {% for zone in zones %} + # - type: entity-button + # entity: switch.irrigation_zone_{{ zone }} + # icon: mdi:water + # name: zones[zone] + # tap_action: + # action: toggle + # {% endfor %} diff --git a/archive/lovelace/button-card.yaml.j2 b/archive/lovelace/button-card.yaml.j2 new file mode 100644 index 0000000..a9f4d8c --- /dev/null +++ b/archive/lovelace/button-card.yaml.j2 @@ -0,0 +1,8 @@ +# lovelace_gen +{% if entity.startswith("light") %} +type: light +{% else %} +type: entity-button +{% endif %} +entity: {{ entity }} +name: {{ name }} diff --git a/lovelace/cameras.yaml b/archive/lovelace/cameras.yaml similarity index 100% rename from lovelace/cameras.yaml rename to archive/lovelace/cameras.yaml diff --git a/lovelace/fio.yaml b/archive/lovelace/fio.yaml similarity index 100% rename from lovelace/fio.yaml rename to archive/lovelace/fio.yaml diff --git a/lovelace/gios.yaml b/archive/lovelace/gpios.yaml similarity index 97% rename from lovelace/gios.yaml rename to archive/lovelace/gpios.yaml index 9e4ec53..0bb6e19 100644 --- a/lovelace/gios.yaml +++ b/archive/lovelace/gpios.yaml @@ -4,7 +4,7 @@ cards: cards: - type: markdown content: > - # Control GPIO Relays on Pine64 + # Control UCI GPIO Relays - type: horizontal-stack cards: - type: entity-button diff --git a/archive/lovelace/irrigation-buttons-old.yaml.j2 b/archive/lovelace/irrigation-buttons-old.yaml.j2 new file mode 100644 index 0000000..75896c4 --- /dev/null +++ b/archive/lovelace/irrigation-buttons-old.yaml.j2 @@ -0,0 +1,34 @@ +# lovelace_gen +title: MQTT Irrigation Buttons +{# icon: mdi:water #} +panel: true +cards: + - type: 'custom:layout-card' + min_columns: 2 + max_columns: 4 + column_width: 200px + cards: + - type: entity-button + entity: switch.irrigation_pump + icon: mdi:water-pump + name: well pump + tap_action: + action: toggle + {% set zones = { + '1':'Front Yard North', + '2':'Front Yard South', + '3':'Fr. Beds/Back E. Spigot', + '4':'Back Garage Spigot', + '5':'Garden North/West', + '6':'Garden South', + '7':'Back West Spigot' + } + %} + {% for zone,name in zones.items() %} + - type: entity-button + entity: switch.irrigation_zone_{{ zone }} + icon: mdi:water + name: {{ name }} + tap_action: + action: toggle + {% endfor %} diff --git a/archive/lovelace/irrigation-buttons.yaml.j2 b/archive/lovelace/irrigation-buttons.yaml.j2 new file mode 100644 index 0000000..72c477b --- /dev/null +++ b/archive/lovelace/irrigation-buttons.yaml.j2 @@ -0,0 +1,34 @@ +# lovelace_gen +title: Irrigation +icon: mdi:water +panel: true +cards: + - type: 'custom:layout-card' + min_columns: 2 + max_columns: 4 + column_width: 200px + cards: + - type: entity-button + entity: input_boolean.irrigation_pump + icon: mdi:water-pump + name: well pump + tap_action: + action: toggle + {% set zones = { + '1':'Front Yard North', + '2':'Front Yard South', + '3':'Fr. Beds/Back E. Spigot', + '4':'Back Garage Spigot', + '5':'Garden North/West', + '6':'Garden South', + '7':'Back West Spigot' + } + %} + {% for zone,name in zones.items() %} + - type: entity-button + entity: input_boolean.irrigation_zone_{{ zone }}_state + icon: mdi:water + name: {{ name }} + tap_action: + action: toggle + {% endfor %} diff --git a/archive/lovelace/irrigation.yaml.j2 b/archive/lovelace/irrigation.yaml.j2 new file mode 100644 index 0000000..b6d4d69 --- /dev/null +++ b/archive/lovelace/irrigation.yaml.j2 @@ -0,0 +1,70 @@ +# lovelace_gen +title: Irrigation +cards: +{% set zones = { + '1':'Front Yard North', + '2':'Front Yard South', + '3':'Fr. Beds/Back E. Spigot', + '4':'Back Garage Spigot', + '5':'Garden North/West', + '6':'Garden South', + '7':'Back West Spigot' + } + %} + - type: vertical-stack + cards: + - type: markdown + content: > + # IRRIGATION SYSTEM STATE + theme: solarized_light + - type: entities + show_header_toggle: false + entities: + - entity: input_boolean.irrigation_enabled + - type: divider + - entity: variable.irrigation_next_trigger + - entity: variable.irrigation_next_trigger_dt + - entity: variable.irrigation_next_schedule_name + - entity: variable.irrigation_running_names + - entity: variable.irrigation_queue_names + - type: divider + {% for zone,name in zones.items() %} + - entity: input_boolean.irrigation_zone_{{ zone }}_schedule_enabled + name: {{ name }} + {% endfor %} + {% for zone,name in zones.items() %} + - type: conditional + conditions: + - entity: input_boolean.irrigation_zone_{{ zone }}_schedule_enabled + state: "on" + card: + type: vertical-stack + cards: + - type: markdown + content: > + ## {{ name }} : Zone {{ zone }} + theme: slate + - type: entities + entities: + - entity: input_boolean.irrigation_zone_{{ zone }}_state + - entity: input_number.irrigation_zone_{{ zone }}_duration + - type: divider + - entity: variable.irrigation_zone_{{ zone }}_schedule_next_dt + - entity: variable.irrigation_zone_{{ zone }}_schedule_countdown + - type: entities + entities: + - type: custom:fold-entity-row + head: input_boolean.irrigation_zone_{{ zone }}_schedule_enabled + entities: + - type: section + label: 'Schedule Parameters for {{name}}' + - type: divider + - type: section + label: 'Base Time of Day' + - entity: input_number.irrigation_zone_{{ zone }}_schedule_base_hour + - entity: input_number.irrigation_zone_{{ zone }}_schedule_base_minute + - type: divider + - entity: input_select.irrigation_zone_{{ zone }}_schedule_delta + + + {% endfor %} diff --git a/archive/lovelace/irrigation.yaml.j2.old b/archive/lovelace/irrigation.yaml.j2.old new file mode 100644 index 0000000..700af14 --- /dev/null +++ b/archive/lovelace/irrigation.yaml.j2.old @@ -0,0 +1,19 @@ +# lovelace_gen +{% set zones = ['1','2','3','4','5','6','7','8'] %} +icon: mdi:water +cards: + - type: vertical-stack + cards: + - type: markdown + content: > + # Irrigation + - type: horizontal-stack + cards: + {% for zone in zones %} + - type: entity-button + entity: switch.irrigation_zone_{{ zone }} + icon: mdi:water + name: {{ zone }} + tap_action: + action: toggle + {% endfor %} diff --git a/archive/lovelace/irrigation0.yaml.j2 b/archive/lovelace/irrigation0.yaml.j2 new file mode 100644 index 0000000..570d360 --- /dev/null +++ b/archive/lovelace/irrigation0.yaml.j2 @@ -0,0 +1,30 @@ +# lovelace_gen +{% set zones = { +'1':'Front Yard North', +'2':'Front Yard South', +'3':'Front Yard Beds', +'4':'Back Yard East', +'5':'Back Yard Garage', +'6':'Back Yard West', +'7':'Garden East', +'8':'Garden West' +} +%} +icon: mdi:water +panel: true +cards: + - type: 'custom:layout-card' + {# layout: #} + {# min_height: #} + min_columns: 2 + max_columns: 4 + column_width: 200px + cards: + {% for zone,name in zones.items() %} + - type: entity-button + entity: switch.irrigation_zone_{{ zone }} + icon: mdi:water + name: {{ name }} + tap_action: + action: toggle + {% endfor %} diff --git a/archive/lovelace/layout-testing.yaml b/archive/lovelace/layout-testing.yaml new file mode 100644 index 0000000..13accc6 --- /dev/null +++ b/archive/lovelace/layout-testing.yaml @@ -0,0 +1,29 @@ +path: tiles +title: Tile Test +panel: true +cards: + - type: 'custom:layout-card' + layout: auto + # min_height: + # min_columns: 2 + # max_columns: 20 + # column_width: 50px + # max_width: + # min_width: 50px + flex_grow: 5 + # gridcols: + # gridrows: + justify_content: flex-start + cards: + - type: 'custom:button-card' # + entity: sensor.outside_temperature # + name: Outside # + label: 238 McHaley + template: large_value # + - type: 'custom:button-card' # + entity: sensor.inside_temperature # + name: Inside # + label: 238 McHaley + template: large_value # + - type: weather-forecast + entity: weather.praire_city_weather diff --git a/archive/lovelace/layout.yaml b/archive/lovelace/layout.yaml new file mode 100644 index 0000000..e6c9531 --- /dev/null +++ b/archive/lovelace/layout.yaml @@ -0,0 +1,58 @@ +path: tiles +title: Tile Test +theme: Backend-selected +badges: [] +panel: true +cards: + - type: 'custom:layout-card' + column_width: 100% + layout: vertical + cards: + - type: 'custom:layout-card' + layout: grid + gridcols: 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px + gridrows: 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px 50px + cards: + - type: 'custom:button-card' # + entity: sensor.outside_temperature # + gridcol: 1/3 # Your first + gridrow: 1/3 # card + name: Outside # + label: 238 McHaley + template: large_value # + - type: custom:canvas-gauge-card + entity: sensor.outside_humidity + name: Humidity (%) + card_height: 135 + gridcol: 4/7 + gridrow: 1/3 + gauge: + type: "radial-gauge" + width: 120 + height: 120 + # borderShadowWidth: 0 + # borderOuterWidth: 0 + # borderMiddleWidth: 0 + # borderInnerWidth: 0 + # minValue: 0 + # maxValue: 100 + # startAngle: 90 + # ticksAngle: 180 + # valueBox: true + # majorTicks: + # ["0", "10", "20", "30", "40", "50", "60", "70", "80", "90", "100"] + # minorTicks: 2 + # strokeTicks: true + # highlights: [{ "from": 80, "to": 100, "color": "rgba(200, 50, 50, .75)" }] + # borders: true + - type: 'custom:button-card' # + entity: sensor.inside_temperature # + gridcol: 1/3 # Your first + gridrow: 4/7 # card + name: Inside # + label: 238 McHaley + template: large_value # + # - type: weather-forecast + # entity: weather.praire_city_weather + # gridcol: 4/14 # Your first + # gridrow: 1/5 diff --git a/archive/lovelace/node-red-scheduler.yaml b/archive/lovelace/node-red-scheduler.yaml new file mode 100644 index 0000000..953603b --- /dev/null +++ b/archive/lovelace/node-red-scheduler.yaml @@ -0,0 +1,6 @@ +# Exampe Scheduler +title: Node Red Scheduler +cards: + - type: iframe + url: 'https://giskard.kebler.net:1880/endpoint/ui' + aspect_ratio: 100% \ No newline at end of file diff --git a/lovelace/node_testing.yaml b/archive/lovelace/node_testing.yaml similarity index 100% rename from lovelace/node_testing.yaml rename to archive/lovelace/node_testing.yaml diff --git a/lovelace/scheduler.yaml b/archive/lovelace/scheduler.yaml similarity index 95% rename from lovelace/scheduler.yaml rename to archive/lovelace/scheduler.yaml index 1dccc9b..ed78df5 100644 --- a/lovelace/scheduler.yaml +++ b/archive/lovelace/scheduler.yaml @@ -6,6 +6,9 @@ cards: ## Scheduler Example - type: vertical-stack cards: + - type: markdown + content: > + ## Scheduler 2 - type: entities entities: # - entity: input_datetime.test_schedule_base diff --git a/lovelace/timer.yaml b/archive/lovelace/timer.yaml similarity index 100% rename from lovelace/timer.yaml rename to archive/lovelace/timer.yaml diff --git a/lovelace/uci-lighting.yaml b/archive/lovelace/uci-lighting.yaml similarity index 100% rename from lovelace/uci-lighting.yaml rename to archive/lovelace/uci-lighting.yaml diff --git a/archive/lovelace/zone_1.yaml b/archive/lovelace/zone_1.yaml new file mode 100644 index 0000000..d0d652a --- /dev/null +++ b/archive/lovelace/zone_1.yaml @@ -0,0 +1,43 @@ +# Exampe Scheduler +title: Zone1 +cards: + - type: markdown + content: > + ## Scheduler Example + - type: vertical-stack + cards: + - type: entities + entities: + # - entity: input_datetime.irrigation_zone_1_schedule_base + - type: section + label: 'Base Time of Day' + - entity: input_number.irrigation_zone_1_schedule_base_hour + - entity: input_number.irrigation_zone_1_schedule_base_minute + - type: divider + - entity: input_select.irrigation_zone_1_schedule_repeatin + # - entity: variable.irrigation_zone_1_schedule_countdown + # - entity: variable.irrigation_zone_1_schedule_next_timestamp + - type: divider + - entity: variable.irrigation_zone_1_schedule_next + - entity: sensor.irrigation_zone_1_schedule_next + - entity: variable.irrigation_zone_1_schedule_countdown + - type: horizontal-stack + cards: + - type: entity-button + name: Enable Scheduler + icon: mdi:timer + tap_action: + action: call-service + service: script.turn_on + service_data: + entity_id: script.enable_irrigation_zone_1_schedule + entity: script.enable_irrigation_zone_1_schedule + - type: entity-button + name: Disable Scheduler + icon: mdi:timer + tap_action: + action: call-service + service: script.turn_on + service_data: + entity_id: script.disable_irrigation_zone_1_schedule + entity: script.disable_irrigation_zone_1_schedule diff --git a/archive/packages/alarm.yaml.off b/archive/packages/alarm.yaml.off new file mode 100644 index 0000000..7ea26d2 --- /dev/null +++ b/archive/packages/alarm.yaml.off @@ -0,0 +1,76 @@ +sensor: + - platform: template + sensors: + alarm_time: + friendly_name: "Time" + value_template: "{{ '%0.02d:%0.02d' | format(states('input_number.alarmhour') | int, states('input_number.alarmminutes') | int) }}" + - platform: time_date + display_options: + - 'time' + - 'date' + - 'date_time' + - 'time_date' + - 'time_utc' + +automation: + - alias: 'Wake Me Up' + trigger: + platform: template + value_template: "{{ states.sensor.time.state == states.sensor.alarm_time.state }}" + condition: + condition: or + conditions: + - condition: and + conditions: + - condition: state + entity_id: input_boolean.alarmweekday + state: 'on' + - condition: time + weekday: + - mon + - tue + - wed + - thu + - fri + - condition: state + entity_id: input_boolean.alarmweekday + state: 'off' + action: + service: notify.notify + data_template: + message: 'Good morning. Time to Wake Up!' + title: '' + +group: + default_view: + view: yes + entities: + - group.alarmclock + + alarmclock: + name: Wake Me Up + entities: + - automation.wake_me_up + - sensor.alarm_time + - input_number.alarmhour + - input_number.alarmminutes + - input_boolean.alarmweekday + +input_boolean: + alarmweekday: + name: Weekdays Only + icon: mdi:calendar + +input_number: + alarmhour: + name: Hour + icon: mdi:timer + min: 0 + max: 23 + step: 1 + alarmminutes: + name: Minutes + icon: mdi:timer + min: 0 + max: 59 + step: 5 diff --git a/packages/cameras.yaml.off b/archive/packages/cameras.yaml.off similarity index 100% rename from packages/cameras.yaml.off rename to archive/packages/cameras.yaml.off diff --git a/archive/packages/irrigation.yaml b/archive/packages/irrigation.yaml new file mode 100644 index 0000000..917b82f --- /dev/null +++ b/archive/packages/irrigation.yaml @@ -0,0 +1,78 @@ +# package of switches to test gpio pins/relays for @uci/gpio example +# gpio pins on pine64 in order for relays 1-8 const PINS = [80,73,69,230,229,75,74,70] +switch: + - platform: mqtt + name: "uci Switch 1" + state_topic: "relay/status/80" + command_topic: "relay/set/80" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + - platform: mqtt + name: "pine64 Switch 2" + state_topic: "relay/status/73" + command_topic: "relay/set/73" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + - platform: mqtt + name: "pine64 Switch 3" + state_topic: "relay/status/69" + command_topic: "relay/set/69" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + - platform: mqtt + name: "pine64 Switch 4" + state_topic: "relay/status/230" + command_topic: "relay/set/230" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + - platform: mqtt + name: "pine64 Switch 5" + state_topic: "relay/status/229" + command_topic: "relay/set/229" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + - platform: mqtt + name: "pine64 Switch 6" + state_topic: "relay/status/75" + command_topic: "relay/set/75" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + - platform: mqtt + name: "pine64 Switch 7" + state_topic: "relay/status/74" + command_topic: "relay/set/74" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + - platform: mqtt + name: "pine64 Switch 8" + state_topic: "relay/status/70" + command_topic: "relay/set/70" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:lightbulb + + + # copy and paste and uncomment below as a view under views: in ui-lovelace.yaml diff --git a/archive/packages/irritation-old-extras.yaml.off b/archive/packages/irritation-old-extras.yaml.off new file mode 100644 index 0000000..6009444 --- /dev/null +++ b/archive/packages/irritation-old-extras.yaml.off @@ -0,0 +1,165 @@ +# - Day +# - Every Other Day +# - Every Third Day +# - Every Week +# values: +# - 3 +# - 6 +# - 12 +# - 24 +# - 48 +# - 72 +# - 168 + +# sensor: +# - platform: template # Derived values +# sensors: +# irrigation_zone_1_schedule_delta: # computes delta based on choice +# entity_id: input_select.irrigation_zone_1_schedule_repeatin +# unit_of_measurement: 'hours' +# value_template: > +# {% for option in state_attr("input_select.irrigation_zone_1_schedule_repeatin", "options") -%} +# {% if is_state("input_select.irrigation_zone_1_schedule_repeatin", option) -%} +# {{ state_attr("input_select.irrigation_zone_1_schedule_repeatin", 'values')[loop.index - 1] }} +# {%- endif %} +# {%- endfor %} + + + + +# format the next timestamp for humans +# irrigation_zone_1_schedule_next: +# friendly_name: Next run to start at +# entity_id: variable.irrigation_zone_1_schedule_next_timestamp +# value_template: '{{ states.variable.irrigation_zone_1_schedule_next.state | int | timestamp_custom("%A, %d %h %H:%M") }}' +# irrigation_zone_1_schedule_countdown: +# friendly_name: Countdown to next run +# entity_id: variable.irrigation_zone_1_schedule_countdown +# # value_template: '{{ states.variable.irrigation_zone_1_schedule_countdown.state | int | timestamp_custom("%A, %d %h %H:%M") }}' +# value_template: >- +# {% set time = states.variable.irrigation_zone_1_schedule_countdown.state | int %} +# {% set minutes = ((time % 3600) / 60) | int %} +# {% set hours = ((time % 86400) / 3600) | int %} +# {% set days = (time / 86400) | int %} +# {{time}} seconds is {{ days }}:{{ hours }}:{{minutes}} (D:H:M) + + + +# switch: +# - platform: mqtt +# name: "Scheduler Test Solenoid" +# state_topic: "status/irrigation/zone_1" +# command_topic: "set/irrigation/zone_1" +# payload_on: "ON" +# payload_off: "OFF" +# qos: 0 +# retain: true + + +# script: +# enable_irrigation_zone_1_schedule: +# sequence: +# - event: ENABLE_SCHEDULE +# - service: variable.set_variable +# data: +# variable: irrigation_zone_1_schedule_countdown +# value_template: '{{ states.variable.irrigation_zone_1_schedule_next_timestamp.state | int - as_timestamp(now()) }}' + # value_template: "{{ as_timestamp(now()) }}" +# - service: variable.set_variable + # data: + # variable: irrigation_zone_1_schedule_dummy_device + # value: 'ON' +# - service: automation.turn_on +# entity_id: automation.irrigation_zone_1_schedule_countdown +# - service: automation.turn_on +# data: +# entity_id: automation.irrigation_zone_1_timer_zero_trigger +# disable_irrigation_zone_1_schedule: +# sequence: +# - service: automation.turn_off +# data: +# entity_id: automation.irrigation_zone_1_schedule_countdown +# - service: variable.set_variable +# data: +# variable: irrigation_zone_1_schedule_countdown +# value: 0 +# automation: +# - alias: irrigation_zone_1_schedule_countdown +# initial_state: false +# trigger: +# platform: time_pattern +# seconds: '/1' +# action: +# - service: variable.set_variable +# data: +# variable: irrigation_zone_1_schedule_countdown + # value_template: '{{ [((variable.state | int) - 1), 0] | max }}' +# - alias: irrigation_zone_1_schedule_changed +# # initial_state: false +# trigger: +# platform: state +# action: +# - service: variable.set_variable +# data: +# variable: irrigation_zone_1_schedule_countdown +# value_template: '{{ states.variable.irrigation_zone_1_schedule_next_timestamp.state | int - as_timestamp(now()) }}' + + +# - service: automation.turn_off +# data: +# entity_id: automation.irrigation_zone_1_timer_zero_trigger +# - service: variable.set_variable +# data: +# variable: irrigation_zone_1_timer_device +# value: 'OFF' + +# +# + +# - alias: irrigation_zone_1_timer_zero_trigger +# initial_state: false +# trigger: +# platform: numeric_state +# entity_id: variable.irrigation_zone_1_timer_countdown +# below: 1 +# action: +# - service: automation.turn_off +# entity_id: automation.irrigation_zone_1_timer_countdown +# - service: variable.set_variable +# data: +# variable: irrigation_zone_1_timer_device +# value: 'OFF' +# # + + + + + +# return corresponding value from input_select option of same name +# irrigation_zone_1_schedule_delta: +# entity_id: input_select.irrigation_zone_1_schedule_repeatin +# value_template: > +# {% for option in state_attr("input_select.irrigation_zone_1_schedule_repeatin", "options") -%} +# {% if is_state("input_select.irrigation_zone_1_schedule_repeatin", option) -%} +# {{ state_attr("input_select.irrigation_zone_1_schedule_repeatin", 'values')[loop.index - 1] }} +# {%- endif %} +# {%- endfor %} +# irrigation_zone_1_schedule_base_timestamp: +# entity_id: input_datetime.irrigation_zone_1_schedule_base +# value_template: > +# {{ +# as_timestamp(now()) +# - ( now().second + now().minute | int * 60 + now().hour | int * 3600 ) +# + state_attr('input_datetime.irrigation_zone_1_schedule_base','hour')|int * 3600 +# + state_attr('input_datetime.irrigation_zone_1_schedule_base','minute') * 60 +# }} + # value_template: "{{ (state_attr('input_datetime.irrigation_zone_1_schedule_base','hour')|int * 3600 + state_attr('input_datetime.irrigation_zone_1_schedule_base','minute')|int * 60) | timestamp_custom('%A, %d %h %H:%M') }}" + # value_template: > + # "{{ (state_attr('input_datetime.irrigation_zone_1_schedule_base','hour')|int * 3600 + + # state_attr('input_datetime.irrigation_zone_1_schedule_base','minute')|int * 60 + + # as_timestamp(now())) + # | timestamp_custom("%A, %d %h %H:%M") }}" + + # value_template: "{{ states(input_datetime.irrigation_zone_1_schedule_base) }}" +# {{ as_timestamp(now()) + as_timestamp(states.input_datetime.irrigation_zone_1_schedule_base) }} +# {{ as_timestamp(now()) + as_timestamp(states.input_datetime.irrigation_zone_1_schedule_base) }} diff --git a/packages/node_test.yaml b/archive/packages/node_test.yaml similarity index 100% rename from packages/node_test.yaml rename to archive/packages/node_test.yaml diff --git a/archive/packages/sprinklers.yaml.off b/archive/packages/sprinklers.yaml.off new file mode 100644 index 0000000..5c5bc5d --- /dev/null +++ b/archive/packages/sprinklers.yaml.off @@ -0,0 +1,47 @@ +homeassistant: + # Customize for Sprinklers - map relay to sprinkler name/location + customize: + switch.relay1: + friendly_name: Front Yard North + # sensor.closet_temperature: + # friendly_name: Closet Temperature (C) + # input_number.fan_on_temp: + # friendly_name: Fan On Set Temperature (C) + +# 8 Relay Board, Topic number is gpio pin +switch: + - platform: mqtt + name: "relay1" # Pine64 gpio 80 is pin 40 + state_topic: "relays/status/80" + command_topic: "relays/set/80" + state_on: "on" + state_off: "off" + payload_on: "on" + payload_off: "off" + icon: mdi:water + +# This automation script runs when a value is received via MQTT on retained topic: setTemperature +# It sets the value slider on the GUI. This slides also had its own automation when the value is changed. +automation: + # - alias: Closet Fan On Set Temperature slider + # trigger: + # platform: mqtt + # topic: 'closet/status/fan/automation/temp' + # action: + # service: input_number.set_value + # data_template: + # entity_id: input_number.fan_on_temp + # value: "{{ trigger.payload }}" + +# This second automation script runs when the target temperature slider is moved. +# It publishes its value to the same MQTT topic it is also subscribed to. + # - alias: Closet Fan Temp Slider Moved + # trigger: + # platform: state + # entity_id: input_number.fan_on_temp + # action: + # service: mqtt.publish + # data_template: + # topic: 'closet/fan/automation/temp' + # retain: true + # payload: "{{ states('input_number.fan_on_temp') }}" diff --git a/archive/packages/temp.yaml.off b/archive/packages/temp.yaml.off new file mode 100644 index 0000000..7140777 --- /dev/null +++ b/archive/packages/temp.yaml.off @@ -0,0 +1,131 @@ +- id: lounge_aircon_manual + alias: 'Lounge Room Aircon' + hide_entity: True + trigger: + platform: state + entity_id: input_select.lounge_ac_mode + action: + - service_template: > + {% if is_state('input_select.lounge_ac_mode', 'Powerful Heat') %} shell_command.lounge_ac_powerful_heat + {% elif is_state('input_select.lounge_ac_mode', 'Normal Heat') %} shell_command.lounge_ac_normal_heat + {% elif is_state('input_select.lounge_ac_mode', 'Silent Heat') %} shell_command.lounge_ac_silent_heat + {% elif is_state('input_select.lounge_ac_mode', 'Powerful Cool') %} shell_command.lounge_ac_powerful_cool + {% elif is_state('input_select.lounge_ac_mode', 'Normal Cool') %} shell_command.lounge_ac_normal_cool + {% elif is_state('input_select.lounge_ac_mode', 'Silent Cool') %} shell_command.lounge_ac_silent_cool + {% elif is_state('input_select.lounge_ac_mode', 'Dry') %} shell_command.lounge_ac_dry + {% elif is_state('input_select.lounge_ac_mode', 'Off') %} shell_command.lounge_ac_off + {% endif %} + +- id: lounge_aircon_auto_am_on + alias: 'Lounge Room Aircon Scheduled AM On' + trigger: + platform: template + value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_am_on_time.state[0:5]) }}" + condition: + condition: and + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_am_automation # If automation is required and ... + state: 'on' + - condition: or + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_workday # If workday is not tested or is a workday + state: 'off' + - condition: state + entity_id: binary_sensor.workday_sensor + state: 'on' + action: + - service: input_select.select_option + data_template: + entity_id: input_select.lounge_ac_mode + option: > + {% if states.sensor.stats_roomt_mean.state < states.input_number.lounge_ac_heat_temp_set.state %} + Normal Heat + {% elif states.sensor.stats_roomt_mean.state > states.input_number.lounge_ac_cool_temp_set.state %} + Normal Cool + {% else %} + 'Off' + {% endif %} + +- id: lounge_aircon_auto_pm_on + alias: 'Lounge Room Aircon Scheduled PM On' + trigger: + platform: template + value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_pm_on_time.state[0:5]) }}" + condition: + condition: and + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_pm_automation # If automation is required and ... + state: 'on' + - condition: or + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_workday # If workday is not tested or is a workday + state: 'off' + - condition: state + entity_id: binary_sensor.workday_sensor + state: 'on' + action: + - service: input_select.select_option + data_template: + entity_id: input_select.lounge_ac_mode + option: > + {% if states.sensor.stats_roomt_mean.state < states.input_number.lounge_ac_heat_temp_set.state %} + Normal Heat + {% elif states.sensor.stats_roomt_mean.state > states.input_number.lounge_ac_cool_temp_set.state %} + Normal Cool + {% else %} + 'Off' + {% endif %} + +- id: lounge_aircon_auto_am_off + alias: 'Lounge Room Aircon Scheduled AM Off' + trigger: + platform: template + value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_am_off_time.state[0:5]) }}" + condition: + condition: and + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_am_automation # If automation is required and ... + state: 'on' + - condition: or + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_workday # If workday is not tested or is a workday + state: 'off' + - condition: state + entity_id: binary_sensor.workday_sensor + state: 'on' + action: + - service: input_select.select_option + data_template: + entity_id: input_select.lounge_ac_mode + option: 'Off' + +- id: lounge_aircon_auto_pm_off + alias: 'Lounge Room Aircon Scheduled PM Off' + trigger: + platform: template + value_template: "{{ states('sensor.time') == (states.input_datetime.lounge_ac_pm_off_time.state[0:5]) }}" + condition: + condition: and + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_pm_automation # If automation is required and ... + state: 'on' + - condition: or + conditions: + - condition: state + entity_id: input_boolean.lounge_ac_workday # If workday not tested or if workday + state: 'off' + - condition: state + entity_id: binary_sensor.workday_sensor + state: 'on' + action: + - service: input_select.select_option + data_template: + entity_id: input_select.lounge_ac_mode + option: 'Off' diff --git a/packages/scheduler.yaml b/archive/packages/test.yaml similarity index 100% rename from packages/scheduler.yaml rename to archive/packages/test.yaml diff --git a/packages/timer.yaml b/archive/packages/timer.yaml similarity index 100% rename from packages/timer.yaml rename to archive/packages/timer.yaml diff --git a/packages/uci_base_fio.yaml b/archive/packages/uci_base_fio.yaml similarity index 100% rename from packages/uci_base_fio.yaml rename to archive/packages/uci_base_fio.yaml diff --git a/packages/uci_gpio_example.yaml b/archive/packages/uci_gpio_example.yaml similarity index 100% rename from packages/uci_gpio_example.yaml rename to archive/packages/uci_gpio_example.yaml diff --git a/packages/uci_lights.yaml b/archive/packages/uci_lights.yaml similarity index 73% rename from packages/uci_lights.yaml rename to archive/packages/uci_lights.yaml index eacf3d1..64f74d0 100644 --- a/packages/uci_lights.yaml +++ b/archive/packages/uci_lights.yaml @@ -1,11 +1,11 @@ -homeassistant: - customize: - fan.fan_state: - friendly_name: 'Fan State (manual override)' - sensor.closet_temperature: - friendly_name: 'Closet Temperature (C)' - input_number.fan_on_temp: - friendly_name: 'Fan On Set Temperature (C)' +# homeassistant: +# customize: +# fan.fan_state: +# friendly_name: 'Fan State (manual override)' +# sensor.closet_temperature: +# friendly_name: 'Closet Temperature (C)' +# input_number.fan_on_temp: +# friendly_name: 'Fan On Set Temperature (C)' switch: - platform: mqtt diff --git a/archive/packages/weatherunderground.yaml b/archive/packages/weatherunderground.yaml new file mode 100644 index 0000000..2a19b40 --- /dev/null +++ b/archive/packages/weatherunderground.yaml @@ -0,0 +1,20 @@ +sensor: + - platform: wundergroundpws + api_key: d54bc75310ae4d378bc75310aead372e + pws_id: KORPRAIR9 +# lang: de + monitored_conditions: + - stationID + - solarRadiation + - obsTimeLocal + - uv + - winddir + - humidity + - dewpt + - heatIndex + - windChill + - precipTotal + - precipRate + - temp + - windGust + - windSpeed diff --git a/packages/zone1.yaml b/archive/packages/zone1.yaml similarity index 100% rename from packages/zone1.yaml rename to archive/packages/zone1.yaml diff --git a/archive/packages/zone1.yaml.org b/archive/packages/zone1.yaml.org new file mode 100644 index 0000000..1b7dc29 --- /dev/null +++ b/archive/packages/zone1.yaml.org @@ -0,0 +1,262 @@ +sensor: + - platform: template + sensors: + zone_1_timer: + value_template: '{{ "{:02d}".format(states.input_number.timer_hours_1.state|int) }}:{{ "{:02d}".format(states.input_number.timer_minutes_1.state|int) }}' + friendly_name: "Time" + last_run_zone_1: + friendly_name: "Last Run" + value_template: '{{ (as_timestamp(states.switch.zone_1.last_changed)) | timestamp_custom("%A, %d %h %H:%M") }}' + next_run_zone_1: + friendly_name: "Next Run" + value_template: '{{states.sensor.zone_1_timer_reset_sensor.state}}' + time_delta: + friendly_name: "Zone 1 Timedelta" + value_template: '{{states.input_number.repeat_1.state|int * 3600}}' + duration_1: + value_template: '{{states.input_number.duration_1.state | int}}mins' + friendly_name: "Duration" + repeat_1: + value_template: '{{ "{:02d}".format(states.input_number.repeat_1.state|int)}}hrs' + friendly_name: "Repeat in" + rain_sensor: + friendly_name: "Rainfall Threshold Sensitivity" + value_template: >- + {% if states.sensor.pws_precip_1d.state <= states.input_number.pws_precip_1d_sensitivity.state and states.sensor.pws_precip_today_metric.state <= states.input_number.pws_precip_today_metric_sensitivity.state %} + dry + {% else %} + too wet + {% endif %} + +switch mqtt: + - platform: mqtt + name: "Zone 1" + state_topic: "status/irrigation/80" + command_topic: "cmnd/irrigation/80" + payload_on: "ON" + payload_off: "OFF" + qos: 0 + retain: true + +binary_sensor mqtt: + - platform: mqtt + name: "ZONE 1" + device_class: moisture + state_topic: "cmnd/irrigation/POWER1" + payload_on: "ON" + +sensor mqtt: + - platform: mqtt + name: "Zone 1 Timer Reset Sensor" + state_topic: "cmnd/zone_1_control/TIMER" + +input_boolean: + reset_zone_1: + name: Reset Next Run Timer + initial: off + icon: mdi:lock-reset + +input_number: + timer_minutes_1: + name: "Minutes" + initial: 0 + min: 0 + max: 55 + step: 1 + icon: mdi:timer + timer_hours_1: + name: "Hour" + initial: 6 + min: 0 + max: 23 + step: 1 + icon: mdi:timer + duration_1: + name: "Set Duration" + initial: 3 + min: 0 + max: 15 + step: 1 + icon: mdi:camera-timer + repeat_1: + name: "Set Repeat" + initial: 24 + min: 0 + max: 48 + icon: mdi:repeat + pws_precip_today_metric_sensitivity: + name: "Rainfall mm Sensitivity" + initial: 0.1 + min: 0 + max: 2 + step: 0.1 + icon: mdi:contrast + pws_precip_1d_sensitivity: + name: "Rainfall Probability Sensitivity" + initial: 40 + min: 0 + max: 100 + step: 10 + icon: mdi:contrast + +group: + garden: + view: yes + control: hidden + name: "Garden" + entities: + - group.solenoids + - group.irrigation_timer_1 + - group.rain_sensor + - sensor.pws_precip_today_metric + - sensor.pws_precip_1d + - binary_sensor.zone_1 + + solenoids: + view: no + name: "Back Garden Irrigation" + icon: 'mdi:flower' + entities: + - switch.zone_1 + + + rain_sensor: + view: no + name: "Rainfall Sensitivity" + icon: mdi:contrast + entities: + - sensor.rain_sensor + - input_number.pws_precip_today_metric_sensitivity + - input_number.pws_precip_1d_sensitivity + + irrigation_timer_1: + view: no + name: "Sprinklers Zone 1" + icon: mdi:clock + entities: + - sensor.last_run_zone_1 + - sensor.next_run_zone_1 + - sensor.duration_1 + - sensor.repeat_1 + - group.setting_zone_1 + - automation.activate_zone_1_timer + + setting_zone_1: + view: no + control: hidden + name: "Settings" + icon: mdi:settings + entities: + - sensor.zone_1_timer + - input_number.timer_hours_1 + - input_number.timer_minutes_1 + - input_number.duration_1 + - input_number.repeat_1 + - input_boolean.reset_zone_1 + +automation zone_1_1: + alias: "Activate Zone 1 Timer" + trigger: + - platform: time_pattern + minutes: '/1' + condition: + condition: and + conditions: + - condition: template + value_template: '{{(as_timestamp(now()) | timestamp_custom("%A, %d %h %H:%M")) == states.sensor.next_run_zone_1.state}}' + - condition: state + entity_id: sensor.rain_sensor + state: 'dry' + action: + - service: script.turn_on + entity_id: script.activate_irrigation_zone_1 + +automation zone_1_2: + alias: "Zone 1 Active Notification" + hide_entity: False + trigger: + - platform: state + entity_id: switch.zone_1 + from: 'off' + to: 'on' + action: + - service: notify.pushbullet + data: + title: "Irrigation Zone 1" + message: "Watering has started" + +automation zone_1_3: + alias: "Zone 1 Completed Notification" + hide_entity: False + trigger: + - platform: state + entity_id: switch.zone_1 + from: 'on' + to: 'off' + action: + - service: notify.pushbullet + data: + title: "Irrigation Zone 1" + message: "Watering has completed" + +automation zone_1_4: + alias: "Zone 1 Timer Reset" + trigger: + - platform: state + entity_id: input_boolean.reset_zone_1 + from: 'off' + to: 'on' + action: + - service: mqtt.publish + data: + topic: "cmnd/zone_1_control/TIMER" + retain: 1 + payload_template: >- + {%if now().strftime("%H:%M") > states.sensor.zone_1_timer.state %} + {{(as_timestamp(now() )+24*3600 ) | timestamp_custom("%A, %d %h ")}}{{states.sensor.zone_1_timer.state}} + {%else%} + {{(as_timestamp(now() ) ) | timestamp_custom("%A, %d %h ")}}{{states.sensor.zone_1_timer.state}} + {%endif%} + - delay: + seconds: 1 + - service: input_boolean.turn_off + data: + entity_id: input_boolean.reset_zone_1 + +automation zone_1_5: + alias: "Extend Zone 1 Timer When Wet" + trigger: + - platform: time_pattern + minutes: '/1' + condition: + condition: and + conditions: + - condition: template + value_template: '{{(as_timestamp(now()) | timestamp_custom("%A, %d %h %H:%M")) == states.sensor.next_run_zone_1.state}}' + - condition: state + entity_id: sensor.rain_sensor + state: 'too wet' + action: + - service: mqtt.publish + data: + topic: "cmnd/zone_1_control/TIMER" + retain: 1 + payload_template: '{{(as_timestamp(now() )+ states.sensor.time_delta.state | int) | timestamp_custom("%A, %d %h %H:%M") }}' + +script: + activate_irrigation_zone_1: + alias: "Activate Irrigation Zone 1" + sequence: + - alias: "Switch on Zone 1" + service: switch.turn_on + entity_id: switch.zone_1 + - delay: '00:{{ states.input_number.duration_1.state | int }}:00' + - alias: "Switch off Zone 1" + service: switch.turn_off + entity_id: switch.zone_1 + - alias: "Update Next Run Time" + service: mqtt.publish + data: + topic: "cmnd/zone_1_control/TIMER" + retain: 1 + payload_template: '{{ (as_timestamp(states.switch.zone_1.last_changed)+ states.sensor.time_delta.state | int) | timestamp_custom("%A, %d %h %H:%M") }}' diff --git a/configs/auth.yaml b/configs/auth.yaml index 54a490d..180e002 100644 --- a/configs/auth.yaml +++ b/configs/auth.yaml @@ -1,4 +1,5 @@ -- type: homeassistant +# authorization settings - none needed +# - type: homeassistant # - type: trusted_networks # trusted_networks: # - 10.0.0.3 diff --git a/configs/automations.yaml b/configs/automations.yaml index adf9ab4..93d91ca 100644 --- a/configs/automations.yaml +++ b/configs/automations.yaml @@ -1,8 +1,32 @@ -- alias: Update Available Notification +# global and startup automations here +- id: 'themestartup' + alias: 'System - Set Custom Theme at Startup' trigger: - platform: state - entity_id: updater.updater + platform: homeassistant + event: start action: - service: notify.martin + service: frontend.set_theme data: - message: "Update for Home Assistant is available." + name: 'Dark Turqoise' +- alias: Restart Notification + trigger: + platform: homeassistant + event: start + action: + service: notify.pushsafer + data: + title: "Home Assistant Has Restarted" + message: "Attention [i]italic[/i] Text[br][url=https://ha.238.kebler.net/]Home Assitant 238[/url]" + target: ["26583"] + data: + icon: "20" + iconcolor: "#FF00FF" + sound: "33" + vibration: "0" + url: "https://ha.238.kebler.net/" + urltitle: "Open Home Assistant" + time2live: "10" + priority: "2" + retry: "60" + expire: "600" + answer: "1" diff --git a/configs/customize.yaml b/configs/customize.yaml index 52e266d..05487d7 100644 --- a/configs/customize.yaml +++ b/configs/customize.yaml @@ -1,2 +1,4 @@ -fan.fan_state: - friendly_name: This is a dummy entry to be overwrrten +#global friendly names and other customizations can go here +# ex: +#fan.fan_state: +# friendly_name: This is a dummy entry to be overwrrten diff --git a/configs/notify.yaml b/configs/notify.yaml new file mode 100644 index 0000000..20b8ea6 --- /dev/null +++ b/configs/notify.yaml @@ -0,0 +1,4 @@ +# push notifications services here. +- name: pushsafer + platform: pushsafer + private_key: !secret pushsafer_api_key diff --git a/configs/secrets.yaml b/configs/secrets.yaml deleted file mode 100644 index d0e011b..0000000 --- a/configs/secrets.yaml +++ /dev/null @@ -1,4 +0,0 @@ - -# Use this file to store secrets like usernames and passwords. -# Learn more at https://home-assistant.io/docs/configuration/secrets/ -some_password: welcome diff --git a/configuration.yaml b/configuration.yaml index 07825cd..8caa168 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -2,26 +2,54 @@ homeassistant: # see packages/aa_system.yaml for basic settings customize: !include configs/customize.yaml auth_providers: !include configs/auth.yaml + # mqtt: !include configs/mqtt.yaml # set by integration packages: !include_dir_named packages # discovery: + default_config: -# CONIG FILES -# group: !include configs/groups.yaml + automation: !include configs/automations.yaml +notify: !include configs/notify.yaml +# group: !include configs/groups.yaml # script: !include configs/scripts.yaml # http: !include configs/http.yaml # mqtt: !include configs/mqtt.yaml # panel_iframe: !include configs/panel.yaml +python_script: + +# see https://github.com/thomasloven/hass-browser_mod +# browser_mod: + +frontend: + themes: !include_dir_merge_named themes + # not needed browser_mod handles it + extra_module_url: + # see https://github.com/Villhellm/custom-sidebar + - /hacsfiles/custom-sidebar/custom-sidebar.js + +# lovelace_gen: + lovelace: - mode: yaml + mode: storage + dashboards: !include lovelace/dashboards.yaml + # needed for any time of day related triggers -sensor: - - platform: time_date - display_options: - - 'time' - - 'date' - - 'date_time' - - 'date_time_iso' - - 'time_date' - - 'time_utc' - - 'beat' +# sensor: +# - platform: time_date +# display_options: +# - 'time' +# - 'date' +# - 'date_time' +# - 'date_time_iso' +# - 'time_date' +# - 'time_utc' +# - 'beat' +# recorder: +# exclude: +# entities: +# - sensor.time +# - sensor.time_date +# - sensor.date +# - sensor.date_time_iso +# - sensor.date_time_iso +# - sensor.date_time_iso diff --git a/custom_components/browser_mod/__init__.py b/custom_components/browser_mod/__init__.py new file mode 100644 index 0000000..a3440d6 --- /dev/null +++ b/custom_components/browser_mod/__init__.py @@ -0,0 +1,52 @@ +import logging + +from .mod_view import setup_view +from .connection import setup_connection +from .service import setup_service +from .const import ( + DOMAIN, + DATA_DEVICES, + DATA_ALIASES, + DATA_ADDERS, + CONFIG_DEVICES, + DATA_CONFIG, + DATA_SETUP_COMPLETE, +) + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup(hass, config): + + aliases = {} + for d in config[DOMAIN].get(CONFIG_DEVICES, {}): + name = config[DOMAIN][CONFIG_DEVICES][d].get("name", None) + if name: + aliases[name] = d.replace('_', '-') + + hass.data[DOMAIN] = { + DATA_DEVICES: {}, + DATA_ALIASES: aliases, + DATA_ADDERS: {}, + DATA_CONFIG: config[DOMAIN], + DATA_SETUP_COMPLETE: False, + } + + await setup_connection(hass, config) + setup_view(hass) + + async_load_platform = hass.helpers.discovery.async_load_platform + await async_load_platform("media_player", DOMAIN, {}, config) + await async_load_platform("sensor", DOMAIN, {}, config) + await async_load_platform("binary_sensor", DOMAIN, {}, config) + await async_load_platform("light", DOMAIN, {}, config) + await async_load_platform("camera", DOMAIN, {}, config) + + await setup_service(hass) + + hass.data[DOMAIN][DATA_SETUP_COMPLETE] = True + + for device in hass.data[DOMAIN][DATA_DEVICES].values(): + device.trigger_update() + + return True diff --git a/custom_components/browser_mod/__pycache__/__init__.cpython-38.pyc b/custom_components/browser_mod/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..352fd6a674cdbcc7dc7dcb80f92d102a9caf7071 GIT binary patch literal 1227 zcmZux%WfMt6eT%NjUI7qBdCjR`q*fZI^70F5W!L$0gfF+P8S2T#CS-{H0Gg@lnnu+ zmGc4l1Z$b^$TnZVZCCk)WRuGorEv?B;2quvFOTlI)a!0HLNMaLKT_c#^p90;4+oT| zu+$j{h8WIJj2K}~hI0}VZ1-;Fl56Cjy_?KeQYAd^?XJz1-}AqqtVi`D0r$x zS%9tE*c6wSBIW(S_`~BDgYl8+3BaLloE?^Jp%aJGJb%i9sz8DmZZWO?Vua1i9jnd zM1MV6I*m<}9aK5i$1nrm(yd&%T{$<7E$oU^q;mJrn{Dk1q`iu0AHKo*Y{*>URp_Fn z(H_!Kg(`ea#z4(;gpqDn=o(`L{?0}BqPO%ak9ivKK$QC}3T*xzw%`t1c!v#c{%<~g z&))hiI|ED>-44>>o<-8^UHH8lD+G1uE&a;BxxWIu@9>+SSOk=}kBnO+IX9So#t)yF zPR^M~=qyVXTpC~TLY2~VPeqZ)1zoFNCZ^}w_$eHe$w_}x`apnjrd&%)hY2Ky3@MkE zAQ@WGvZ8H82ZV81q7&&_-FCVO&UrTDQkfPLDlIR$T$rsyEsB(8Wx^~VeOAg`!dpSc zzf7_@S8-2EF`aTr=QGG>8_+#ZlmcXBaVDlFNHUQqt_)_fYw!B8KFzpo*Y`;N4C?1U zcAMzkbgp!n)3nTIWx)%rc28w_1C|3ZW_eeYP;|23g(xTaZIN27!o+xA70y z#hVU61b<(?-G7O~4IGjPV%C%NU_QVdB7cR`{Vfw@<#Z~FDLmbh)$gt;PW1iJ)g?2* z`o%TX@W8Ef&B6Z*ZrQk1^%gL62zLOrA*GggN=-lyj}Hz;uWA%^Np6W>Ab|<@0M9(* SPwXFqviyalgYEn+ikz=7UMxER literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/binary_sensor.cpython-38.pyc b/custom_components/browser_mod/__pycache__/binary_sensor.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9132b9d929af476b8280c83c7d34a8a627eac67d GIT binary patch literal 2137 zcmZ`)OK%%D5GJ|1(&}j^XT}I z=w<`?2R++I|Bhb!3wZ4*eG?%@=UK{Q7 zcLx0Fu>Y*TvDtsPIj}eTJ3HI_A;`h@uYB$2{`TXI;bVLK()Q-y*b=(JQ_UPSR3%Q&HiY_pKR@HYz<#g@ZMv#tEHKY`8bQsUa4}s{7{uIv{X+@F(^!G z4sB;QEn;=ZwJdb06696962*l{o-=XBWfR0~dQ_{(O0XMJn? z$)byq`)1F#{z0s@ttDl#m+sqZu|6yk9t**Re4Zv!+pb7;Qa+aeBrW#JbE0m-S6_d> zT3uO9CfbxaPs)5;7P2sUb+-zH=cQP^6vE2*(0)*PxEws<)C>fn>Ndy()x|N7T}QAh zNkj#SnDioFbYGG^Dwyz28c`s85uA{yChMXm>L=uwMj_~!4X8IjcliK%6ZE#|xc$~k z;*+Qixv`{>%wYmK`(~hyTzfF}JrIT*lT(UpPra#s=3_=q31@zHt>|PfMoNv%=XkZFttsfv5<*L%L4MzalY}$262|g8ZxNCc>*^z=Zwww9xDH=+)cz=4Yf+10Lnv5@+-3-jpK70}M~{g5$@5b`OlV78IguVAys)Ys_x0g88m zNHs5u&)~SK2=)%=&g6=7d|k&E5@+V`sKWPoYFFu2CD{E1haX>wcG2>RU#wo|_d8H9 zT;XBphR)u}7vzi?f3^y6mAciR3`(vxw7xkUOWWGL=o?kS)puiKpz+u+If&JMTI}}% zccQQ+7kI>g-$~}}8F2Sn9KNQ~-HDO<8@S*KfOw2B%7_oMjq^Vi`2QX~cpn3Af2aq2 zwu5C}#JPkrZ*pvVSR>rFIsao4XVr;X0++TX$~;aBSHbE_+zH33gic`&RS$zeVCq{G zNT%(~%0H1=Hg5qgUq3|?*9r5;uNsKFejqc5OS}I;s*P}w0oUFg eo`vGOsKD@tioG8(moHQDl`Sxc3 literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/camera.cpython-38.pyc b/custom_components/browser_mod/__pycache__/camera.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c5bb9b4a8cc0bed3336e0be89fedc964c918818 GIT binary patch literal 1736 zcmZ`(OK%%D5GMDbUA-LV(MBoSq(y-?TNKvC@Tn-GBz9rIQHt8>VGkwNOIliIA1X;D zkfEFXGkUO&{tvzN+F!tHPyP!53Ur2R)oPp)Aa^(%&VDoV4PSM;EdnF{a`!i&AakNu(%H;Nz*?75eRMKf-K?DIz6 zD%x?o=)@h^2XU8oc*vV)G+z6G@D^{M5#AO}(K+?vTb%BbsCx;2Cc}uDkSAK`tPqiB zZtouq4z}5=;o#@N?%v?(-nI#A)ytu2UP(JUrd5l(&-b>$L*rRe2$@_`@EAE}O$j}n zut}ciqe>QL{i&?pDj{D~e7n?{o|#5!FQkx~e+SA9n18@j>#%4tCv!S?G}X?jzfUgc zFmh!RPE7dY-r!*8Vn9-;PqXCkdrByMhN>OUnKdd8QMaBCy_KnHR+^b#xH{1^5cVHn@ zegtcZ;_7f=^8}{af<=&+auPejjXkK7!(FJE&pqy+k+{JdJb-T?LR(F8O5>)jUR$og zbaqBK)Nl#y>dCGsPA3-v8XR4G!ORVC2JW_9YWbJg+&SnNy-7Q2!I zk&ZF(0Q5K4!Zhub_J~~L>DffcPY@AiC~~gl58}LEGpJ5EOSG2R;ZzIt2res(pSTY7 zsQ35D;~T*L=V}CRMGM(n!b8KDX)$bFtS~bj#(te9d3_?g;NCQNRU}zyxhlT^jrdyW^@5 z@JeMsvr=yW4V9KIoJdTzM0!ZCO#r+4};GQS| literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/connection.cpython-38.pyc b/custom_components/browser_mod/__pycache__/connection.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e650c425e5ef63a972dd0b344acd970e8814789 GIT binary patch literal 3325 zcmbVO&2QYs6`vV?F14%3i4#~&nxt*MwoT=yr#vr^2V6rCa@X^wL8QJ}#Zvd>sQ@l2ul7^F4$`0w)3A=xh_k{g{3@<{ zX|GPw#Nt7Q1U-wkI4Chr%SkznxvbwXt#I`e&wx`R5;h@axZR;7?0q{G*U}%aPP(Fj6w_WCp=y zw`&sG8#R-ya%?V@I@7Y7wj{Z&WG6?0w4FabzA?sMF!T;U$2xu}3Vy^p&&~v9ekt}j znd|}E=l8_|O>jzsSy%+1j!F;I)?A?Wx_HKBVqb7(XCBQ82D8N*#_x7pX<~Pd-+nM6 zUmiaJCLzXc4E-^{3~H@12xK#;amZ(0Cmw=MOysC5n~8~FQJR~+mXqSx zpNdo-2FBmcwKmC|hUmRokDdI`o$oPIS3vyryH}lWu8yXotDEk}S=|&w zH2?KlyK9!W5lodNx6)6*Pyz5mn$}9c;+F1d{FY+$` z?*{)lL^}iC_vVI&iD%xOP_50pnZL^50D(wBiX9_*41S zv{bTCkzLcBQY6*Hw_5UaUcor!s8bYyL*FWFLgzE}J*=sh36L)rR9!i*$|daU*8rFh zJZ5%$;-()k*O{sn+!DK8jG8ZQJ2k>~O;^^2egfd@rY z#Q26quSoEXiY4S&Uy&MT+)M#Wg0Ev(!N?kxA-e$i7`c_frGTD=zJ-B>p@k78?_<6( zjGZef3avj)zm`RrXKj`5N@YTgsw5})%87223AUzHwF^V$wN&}gGrpG9)`E<4292|< zoRnRb{frc);9)$ZS6GPI-uEv`)U{*2GpD9PM}tnUe+h6Pb`Zh`{D5uQ@I62y+e)y?-ydmOVpY_;SO1-Gf$r4p`vWBS=1V-nsm{e3LUB^u0O?7bEu*{$CY}l*zS@MOd@qI>%Ip*bDRX4Y$% zPyyZH`&_+>FG^+S5su;Si$5-u%$Gnr;3^ zG##7qgzx?6LUqsj{fs{-woVlPA&!9y6P`HY>OE4peU&cS9S@#>>bQI{GCBtB&nXGZ z0*iUzYk`ODALe|ZKj8~$2m8baO2X!R=pEFbfk*A5IUms;|1Y8)>=Pd-37hjp;QI&g zxc%##4;{w|AI)Rz6CWrEoAdRK_#>`f#!BxIZ~ZxMZ-JNQ*aHo5gP!mV#gLl54HIp; z>sk$AKNGsTr``mj-lCPDD#zQM`Z3K1o8=@|yXeKJ7^)~Ew^jn>*}pPsj+?G0=r zXG@2@_f3GBiq3#q1lJuz+p=n<(zdB_{<5h_>1ulD8$W~^QmJ1N9%Ucix_R@)r}kdA eie7Xw6R$h_yicXW_H#I%xDu`KDT9Wi*3-^~Q;Dz{#OgNCR1TvPP zgXms>d#$y=m)daM?I)E`u-~bNc z2#(JOghv|{AHta@J$cO4RnBe-ng=4sETP& z={mXO_V#?bj zj_cv4&lloAO2e`q2wi1BO4`VfBs3?y3v^X&a5rOFmDOq(vw}_=!2Oh5N1LWTVL6>f z>6+C8@$R&IM-y64Vs=eq64Gf-V@9CXZlV;}%^q=oC6XUlRn}_Crgwn5B_l;?KRM!x z;mmc@=Es+=WBbT90&nJ;3qM%87C$v~Uk}ig@9M$aF_so;!lNc^px5RMd3;B@W$KF%i9@$DqN@4nB3V}>)$?< Lg|%Dqf9&TUwn6ze literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/helpers.cpython-38.pyc b/custom_components/browser_mod/__pycache__/helpers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5c0656772b161389af47c56f705a640ab704abf9 GIT binary patch literal 3176 zcmb6b+fExtbY}MAwXqEtl0cNGY1P}d4Mb6Tk(x$vz(J_SL_nfyds%Hgi(~Mvo!KQR zba?_Pd1)W3N>L?v_Bs7I9(_9)C-V})NCfrN*FieT{+`LyJ6jJ_@OVn*mr&Px7*|| zC{WrtX)dm=l$UEVzgXTVyNi_v%L|qD?yg*2F0aFGOySCli$+Ow z9zKi<4Tci$8Y~9IJJOEHBKdyiJGRgEwM0wkHWQ_UF+xG~Mq(WDVRod7NzxCO)r{-ZDau;^dhxrj*5%09+DWPg$Pe2 zz7C6FiDSJ-KPtxD*5DsWd)2U8pU}kjs75kUzIxPW4ybm;W zOrqY0xzr2r2(es$N}ke9hT(9SO_lFwW1`)J5(wd8)P{V9SK`22j4lCq2G$3g(L@7l z>CeC;4SGN+%w4-FjclVIIN}{R25MuB3)JX@$;q_1fhl^KDz$nm>gR=c7YILv5uqZ4 z7Qp~}mm@#k>9{=$&)sab>s~9mHK$CvVPicRa1iv5Fm?`l0Yl zVGMm(4sOGUssMcAP)-~M?S^sfkUG%NxB(4E_n|@Y%pp0Tj_I3Q(56mzM%t_ZV|CkA zR<)y|oVb%#*OfL;8&d5|cwtAx2k735zdQFl&ZSLoNWidOS`V!+Q%`U@L&0{1dRmP4X z5gJIEyd8MWP-ag%sBKC;@>`oD*NG1lc}Mu0&7VfyW4UfKY{ssuG{#sV)a?GwQw9aQ zNZCJov!iC4>zz9jHrO52WtgU??L_fCp#rP>s0^8%4(RLiRls!vV{hgR!!FVP^TUk7 z#?dQuRLmnY%(fAZDw)fo1BY65T7Ff zavku0U!1FeD8q=*n-JQjqtOf-&ZUR#z=tPXGm5=1p6fl$ zx(yFaohGIM{VWQbi uvABX4VB1qATVVNAa=uPMY28c>cMDTe`O{((>{=nGS-O?63~SuVTKa$L^mYpX literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/light.cpython-38.pyc b/custom_components/browser_mod/__pycache__/light.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3605dbd733646804d7baf16b92eb005ff6032707 GIT binary patch literal 2516 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 Cxl)S& literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/mod_view.cpython-38.pyc b/custom_components/browser_mod/__pycache__/mod_view.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54d96efa2e96a8f30f5e2e789367aafd132fd806 GIT binary patch literal 1436 zcmah}&2k$>5T3vNks=evAqEayC@!i33l3CqNKtmh4nLJBAlqQ}z|?wYEUmKIm1jnA zEF><@HN`7P$2(_GyQe<9B*ti35?{Y-{m)gkUwy7 zbAXQQ0@;0FiYQu;5ltvXnH6lr6Am(0q7b7X2}WTO!a7jl3z9@%5*4ZV1yQl)S3HST z(Q_nB-DNJ|1sz!Rx+T6Odbl>Ot;x$DB=I=i2J?S1Dbob@qgWlKQ9?9e0 zBY$sy?`TgRoE-J`0V2;elkUovfwqsYd?8yD~{2rOp z8C`HEmJk)#1&h!{GvdS*LyaPbsXssMWL0^V_dD6dx@sh|YBa7&T{_!2HPwX$ zN25x0fNF<3<4fO!2Sjj1K97Edfi(5j}%X zZ41vKejl!4_F|m6fp4x>Qst)2y|B7CGjD;lxr+=@TCqDI`B=)l%$<~P!^&b|5*pI! z<{Ian>+KBRbUiR>h|+IB)&Wy=QD=Th=S;CVpMK)lj3Ew)!AtS$_FT-wf-lLO!z`AB zk{`%t;`gJfLJMa94r+OK?JdBqci`E*wTP4z({-{E`ErjqbGb z^Zb7jeFN^Dq4mU5GYuy0?BmaUnD@)dXv7H0LHk&jS*7x_??Y`&W$g5})6ZRJm_AEa znL^XkN?rOEK>f@faP@h(}L-P(QBBReI+B$O&SGUgcLa*=gjiSoZ0yVy&|911QeDc0_@4``w zWlLy_3C8Fajp;Vsq+4uy>pHPu*KJ3b|1uqEDScC7XG{u|TT-4+(qg?MaK=tNpX5ec znNHlm;3eiGWN%(jOITKQt=|U0#=v4Gs9>?EwcU2ZM`>PRch?vs?+@Uwf?i(zRXcAw qcBi(%h5+~gBv*e7^_Nk1W$o|3uJ`f%RVZKJ4_3dS1yF4AIQ|P)Qflr1 literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/sensor.cpython-38.pyc b/custom_components/browser_mod/__pycache__/sensor.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..39937b10645ed37592c4243649eb2c56eaad93b4 GIT binary patch literal 1560 zcmZ`(Pmdcl6t_K|$z+mUc4=w15+Dv7$Ypclh7eg;P^E1^&6dL)u%=^YGid$<+i8_m zZi&=yz==cmxSxY7pTJj6{R#*q-t%m_K}#L^<$31$`S*VB=Xtibmk=1~Pk)HdIU#@I zVt2qZ@)b<;6pSR2mL$c0R?>>5l%gM$tP0Wqd@jS1S791fQ5wNIN;|SAJ2KwTH2#jr zu1q#WCMsG7X;;z-8T8*kUNRm~7t72lTT}}C!^!#R{8T&}kA56IJ{z5!oxY(EHehbw zD7%=8d70U1qbqlCqMMgS=_idmt!-hKE;6b%jm{DIcAy`@{2it_0AtCDtmukaYS}uR zkZU>~1UiH_F8<+cbpGx4PoM1Op7@*t?)WS-#zlEkPm2q8n3-jri%d!()r%rm#_h|( z)sV;q$fvQ8jYdVn3cQ-TgN8f6K*Q z83u+>eF!#3cXw=yHYe~@k(5f3G8Lp;Ca=hpN+yF%Hx02-8#ZK9)!9Cc&b%yi>3xicv2sKgGILE`SBM;T8X-Kd z8k?!_fkDUk^6C5Z^3km<+SVO!56)3Y+krmP`87=Q0E~q~t~~e^c^zC4EM;2+*BI)8 zx_PPZqjzNTnUafAiNzenD`uH67MyFUKY)N1A-3Pcbe%R#;^rHu3B5eJ1NA2C-AIoh z!HWD0Khw8xi!5tbYw&Lf*I13l7P+8Qwf+c~9?C7A(1XQf2Kz}^OgX)La0ibMyMqR~ ze#@JrhyW%??6&~^*{=-b6O3JNN8X?VZkM0~WI#Uz1DEct?>LZ{{V&potq@vvMP{uo zo-eF2A43Y$2SWk|*!e%4^S>`YyHmkCwh!Re_fV5{Rw>YABJdJoLxt-L@yjAB+c&xk zN4Q8fRaVqqZW@nv@mx1hj$P_cV5vXFXL~ma%~e@$Rq8o^gdtw9c&GMYntd=pI@{+l zXEX^J_4f^NC9YF87Z*i+(aP@Ltf>?TzkqJk_84x7v2A}lGgSfz<3C>-JL%#24}?#* b)A!rv$6vIieu;O$a~RV;>$3yKSrYsUGmmcI literal 0 HcmV?d00001 diff --git a/custom_components/browser_mod/__pycache__/service.cpython-38.pyc b/custom_components/browser_mod/__pycache__/service.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cf73bf699cf19032aada6c3ebcb632dcc482a96 GIT binary patch literal 1414 zcma)6OKTff6h8Mc4-3gjA8F$@ZL??t1Fh{vDHL2&5t(4HWr&(CMAKn1_ePO3Gg9sy zH`t6eks;2e(0?Fp3i&Vn1$Wz3{z6tgXDr)QOQ8dEbk4o!^_}yL9<*8wz^DK7AMuF< z_>+vkKLU;K@wMNeP@q@_eNbG9jODz~bI}*M?91Hidki$pd>zbLKU7kAr_f(ftOt?* z24kW7hM|n4^E(Io?cJ_xcG^F*cZ#p=qIk(3GsP4?4PU`aKwT(#8cx_5JA@f4x$@3nE*O-e0}pRa_{Dj}XHxkp znDdI!{j)C>V7Fm+shp1$a4_&5mHQn<1(X&G!kkZ7#q0+Zwm9YWDKB}&OEKl<>xvK9 zdC5|)LFui*L{I~@<*&gjsJQ*960=|q6Mofx#XB&?$TFnNsL4ty_8od7h)*Y8fO!~GLB#fU5)G}Dg#9E@H!3- zW>6urIsc76lRbD15p!ao%ZNL^zUf%`nz3)#e7KtpP{ed`LRYBx2@c(rM%8u&*t)5kEJ{c&yDQcw-4X z;cg=4lRN)`?;nA6{yN;;!sy6z;i4z&Rug+%q_NQhB#d6{k%((PBUfQQBKNi z?P1$>tMygAq1I9(6mIK(KHj|1^snP8%;$K96biD@U@$BO&O=HgZom#T@ddb+`G4PzfN>MqMath.floor(1e5*(1+Math.random())).toString(16).substring(1);window.fully&&"function"==typeof fully.getDeviceId?localStorage[s]=fully.getDeviceId():localStorage[s]=`${e()}${e()}-${e()}${e()}`}return localStorage[s]}let a=i();const n=e=>{null!==e&&("clear"===e?localStorage.removeItem(s):localStorage[s]=e,a=i())},r=new URLSearchParams(window.location.search);function l(){return document.querySelector("hc-main")?document.querySelector("hc-main").hass:document.querySelector("home-assistant")?document.querySelector("home-assistant").hass:void 0}function c(e){return document.querySelector("hc-main")?document.querySelector("hc-main").provideHass(e):document.querySelector("home-assistant")?document.querySelector("home-assistant").provideHass(e):void 0}function d(){var e,t=document.querySelector("hc-main");return t?((e=t._lovelaceConfig).current_view=t._lovelacePath,e):(t=(t=(t=(t=(t=(t=(t=(t=(t=document.querySelector("home-assistant"))&&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"))?((e=t.lovelace).current_view=t.___curView,e):null}function u(){var e=document.querySelector("hc-main");return e=e?(e=(e=(e=e&&e.shadowRoot)&&e.querySelector("hc-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-view")||e.querySelector("hui-panel-view"):(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=(e=document.querySelector("home-assistant"))&&e.shadowRoot)&&e.querySelector("home-assistant-main"))&&e.shadowRoot)&&e.querySelector("app-drawer-layout partial-panel-resolver"))&&e.shadowRoot||e)&&e.querySelector("ha-panel-lovelace"))&&e.shadowRoot)&&e.querySelector("hui-root"))&&e.shadowRoot)&&e.querySelector("ha-app-layout"))&&e.querySelector("#view"))&&e.firstElementChild}async function h(){if(customElements.get("hui-view"))return!0;await customElements.whenDefined("partial-panel-resolver");const e=document.createElement("partial-panel-resolver");if(e.hass={panels:[{url_path:"tmp",component_name:"lovelace"}]},e._updateRoutes(),await e.routerOptions.routes.tmp.load(),!customElements.get("ha-panel-lovelace"))return!1;const t=document.createElement("ha-panel-lovelace");return t.hass=l(),void 0===t.hass&&(await new Promise(e=>{window.addEventListener("connection-status",t=>{console.log(t),e()},{once:!0})}),t.hass=l()),t.panel={config:{mode:null}},t._fetchConfig(),!0}async function p(e,t,o=!1){let s=e;"string"==typeof t&&(t=t.split(/(\$| )/)),""===t[t.length-1]&&t.pop();for(const[e,i]of t.entries())if(i.trim().length){if(!s)return null;s.localName&&s.localName.includes("-")&&await customElements.whenDefined(s.localName),s.updateComplete&&await s.updateComplete,s="$"===i?o&&e==t.length-1?[s.shadowRoot]:s.shadowRoot:o&&e==t.length-1?s.querySelectorAll(i):s.querySelector(i)}return s}async function m(e,t,o=!1,s=1e4){return Promise.race([p(e,t,o),new Promise((e,t)=>setTimeout(()=>t(new Error("timeout")),s))]).catch(e=>{if(!e.message||"timeout"!==e.message)throw e;return null})}function w(e,t,o=null){if((e=new Event(e,{bubbles:!0,cancelable:!1,composed:!0})).detail=t||{},o)o.dispatchEvent(e);else{var s=u();s&&s.dispatchEvent(e)}}r.get("deviceID")&&n(r.get("deviceID"));let y=window.cardHelpers;new Promise(async(e,t)=>{y&&e();const o=async()=>{y=await window.loadCardHelpers(),window.cardHelpers=y,e()};window.loadCardHelpers?o():window.addEventListener("load",async()=>{h(),window.loadCardHelpers&&o()})});async function _(){const e=document.querySelector("home-assistant")||document.querySelector("hc-root");w("hass-more-info",{entityId:"."},e);const t=await m(e,"$ card-tools-popup");t&&t.closeDialog()}async function v(e,t,o=!1,s={},i=!1){if(!customElements.get("card-tools-popup")){const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html,o=e.prototype.css;class s extends e{static get properties(){return{open:{},large:{reflect:!0,type:Boolean},hass:{}}}updated(e){e.has("hass")&&this.card&&(this.card.hass=this.hass)}closeDialog(){this.open=!1}async _makeCard(){const e=await window.loadCardHelpers();this.card=await e.createCardElement(this._card),this.card.hass=this.hass,this.requestUpdate()}async _applyStyles(){let e=await m(this,"$ ha-dialog");customElements.whenDefined("card-mod").then(async()=>{if(!e)return;customElements.get("card-mod").applyToElement(e,"more-info",this._style,{config:this._card},[],!1)})}async showDialog(e,t,o=!1,s={},i=!1){this.title=e,this._card=t,this.large=o,this._style=s,this.fullscreen=!!i,this._makeCard(),await this.updateComplete,this.open=!0,await this._applyStyles()}_enlarge(){this.large=!this.large}render(){return this.open?t` + + ${this.fullscreen?t`
`:t` + + + + +
+ ${this.title} +
+
+ `} +
+ ${this.card} +
+
+ `:t``}static get styles(){return o` + ha-dialog { + --mdc-dialog-min-width: 400px; + --mdc-dialog-max-width: 600px; + --mdc-dialog-heading-ink-color: var(--primary-text-color); + --mdc-dialog-content-ink-color: var(--primary-text-color); + --justify-action-buttons: space-between; + } + @media all and (max-width: 450px), all and (max-height: 500px) { + ha-dialog { + --mdc-dialog-min-width: 100vw; + --mdc-dialog-max-width: 100vw; + --mdc-dialog-min-height: 100%; + --mdc-dialog-max-height: 100%; + --mdc-shape-medium: 0px; + --vertial-align-dialog: flex-end; + } + } + + app-toolbar { + flex-shrink: 0; + color: var(--primary-text-color); + background-color: var(--secondary-background-color); + } + + .main-title { + margin-left: 16px; + line-height: 1.3em; + max-height: 2.6em; + overflow: hidden; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + text-overflow: ellipsis; + } + .content { + margin: -20px -24px; + } + + @media all and (max-width: 450px), all and (max-height: 500px) { + app-toolbar { + background-color: var(--app-header-background-color); + color: var(--app-header-text-color, white); + } + } + + @media all and (min-width: 451px) and (min-height: 501px) { + ha-dialog { + --mdc-dialog-max-width: 90vw; + } + + .content { + width: 400px; + } + :host([large]) .content { + width: calc(90vw - 48px); + } + + :host([large]) app-toolbar { + max-width: calc(90vw - 32px); + } + } + `}}customElements.define("card-tools-popup",s)}const a=document.querySelector("home-assistant")||document.querySelector("hc-root");if(!a)return;let n=await m(a,"$ card-tools-popup");if(!n){n=document.createElement("card-tools-popup");const e=a.shadowRoot.querySelector("ha-more-info-dialog");e?a.shadowRoot.insertBefore(n,e):a.shadowRoot.appendChild(n),c(n)}if(!window._moreInfoDialogListener){const e=async e=>{if(e.state&&"cardToolsPopup"in e.state)if(e.state.cardToolsPopup){const{title:t,card:o,large:s,style:i,fullscreen:a}=e.state.params;v(t,o,s,i,a)}else n.closeDialog()};window.addEventListener("popstate",e),window._moreInfoDialogListener=!0}history.replaceState({cardToolsPopup:!1},""),history.pushState({cardToolsPopup:!0,params:{title:e,card:t,large:o,style:s,fullscreen:i}},""),n.showDialog(e,t,o,s,i)}async function g(e,t=!1){const o=document.querySelector("hc-main")||document.querySelector("home-assistant");w("hass-more-info",{entityId:e},o);const s=await m(o,"$ ha-more-info-dialog");return s&&(s.large=t),s}const f=[customElements.whenDefined("home-assistant-main"),customElements.whenDefined("hui-view")];Promise.race(f).then(()=>{const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html;e.prototype.css;class o extends e{setConfig(e){}render(){return t` +
+ Nothing to configure. +
+ `}}customElements.get("browser-player-editor")||(customElements.define("browser-player-editor",o),window.customCards=window.customCards||[],window.customCards.push({type:"browser-player",name:"Browser Player",preview:!0}))});const b=[customElements.whenDefined("home-assistant-main"),customElements.whenDefined("hui-view")];Promise.race(b).then(()=>{const e=customElements.get("home-assistant-main")?Object.getPrototypeOf(customElements.get("home-assistant-main")):Object.getPrototypeOf(customElements.get("hui-view")),t=e.prototype.html,o=e.prototype.css;customElements.get("browser-player")||customElements.define("browser-player",class extends e{static get properties(){return{hass:{}}}static getConfigElement(){return document.createElement("browser-player-editor")}static getStubConfig(){return{}}setConfig(e){this._config=e;for(const e of["play","pause","ended","volumechange","canplay","loadeddata"])window.browser_mod.player.addEventListener(e,()=>this.requestUpdate())}handleMute(e){window.browser_mod.player_mute()}handleVolumeChange(e){const t=parseFloat(e.target.value);window.browser_mod.player_set_volume(t)}handleMoreInfo(e){g("media_player."+window.browser_mod.entity_id)}handlePlayPause(e){window.browser_mod.player.paused?window.browser_mod.player_play():window.browser_mod.player_pause()}setDeviceID(){const e=prompt("Set deviceID",a);e!==a&&(n(e),this.requestUpdate())}render(){if(!window.browser_mod)return window.setTimeout(()=>this.requestUpdate(),100),t``;const e=window.browser_mod.player;return t` + +
+ + + + ${"stopped"===window.browser_mod.player_state?t`
`:t` + + `} + +
+ +
+ ${a} +
+ +
+ `}static get styles(){return o` + paper-icon-button[highlight] { + color: var(--accent-color); + } + .card-content { + display: flex; + justify-content: center; + } + .placeholder { + width: 24px; + padding: 8px; + } + .device-id { + opacity: 0.7; + font-size: xx-small; + margin-top: -10px; + user-select: all; + -webkit-user-select: all; + -moz-user-select: all; + -ms-user-select: all; + } + `}})});class S{async connect(){if(null!==document.querySelector("hc-main"))this._connection=l().connection;else{if(!window.hassConnection)return void window.setTimeout(()=>this._do_connect(),100);this._connection=(await window.hassConnection).conn}this._connection.subscribeMessage(e=>this.msg_callback(e),{type:"browser_mod/connect",deviceID:a}),this._hass_patched=!1,c(this)}set hass(e){if(this._hass=e,!e||this._hass_patched)return;this._hass_patched=!0;const t=e.callService;e.callService=(e,o,s)=>{if(s&&s.deviceID){s=JSON.parse(JSON.stringify(s));const e=JSON.stringify(s.deviceID).replace('"this"',`"${a}"`);s.deviceID=JSON.parse(e)}return t(e,o,s)},document.querySelector("hc-main")?document.querySelector("hc-main").hassChanged(e,e):document.querySelector("home-assistant").hassChanged(e,e)}get connected(){return void 0!==this._connection}msg_callback(e){console.log(e)}sendUpdate(e){this.connected&&this._connection.sendMessage({type:"browser_mod/update",deviceID:a,data:e})}}const k=e=>class extends e{constructor(){super(),this.player=new Audio;for(const e of["play","pause","ended","volumechange"])this.player.addEventListener(e,()=>this.player_update());window.addEventListener("click",()=>this.player.play(),{once:!0})}player_update(e){this.sendUpdate({player:{volume:this.player.volume,muted:this.player.muted,src:this.player.src,state:this.player_state}})}get player_state(){return this.player.src?this.player.ended?"stopped":this.player.paused?"paused":"playing":"stopped"}player_play(e){e&&(this.player.src=e),this.player.play()}player_pause(){this.player.pause()}player_stop(){this.player.pause(),this.player.src=null}player_set_volume(e){void 0!==e&&(this.player.volume=e)}player_mute(e){void 0===e&&(e=!this.player.muted),this.player.muted=Boolean(e)}},x=e=>class extends e{get isFully(){return void 0!==window.fully}constructor(){if(super(),this.isFully){this._fullyMotion=!1,this._motionTimeout=void 0;for(const e of["screenOn","screenOff","pluggedAC","pluggedUSB","onBatteryLevelChanged","unplugged","networkReconnect","onMotion"])window.fully.bind(e,`window.browser_mod.fully_update("${e}");`);this._keepingAlive=!1}}fully_update(e){this.isFully&&("screenOn"===e?(window.clearTimeout(this._keepAliveTimer),this._keepingAlive||this.screen_update()):"screenOff"===e?(this.screen_update(),this._keepingAlive=!1,this.config.force_stay_awake&&(this._keepAliveTimer=window.setTimeout(()=>{this._keepingAlive=!0,window.fully.turnScreenOn(),window.fully.turnScreenOff()},27e4))):"onMotion"===e&&this.fullyMotionTriggered(),this.sendUpdate({fully:{battery:window.fully.getBatteryLevel(),charging:window.fully.isPlugged(),motion:this._fullyMotion,ip:window.fully.getIp4Address()}}))}fullyMotionTriggered(){this._keepingAlive||(this._fullyMotion=!0,clearTimeout(this._motionTimeout),this._motionTimeout=setTimeout(()=>{this._fullyMotion=!1,this.fully_update()},5e3),this.fully_update())}},E=e=>class extends e{setup_camera(){console.log("Starting camera"),this._video||(this._video=document.createElement("video"),this._video.autoplay=!0,this._video.playsInline=!0,this._video.style.display="none",this._canvas=document.createElement("canvas"),this._canvas.style.display="none",document.body.appendChild(this._video),document.body.appendChild(this._canvas),navigator.mediaDevices&&(console.log("Starting devices"),navigator.mediaDevices.getUserMedia({video:!0,audio:!1}).then(e=>{this._video.srcObject=e,this._video.play(),this.update_camera()}),this._camera_framerate=2,window.addEventListener("click",()=>this._video.play(),{once:!0})))}update_camera(){this._canvas.width=this._video.videoWidth,this._canvas.height=this._video.videoHeight;this._canvas.getContext("2d").drawImage(this._video,0,0,this._video.videoWidth,this._video.videoHeight),this.sendUpdate({camera:this._canvas.toDataURL("image/jpeg")}),setTimeout(()=>this.update_camera(),Math.round(1e3/this._camera_framerate))}},q=e=>class extends e{constructor(){super(),this._blackout_panel=document.createElement("div"),this._screenSaver=void 0,this._screenSaverTimer=void 0,this._screenSaverTimeOut=0,this._screenSaver={fn:void 0,clearfn:void 0,timer:void 0,timeout:void 0,listeners:{},active:!1},this._blackout_panel.style.cssText="\n position: fixed;\n left: 0;\n top: 0;\n padding: 0;\n margin: 0;\n width: 100%;\n height: 100%;\n background: black;\n display: none;\n ",document.body.appendChild(this._blackout_panel)}screensaver_set(e,t,o){this._ss_clear(),this._screenSaver={fn:e,clearfn:t,timer:void 0,timeout:o,listeners:{},active:!1};const s=()=>this.screensaver_update();for(const e of["mousemove","mousedown","keydown","touchstart"])window.addEventListener(e,s),this._screenSaver.listeners[e]=s;this._screenSaver.timer=window.setTimeout(()=>this._ss_run(),1e3*o)}screensaver_update(){this._screenSaver.active?this.screensaver_stop():(window.clearTimeout(this._screenSaver.timer),this._screenSaver.timer=window.setTimeout(()=>this._ss_run(),1e3*this._screenSaver.timeout))}screensaver_stop(){this._ss_clear(),this._screenSaver.active=!1,this._screenSaver.clearfn&&this._screenSaver.clearfn(),this._screenSaver.timeout&&this.screensaver_set(this._screenSaver.fn,this._screenSaver.clearfn,this._screenSaver.timeout)}_ss_clear(){window.clearTimeout(this._screenSaverTimer);for(const[e,t]of Object.entries(this._screenSaver.listeners))window.removeEventListener(e,t)}_ss_run(){this._screenSaver.active=!0,this._screenSaver.fn()}do_blackout(e){this.screensaver_set(()=>{this.isFully?window.fully.turnScreenOff(!0):this._blackout_panel.style.display="block",this.screen_update()},()=>{(this._blackout_panel.style.display="block")&&(this._blackout_panel.style.display="none"),this.isFully&&!window.fully.getScreenOn()&&window.fully.turnScreenOn(),this.screen_update()},e||0)}no_blackout(){this.isFully&&window.fully.turnScreenOn(),this.screensaver_stop()}screen_update(){this.sendUpdate({screen:{blackout:this.isFully?!window.fully.getScreenOn():Boolean("block"===this._blackout_panel.style.display),brightness:this.isFully?window.fully.getScreenBrightness():void 0}})}},D=e=>class extends e{constructor(){super(),document.querySelector("home-assistant")&&document.querySelector("home-assistant").addEventListener("hass-more-info",e=>this._popup_card(e));null!==document.querySelector("hc-main")||h()}_popup_card(e){if(!d())return;if(!e.detail||!e.detail.entityId)return;const t={...d().config.popup_cards,...d().config.views[d().current_view].popup_cards}[e.detail.entityId];t&&(v(t.title,t.card,t.large||!1,t.style),window.setTimeout(()=>{w("hass-more-info",{entityID:"."},document.querySelector("home-assistant"))},50))}do_popup(e){if(!(e.title||e.auto_close||e.hide_header))return;if(!e.card)return;const t=()=>{v(e.title,e.card,e.large,e.style,e.auto_close||e.hide_header)};e.auto_close?this.screensaver_set(t,_,e.time):t()}do_close_popup(){this.screensaver_stop(),_()}do_more_info(e,t){e&&g(e,t)}do_toast(e,t){e&&w("hass-notification",{message:e,duration:parseInt(t)},document.querySelector("home-assistant"))}},O=e=>class extends e{constructor(){super(),document.addEventListener("visibilitychange",()=>this.sensor_update()),window.addEventListener("location-changed",()=>this.sensor_update()),window.setInterval(()=>this.sensor_update(),1e4)}sensor_update(){window.queueMicrotask(async()=>{const e=navigator.getBattery?await navigator.getBattery():void 0;this.sendUpdate({browser:{path:window.location.pathname,visibility:document.visibilityState,userAgent:navigator.userAgent,currentUser:this._hass&&this._hass.user&&this._hass.user.name,fullyKiosk:this.isFully,width:window.innerWidth,height:window.innerHeight,battery_level:this.isFully?window.fully.getBatteryLevel():e?100*e.level:void 0,charging:this.isFully?window.fully.isPlugged():e?e.charging:void 0}})})}do_navigate(e){e&&(history.pushState(null,"",e),w("location-changed",{},document.querySelector("home-assistant")))}};class C extends(((e,t)=>t.reduceRight((e,t)=>t(e),e))(S,[O,D,q,E,x,k])){constructor(){super(),this.entity_id=a.replace("-","_"),this.cast=null!==document.querySelector("hc-main"),this.connect();const e=o(0);console.info(`%cBROWSER_MOD ${e.version} IS INSTALLED\n %cDeviceID: ${a}`,"color: green; font-weight: bold","")}msg_callback(e){({update:e=>this.update(e),debug:e=>this.debug(e),play:e=>this.player_play(e.media_content_id),pause:e=>this.player_pause(),stop:e=>this.player_stop(),set_volume:e=>this.player_set_volume(e.volume_level),mute:e=>this.player_mute(e.mute),toast:e=>this.do_toast(e.message,e.duration),popup:e=>this.do_popup(e),"close-popup":e=>this.do_close_popup(),"more-info":e=>this.do_more_info(e.entity_id,e.large),navigate:e=>this.do_navigate(e.navigation_path),"set-theme":e=>this.set_theme(e),"lovelace-reload":e=>this.lovelace_reload(e),"window-reload":()=>window.location.reload(!1),blackout:e=>this.do_blackout(e.time?parseInt(e.time):void 0),"no-blackout":e=>{e.brightness&&this.isFully&&window.fully.setScreenBrightness(e.brightness),this.no_blackout()}})[e.command](e)}debug(e){v("deviceID",{type:"markdown",content:"# "+a}),alert(a)}set_theme(e){e.theme||(e.theme="default"),w("settheme",{theme:e.theme},document.querySelector("home-assistant"))}lovelace_reload(e){const t=u();t&&w("config-refresh",{},t)}update(e=null){e&&(e.name&&(this.entity_id=e.name.toLowerCase()),e.camera&&this.setup_camera(),this.config={...this.config,...e}),this.player_update(),this.fully_update(),this.screen_update(),this.sensor_update()}}const $=[customElements.whenDefined("home-assistant"),customElements.whenDefined("hc-main")];Promise.race($).then(()=>{window.browser_mod=window.browser_mod||new C})}]); \ No newline at end of file diff --git a/custom_components/browser_mod/camera.py b/custom_components/browser_mod/camera.py new file mode 100644 index 0000000..59baad4 --- /dev/null +++ b/custom_components/browser_mod/camera.py @@ -0,0 +1,37 @@ +import logging +from datetime import datetime +import base64 + +from homeassistant.const import STATE_UNAVAILABLE, STATE_ON, STATE_OFF, STATE_IDLE +from homeassistant.components.camera import Camera + +from .helpers import setup_platform, BrowserModEntity + +PLATFORM = 'camera' + +async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModCamera) + +class BrowserModCamera(Camera, BrowserModEntity): + domain = PLATFORM + + def __init__(self, hass, connection, deviceID, alias=None): + Camera.__init__(self) + BrowserModEntity.__init__(self, hass, connection, deviceID, alias) + self.last_seen = None + + def updated(self): + if self.last_seen is None or (datetime.now() - self.last_seen).seconds > 15: + self.last_seen = datetime.now() + self.schedule_update_ha_state() + + def camera_image(self): + return base64.b64decode(self.data.split(',')[1]) + + @property + def device_state_attributes(self): + return { + "type": "browser_mod", + "deviceID": self.deviceID, + "last_seen": self.last_seen, + } diff --git a/custom_components/browser_mod/connection.py b/custom_components/browser_mod/connection.py new file mode 100644 index 0000000..964381b --- /dev/null +++ b/custom_components/browser_mod/connection.py @@ -0,0 +1,125 @@ +import logging +import voluptuous as vol + +from homeassistant.components.websocket_api import ( + websocket_command, + result_message, + event_message, + async_register_command +) + +from .const import WS_CONNECT, WS_UPDATE +from .helpers import get_devices, create_entity, get_config, is_setup_complete + +_LOGGER = logging.getLogger(__name__) + + +async def setup_connection(hass, config): + + @websocket_command({ + vol.Required("type"): WS_CONNECT, + vol.Required("deviceID"): str, + }) + def handle_connect(hass, connection, msg): + deviceID = msg["deviceID"] + + device = get_devices(hass).get(deviceID, + BrowserModConnection(hass, deviceID)) + device.connect(connection, msg["id"]) + get_devices(hass)[deviceID] = device + + connection.send_message(result_message(msg["id"])) + + @websocket_command({ + vol.Required("type"): WS_UPDATE, + vol.Required("deviceID"): str, + vol.Optional("data"): dict, + }) + def handle_update(hass, connection, msg): + devices = get_devices(hass) + deviceID = msg["deviceID"] + if deviceID in devices: + devices[deviceID].update(msg.get("data", None)) + + async_register_command(hass, handle_connect) + async_register_command(hass, handle_update) + + +class BrowserModConnection: + def __init__(self, hass, deviceID): + self.hass = hass + self.deviceID = deviceID + self.connection = [] + + self.media_player = None + self.screen = None + self.sensor = None + self.fully = None + self.camera = None + + def connect(self, connection, cid): + self.connection.append((connection, cid)) + self.trigger_update() + + def disconnect(): + self.connection.remove((connection, cid)) + + connection.subscriptions[cid] = disconnect + + def send(self, command, **kwargs): + if self.connection: + connection, cid = self.connection[-1] + connection.send_message(event_message(cid, { + "command": command, + **kwargs, + })) + + def trigger_update(self): + if is_setup_complete(self.hass): + self.send("update", **get_config(self.hass, self.deviceID)) + + def update(self, data): + if data.get('browser'): + self.sensor = self.sensor or create_entity( + self.hass, + 'sensor', + self.deviceID, + self) + if self.sensor: + self.sensor.data = data.get('browser') + + if data.get('player'): + self.media_player = self.media_player or create_entity( + self.hass, + 'media_player', + self.deviceID, + self) + if self.media_player: + self.media_player.data = data.get('player') + + if data.get('screen'): + self.screen = self.screen or create_entity( + self.hass, + 'light', + self.deviceID, + self) + if self.screen: + self.screen.data = data.get('screen') + + if data.get('fully'): + self.fully = self.fully or create_entity( + self.hass, + 'binary_sensor', + self.deviceID, + self) + if self.fully: + self.fully.data = data.get('fully') + + if data.get('camera'): + self.camera = self.camera or create_entity( + self.hass, + 'camera', + self.deviceID, + self) + if self.camera: + self.camera.data = data.get('camera') diff --git a/custom_components/browser_mod/const.py b/custom_components/browser_mod/const.py new file mode 100644 index 0000000..95a54d6 --- /dev/null +++ b/custom_components/browser_mod/const.py @@ -0,0 +1,35 @@ +DOMAIN = "browser_mod" + +FRONTEND_SCRIPT_URL = "/browser_mod.js" + +DATA_EXTRA_MODULE_URL = 'frontend_extra_module_url' + +DATA_DEVICES = "devices" +DATA_ALIASES = "aliases" +DATA_ADDERS = "adders" +DATA_CONFIG = "config" +DATA_SETUP_COMPLETE = "setup_complete" + +CONFIG_DEVICES = "devices" +CONFIG_PREFIX = "prefix" +CONFIG_DISABLE = "disable" +CONFIG_DISABLE_ALL = "all" + +WS_ROOT = DOMAIN +WS_CONNECT = "{}/connect".format(WS_ROOT) +WS_UPDATE = "{}/update".format(WS_ROOT) +WS_CAMERA = "{}/camera".format(WS_ROOT) + +USER_COMMANDS = [ + "debug", + "popup", + "close-popup", + "navigate", + "more-info", + "set-theme", + "lovelace-reload", + "window-reload", + "blackout", + "no-blackout", + "toast", + ] diff --git a/custom_components/browser_mod/helpers.py b/custom_components/browser_mod/helpers.py new file mode 100644 index 0000000..b65b5fb --- /dev/null +++ b/custom_components/browser_mod/helpers.py @@ -0,0 +1,96 @@ +import logging + +from homeassistant.helpers.entity import Entity, async_generate_entity_id + +from .const import ( + DOMAIN, + DATA_DEVICES, + DATA_ALIASES, + DATA_ADDERS, + CONFIG_DEVICES, + DATA_CONFIG, + CONFIG_PREFIX, + CONFIG_DISABLE, + CONFIG_DISABLE_ALL, + DATA_SETUP_COMPLETE, +) + +_LOGGER = logging.getLogger(__name__) + + +def get_devices(hass): + return hass.data[DOMAIN][DATA_DEVICES] + + +def get_alias(hass, deviceID): + for k, v in hass.data[DOMAIN][DATA_ALIASES].items(): + if v == deviceID: + return k + return None + + +def get_config(hass, deviceID): + config = hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DEVICES, {}) + return config.get(deviceID, config.get(deviceID.replace('-', '_'), {})) + + +def create_entity(hass, platform, deviceID, connection): + conf = get_config(hass, deviceID) + if conf and (platform in conf.get(CONFIG_DISABLE, []) + or CONFIG_DISABLE_ALL in conf.get(CONFIG_DISABLE, [])): + return None + if not conf and \ + (platform in hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DISABLE, []) + or CONFIG_DISABLE_ALL in + hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_DISABLE, [])): + return None + adder = hass.data[DOMAIN][DATA_ADDERS][platform] + entity = adder(hass, deviceID, connection, get_alias(hass, deviceID)) + return entity + + +def setup_platform(hass, config, async_add_devices, platform, cls): + def adder(hass, deviceID, connection, alias=None): + entity = cls(hass, connection, deviceID, alias) + async_add_devices([entity]) + return entity + hass.data[DOMAIN][DATA_ADDERS][platform] = adder + return True + + +def is_setup_complete(hass): + return hass.data[DOMAIN][DATA_SETUP_COMPLETE] + + +class BrowserModEntity(Entity): + + def __init__(self, hass, connection, deviceID, alias=None): + self.hass = hass + self.connection = connection + self.deviceID = deviceID + self._data = {} + prefix = hass.data[DOMAIN][DATA_CONFIG].get(CONFIG_PREFIX, '') + self.entity_id = async_generate_entity_id( + self.domain+".{}", + alias or f"{prefix}{deviceID}", + hass=hass + ) + + def updated(self): + pass + + @property + def data(self): + return self._data + + @data.setter + def data(self, data): + self._data = data + self.updated() + + @property + def device_id(self): + return self.deviceID + + def send(self, command, **kwargs): + self.connection.send(command, **kwargs) diff --git a/custom_components/browser_mod/light.py b/custom_components/browser_mod/light.py new file mode 100644 index 0000000..ffd1fb8 --- /dev/null +++ b/custom_components/browser_mod/light.py @@ -0,0 +1,59 @@ +import logging +from datetime import datetime + +from homeassistant.const import STATE_UNAVAILABLE, STATE_ON, STATE_OFF +from homeassistant.components.light import LightEntity, SUPPORT_BRIGHTNESS + +from .helpers import setup_platform, BrowserModEntity + +PLATFORM = 'light' + +async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModLight) + +class BrowserModLight(LightEntity, BrowserModEntity): + domain = PLATFORM + + def __init__(self, hass, connection, deviceID, alias=None): + super().__init__(hass, connection, deviceID, alias) + self.last_seen = None + + def updated(self): + self.last_seen = datetime.now() + self.schedule_update_ha_state() + + @property + def state(self): + if not self.connection.connection: + return STATE_UNAVAILABLE + if self.data.get('blackout', False): + return STATE_OFF + return STATE_ON + + @property + def is_on(self): + return not self.data.get('blackout', False) + + @property + def device_state_attributes(self): + return { + "type": "browser_mod", + "deviceID": self.deviceID, + "last_seen": self.last_seen, + } + + @property + def supported_features(self): + if self.data.get('brightness', False): + return SUPPORT_BRIGHTNESS + return 0 + + @property + def brightness(self): + return self.data.get('brightness', None) + + def turn_on(self, **kwargs): + self.connection.send("no-blackout", **kwargs) + + def turn_off(self, **kwargs): + self.connection.send("blackout") diff --git a/custom_components/browser_mod/manifest.json b/custom_components/browser_mod/manifest.json new file mode 100644 index 0000000..a559ef5 --- /dev/null +++ b/custom_components/browser_mod/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "browser_mod", + "name": "Browser mod", + "documentation": "", + "dependencies": ["websocket_api", "http"], + "codeowners": [], + "requirements": [] +} diff --git a/custom_components/browser_mod/media_player.py b/custom_components/browser_mod/media_player.py new file mode 100644 index 0000000..b9b8d6c --- /dev/null +++ b/custom_components/browser_mod/media_player.py @@ -0,0 +1,82 @@ +import logging +from homeassistant.components.media_player import ( + SUPPORT_PLAY, SUPPORT_PLAY_MEDIA, + SUPPORT_PAUSE, SUPPORT_STOP, + SUPPORT_VOLUME_SET, SUPPORT_VOLUME_MUTE, + MediaPlayerEntity, + ) +from homeassistant.const import ( + STATE_UNAVAILABLE, + STATE_PAUSED, + STATE_PLAYING, + STATE_IDLE, + STATE_UNKNOWN, + ) + +from .helpers import setup_platform, BrowserModEntity + +_LOGGER = logging.getLogger(__name__) + +PLATFORM = 'media_player' + +async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModPlayer) + + +class BrowserModPlayer(MediaPlayerEntity, BrowserModEntity): + domain = PLATFORM + + def __init__(self, hass, connection, deviceID, alias=None): + super().__init__(hass, connection, deviceID, alias) + self.last_seen = None + + def updated(self): + self.schedule_update_ha_state() + + @property + def device_state_attributes(self): + return { + "type": "browser_mod", + "deviceID": self.deviceID, + } + + @property + def state(self): + if not self.connection.connection: + return STATE_UNAVAILABLE + state = self.data.get("state", "unknown") + return { + "playing": STATE_PLAYING, + "paused": STATE_PAUSED, + "stopped": STATE_IDLE, + }.get(state, STATE_UNKNOWN) + @property + def supported_features(self): + return ( + SUPPORT_PLAY | SUPPORT_PLAY_MEDIA | + SUPPORT_PAUSE | SUPPORT_STOP | + SUPPORT_VOLUME_SET | SUPPORT_VOLUME_MUTE + ) + @property + def volume_level(self): + return self.data.get("volume", 0) + @property + def is_volume_muted(self): + return self.data.get("muted", False) + @property + def media_content_id(self): + return self.data.get("src", "") + + def set_volume_level(self, volume): + self.connection.send("set_volume", volume_level=volume) + def mute_volume(self, mute): + self.connection.send("mute", mute=mute) + + def play_media(self, media_type, media_id, **kwargs): + self.connection.send("play", media_content_id=media_id) + def media_play(self): + self.connection.send("play") + def media_pause(self): + self.connection.send("pause") + def media_stop(self): + self.connection.send("stop") diff --git a/custom_components/browser_mod/mod_view.py b/custom_components/browser_mod/mod_view.py new file mode 100644 index 0000000..ec84dfb --- /dev/null +++ b/custom_components/browser_mod/mod_view.py @@ -0,0 +1,36 @@ +from aiohttp import web +from homeassistant.components.http import HomeAssistantView + +from .const import FRONTEND_SCRIPT_URL, DATA_EXTRA_MODULE_URL + + +def setup_view(hass): + if DATA_EXTRA_MODULE_URL not in hass.data: + hass.data[DATA_EXTRA_MODULE_URL] = set() + url_set = hass.data[DATA_EXTRA_MODULE_URL] + url_set.add(FRONTEND_SCRIPT_URL) + + hass.http.register_view(ModView(hass, FRONTEND_SCRIPT_URL)) + +class ModView(HomeAssistantView): + + name = "browser_mod_script" + requires_auth = False + + def __init__(self, hass, url): + self.url = url + self.config_dir = hass.config.path() + + async def get(self, request): + path = "{}/custom_components/browser_mod/browser_mod.js".format(self.config_dir) + + filecontent = "" + + try: + with open(path, mode="r", encoding="utf-8", errors="ignore") as localfile: + filecontent = localfile.read() + localfile.close() + except Exception as exception: + pass + + return web.Response(body=filecontent, content_type="text/javascript", charset="utf-8") diff --git a/custom_components/browser_mod/sensor.py b/custom_components/browser_mod/sensor.py new file mode 100644 index 0000000..71c9a07 --- /dev/null +++ b/custom_components/browser_mod/sensor.py @@ -0,0 +1,37 @@ +import logging +from datetime import datetime + +from homeassistant.const import STATE_UNAVAILABLE + +from .helpers import setup_platform, BrowserModEntity + +PLATFORM = 'sensor' + +async def async_setup_platform(hass, config, async_add_devices, discovery_info=None): + return setup_platform(hass, config, async_add_devices, PLATFORM, BrowserModSensor) + +class BrowserModSensor(BrowserModEntity): + domain = PLATFORM + + def __init__(self, hass, connection, deviceID, alias=None): + super().__init__(hass, connection, deviceID, alias) + self.last_seen = None + + def updated(self): + self.last_seen = datetime.now() + self.schedule_update_ha_state() + + @property + def state(self): + if not self.connection.connection: + return STATE_UNAVAILABLE + return len(self.connection.connection) + + @property + def device_state_attributes(self): + return { + "type": "browser_mod", + "last_seen": self.last_seen, + "deviceID": self.deviceID, + **self.data + } diff --git a/custom_components/browser_mod/service.py b/custom_components/browser_mod/service.py new file mode 100644 index 0000000..9ed092e --- /dev/null +++ b/custom_components/browser_mod/service.py @@ -0,0 +1,37 @@ +import logging +from .const import DOMAIN, DATA_DEVICES, DATA_ALIASES, USER_COMMANDS + +_LOGGER = logging.getLogger(__name__) + +async def setup_service(hass): + + def handle_command(call): + command = call.data.get("command", None) + if not command: + return + + targets = call.data.get("deviceID", None) + if isinstance(targets, str): + targets = [targets] + devices = hass.data[DOMAIN][DATA_DEVICES] + aliases = hass.data[DOMAIN][DATA_ALIASES] + if not targets: + targets = devices.keys() + targets = [aliases.get(t, t) for t in targets] + + data = dict(call.data) + del data["command"] + + for t in targets: + if t in devices: + devices[t].send(command, **data) + + def command_wrapper(call): + command = call.service.replace('_','-') + call.data = dict(call.data) + call.data['command'] = command + handle_command(call) + + hass.services.async_register(DOMAIN, 'command', handle_command) + for cmd in USER_COMMANDS: + hass.services.async_register(DOMAIN, cmd.replace('-','_'), command_wrapper) diff --git a/custom_components/browser_mod/services.yaml b/custom_components/browser_mod/services.yaml new file mode 100644 index 0000000..f9225fe --- /dev/null +++ b/custom_components/browser_mod/services.yaml @@ -0,0 +1,85 @@ +command: + description: 'Send a command to a browser.' + fields: + command: + description: 'Command to send' + example: 'navigate' + deviceID: + description: 'List of receiving browsers' + example: '["99980b13-dabc9563", "office_computer"]' +debug: + description: 'On all browsers, show a popup, and a javascript alert with the current device ID.' +set_theme: + description: 'On all browsers, change the theme.' + fields: + theme: + description: 'Theme to change to' + example: '{theme: "clear_light"}' +navigate: + description: 'Navigate to a path on a browser.' + fields: + navigation_path: + description: 'Path to navigate to' + example: '/lovelace/1' + deviceID: + description: 'List of receiving browsers' + example: '["99980b13-dabc9563", "office_computer"]' +more_info: + description: 'Open the more info dialog of an entity on a browser.' + fields: + entity_id: + description: 'Entity to show more info for' + example: 'camera.front_door' + deviceID: + description: 'List of receiving browsers' + example: '["99980b13-dabc9563", "office_computer"]' + large: + description: '(optional) Set to true to make wider' + example: 'true' +toast: + description: 'Show a toast message in the bottom left on all browsers.' + fields: + message: + description: 'Message to show' + example: 'Short message' + duration: + description: '(optional) Time in milliseconds to show message for. Set to 0 for persistent display.' + example: '10000' +popup: + description: 'Pop up a card on a browser.' + fields: + title: + description: 'Name to show in popup bar' + example: 'Popup example' + card: + description: 'YAML config for card to show' + deviceID: + description: 'List of receiving browsers' + example: '["99980b13-dabc9563", "office_computer"]' + large: + description: '(optional) Set to true to make wider' + example: 'true' + hide_header: + description: '(optional) Hide header title and close button' + example: 'true' + auto_close: + description: '(optional) Close popup when mouse is moved or key is pressed. Also hides header' + example: 'true' + time: + description: "(optional) When mouse isn't moved or keys aren't pressed for this amount of seconds, reopen. Only usable with auto_close. See blackout" + example: '20' +close_popup: + description: 'Close all popups on all browsers.' +blackout: + description: 'Cover screen in black until the mouse is moved or a key is pressed.' + fields: + time: + description: '(optional) The blackout will turn on automatically after the specified number of seconds. It works kind of like a screensaver and will keep turning on until blackout is called again with time: -1.' + example: '20' +no_blackout: + description: 'Remove a blackout from a browser.' + fields: + brightness: + description: '(optional) On a Fully Kiosk Browser Plus set the screen brightness from 0 - 255.' +lovelace_reload: + description: 'Refresh the lovelace configuration.' diff --git a/custom_components/hacs/.translations/da.json b/custom_components/hacs/.translations/da.json deleted file mode 100644 index 8e45229..0000000 --- a/custom_components/hacs/.translations/da.json +++ /dev/null @@ -1,191 +0,0 @@ -{ - "common": { - "about": "Om", - "appdaemon_apps": "AppDaemon-apps", - "background_task": "Baggrundsopgave kører. Denne side vil genindlæses automatisk.", - "check_log_file": "Tjek din logfil for flere detaljer.", - "continue": "Fortsæt", - "disabled": "Deaktiveret", - "documentation": "Dokumentation", - "hacs_is_disabled": "HACS er deaktiveret", - "installed": "installeret", - "integration": "Integration", - "integrations": "Integrationer", - "manage": "administrer", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Python-script", - "python_scripts": "Python-scripts", - "repositories": "Repositories", - "settings": "indstillinger", - "theme": "Tema", - "themes": "Temaer", - "version": "Version" - }, - "config": { - "abort": { - "single_instance_allowed": "Kun en enkelt konfiguration af HACS er tilladt." - }, - "error": { - "auth": "Personlig adgangstoken er ikke korrekt." - }, - "step": { - "user": { - "data": { - "appdaemon": "Aktiver opdagelse & sporing af AppDaemon-apps", - "python_script": "Aktivér opdagelse og sporing af python_scripts", - "sidepanel_icon": "Sidepanelikon", - "sidepanel_title": "Sidepanelets titel", - "theme": "Aktivér opdagelse og sporing af temaer", - "token": "GitHub personlig adgangstoken" - }, - "description": "Hvis du har brug for hjælp til konfigurationen, så kig her: https:\/\/hacs.xyz\/docs\/configuration\/start" - } - } - }, - "confirm": { - "add_to_lovelace": "Er du sikker på, at du vil tilføje dette til dine Lovelace-ressourcer?", - "bg_task": "Handlingen er deaktiveret, mens baggrundsopgaver kører.", - "cancel": "Annuller", - "continue": "Er du sikker på, at du vil fortsætte?", - "delete": "Er du sikker på, at du vil slette '{Item}'?", - "delete_installed": "'{item}' er installeret. Du skal afinstallere det, før du kan slette det.", - "exist": "{item} findes allerede", - "generic": "Er du sikker?", - "home_assistant_is_restarting": "Vent venligst - Home Assistant genstarter nu.", - "home_assistant_version_not_correct": "Du kører Home Assistant version '{haversion}', men dette repository kræver som minimum version '{minversion}'.", - "no": "Nej", - "no_upgrades": "Der er ingen opdateringer tilgængelig", - "ok": "OK", - "overwrite": "Dette vil overskrive den.", - "reload_data": "Dette genindlæser data fra alle repositories, som HACS kender til. Dette vil tage nogen tid at fuldføre.", - "restart_home_assistant": "Er du sikker på, at du vil genstarte Home Assistant?", - "uninstall": "Er du sikker på, at du vil afinstallere '{Item}'?", - "upgrade_all": "Dette vil opdatere alle repositories. Sørg for at du har læst udgivelsesnoterne for dem alle, inden du fortsætter.", - "yes": "Ja" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Aktiver opdagelse og sporing af AppDaemon-apps", - "country": "Filtrer med landekode.", - "debug": "Aktiver debug.", - "experimental": "Aktivér eksperimentelle funktioner", - "not_in_use": "Ikke i brug med YAML", - "release_limit": "Antal udgivelser, der skal vises.", - "sidepanel_icon": "Sidepanelikon", - "sidepanel_title": "Sidepanelets titel" - } - } - } - }, - "repository_banner": { - "config_flow": "Denne integration understøtter config_flow. Det betyder, at du nu kan gå til integrationssektionen i din brugerflade for at konfigurere den.", - "config_flow_title": "Brugerfladekonfiguration understøttet", - "integration_not_loaded": "Denne integration er ikke indlæst i Home Assistant.", - "no_restart_required": "Ingen genstart påkrævet", - "not_loaded": "Ikke indlæst", - "plugin_not_loaded": "Dette plugin er ikke føjet til dine Lovelace-ressourcer.", - "restart": "Du skal genstarte Home Assistant.", - "restart_pending": "Afventer genstart" - }, - "repository": { - "add_to_lovelace": "Tilføj til Lovelace", - "authors": "Forfattere", - "available": "Tilgængelig", - "back_to": "Tilbage til", - "changelog": "Udgivelsesnoter", - "downloads": "Downloads", - "flag_this": "Marker denne", - "frontend_version": "Frontend-version", - "github_stars": "GitHub-stjerner", - "goto_integrations": "Gå til integrationer", - "hide": "Skjul", - "hide_beta": "Skjul beta", - "install": "Installer", - "installed": "Installeret", - "lovelace_copy_example": "Kopiér eksemplet til din Udklipsholder", - "lovelace_instruction": "Tilføj dette til din lovelace-konfiguration", - "lovelace_no_js_type": "Kunne ikke afgøre typen af dette plugin, tjek venligst repository.", - "newest": "nyeste", - "note_appdaemon": "Du skal stadig føje den til filen 'apps.yaml'", - "note_installed": "Når det er installeret, vil dette være placeret i", - "note_integration": "du skal stadig føje den til filen 'configuration.yaml'", - "note_plugin": "du skal stadig tilføje det til din lovelace-konfiguration ('ui-lovelace.yaml' eller Tekstbaseret redigering)", - "open_issue": "Opret issue", - "open_plugin": "Åbn plugin", - "reinstall": "Geninstaller", - "repository": "Repository", - "restart_home_assistant": "Genstart Home Assistant", - "show_beta": "Vis beta", - "uninstall": "Afinstaller", - "update_information": "Opdater oplysninger", - "upgrade": "Opdater" - }, - "settings": { - "add_custom_repository": "TILFØJ ET BRUGERDEFINERET REPOSITORY", - "adding_new_repo": "Tilføjer nyt repository '{repo}'", - "adding_new_repo_category": "Med kategorien '{category}'.", - "bg_task_custom": "Brugerdefinerede repositories er skjult, mens opgaver i baggrunden kører.", - "category": "Kategori", - "compact_mode": "Kompakt tilstand", - "custom_repositories": "BRUGERDEFINEREDE REPOSITORIES", - "delete": "Slet", - "display": "Visning", - "grid": "Gitter", - "hacs_repo": "HACS-repo", - "hidden_repositories": "Skjulte repositories", - "missing_category": "Du skal vælge en kategori", - "open_repository": "Åbn repository", - "reload_data": "Genindlæs data", - "reload_window": "Genindlæs vindue", - "repository_configuration": "Konfiguration af repository", - "save": "Gem", - "table": "Tabel", - "table_view": "Tabelvisning", - "unhide": "Vis", - "upgrade_all": "Opdater alle" - }, - "store": { - "ascending": "stigende", - "clear_new": "Marker alle som set", - "descending": "faldende", - "last_updated": "Sidst opdateret", - "name": "Navn", - "new_repositories": "Nye repositories", - "pending_upgrades": "Afventende opdateringer", - "placeholder_search": "Indtast en søgeterm...", - "sort": "sorter", - "stars": "Stjerner", - "status": "Status" - }, - "time": { - "ago": "siden", - "day": "dag", - "days": "dage", - "hour": "time", - "hours": "timer", - "minute": "minut", - "minutes": "minutter", - "month": "måned", - "months": "måneder", - "one": "Et", - "one_day_ago": "en dag siden", - "one_hour_ago": "en time siden", - "one_minute_ago": "et minut siden", - "one_month_ago": "en måned siden", - "one_second_ago": "et sekund siden", - "one_year_ago": "et år siden", - "second": "sekund", - "seconds": "sekunder", - "x_days_ago": "{x} dage siden", - "x_hours_ago": "{x} timer siden", - "x_minutes_ago": "{x} minutter siden", - "x_months_ago": "{x} måneder siden", - "x_seconds_ago": "{x} sekunder siden", - "x_years_ago": "{x} år siden", - "year": "år", - "years": "år" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/de.json b/custom_components/hacs/.translations/de.json deleted file mode 100644 index f96ac00..0000000 --- a/custom_components/hacs/.translations/de.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "common": { - "about": "Über", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon Apps", - "background_task": "Hintergrundprozess läuft. Diese Seite lädt neu, sobald dieser fertig ist.", - "check_log_file": "Überprüfe die Logdatei für weitere Informationen.", - "continue": "Fortfahren", - "disabled": "Deaktiviert", - "documentation": "Dokumentation", - "hacs_is_disabled": "HACS ist deaktiviert", - "installed": "Installiert", - "integration": "Integration", - "integrations": "Integrationen", - "manage": "verwalten", - "netdaemon": "NetDaemon", - "netdaemon_apps": "NetDaemon Apps", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Python Skript", - "python_scripts": "Python Skripte", - "repositories": "Repositories", - "settings": "Einstellungen", - "theme": "Theme", - "themes": "Themes", - "version": "Version" - }, - "config": { - "abort": { - "single_instance_allowed": "Es ist nur eine einzelne HACS-Instanz erlaubt." - }, - "error": { - "auth": "Persönlicher Zugriffstoken ist falsch." - }, - "step": { - "user": { - "data": { - "appdaemon": "AppDaemon App-Entdeckung & Nachverfolgung aktivieren", - "netdaemon": "NetDaemon App-Entdeckung & Nachverfolgung aktivieren", - "python_script": "Python Script-Entdeckung & Nachverfolgung aktivieren", - "sidepanel_icon": "Sidepanel Symbol", - "sidepanel_title": "Sidepanel Titel", - "theme": "Theme-Entdeckung & Nachverfolgung aktivieren", - "token": "Persönlicher GitHub Zugriffstoken" - }, - "description": "Wenn du Hilfe mit den Einstellungen brauchst, kannst du hier nachsehen: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Möchtest du dies wirklich zu deinen Lovelace-Ressourcen hinzufügen?", - "bg_task": "Die Aktion ist deaktiviert, während Hintergrundprozesse ausgeführt werden.", - "cancel": "Abbrechen", - "continue": "Bist du dir sicher, dass du fortfahren möchtest?", - "delete": "Möchtest du '{item}' wirklich löschen?", - "delete_installed": "'{item}' ist installiert. Du musst es deinstallieren, bevor du es löschen kannst.", - "exist": "{item} existiert bereits", - "generic": "Bist du dir sicher?", - "home_assistant_is_restarting": "Bitte warte, Home Assistant wird jetzt neu gestartet.", - "home_assistant_version_not_correct": "Du benutzt die Home Assistant-Version '{haversion}', für dieses Repository muss jedoch die Mindestversion '{minversion}' installiert sein.", - "no": "Nein", - "no_upgrades": "Keine Upgrades ausstehend", - "ok": "OK", - "overwrite": "Dadurch wird die Datei überschrieben.", - "reload_data": "Hierdurch werden die Daten aller Repositories die HACS kennt neu geladen. Dies wird einige Zeit in Anspruch nehmen.", - "restart_home_assistant": "Bist du dir sicher, dass du Home Assistant neu starten möchtest?", - "uninstall": "Möchtest du '{item}' wirklich deinstallieren?", - "upgrade_all": "Hierdurch werden all diese Repositories aktualisiert. Stelle sicher, dass du die Versionshinweise vorher gelesen hast.", - "yes": "Ja" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "AppDaemon App-Entdeckung & Nachverfolgung aktivieren", - "country": "Nach Ländercode filtern.", - "debug": "Debug aktivieren.", - "experimental": "Experimentelle Funktionen aktivieren", - "netdaemon": "NetDaemon App-Entdeckung & Nachverfolgung aktivieren", - "not_in_use": "Nicht in Verwendung mit YAML", - "release_limit": "Anzahl anzuzeigender Releases.", - "sidepanel_icon": "Sidepanel Symbol", - "sidepanel_title": "Sidepanel Titel" - } - } - } - }, - "repository_banner": { - "config_flow": "Diese Integration unterstützt config_flow. Das bedeutet, dass du jetzt in den Integrationsbereich deiner Benutzeroberfläche wechseln kannst, um es zu konfigurieren.", - "config_flow_title": "UI-Konfiguration unterstützt", - "integration_not_loaded": "Diese Integration ist in Home Assistant nicht geladen.", - "no_restart_required": "Kein Neustart erforderlich", - "not_loaded": "Nicht geladen", - "plugin_not_loaded": "Dieses Plugin wurde deinen Lovelace-Ressourcen nicht hinzugefügt.", - "restart": "Du musst Home Assistant neu starten.", - "restart_pending": "Neustart ausstehend" - }, - "repository": { - "add_to_lovelace": "Zu Lovelace hinzufügen", - "authors": "Autoren", - "available": "Verfügbar", - "back_to": "Zurück zu", - "changelog": "Änderungsprotokoll", - "downloads": "Downloads", - "flag_this": "Melden", - "frontend_version": "Frontend Version", - "github_stars": "GitHub Sterne", - "goto_integrations": "Gehe zu Integrationen", - "hide": "Verstecken", - "hide_beta": "Beta verstecken", - "install": "Installieren", - "installed": "Installiert", - "lovelace_copy_example": "Beispiel in die Zwischenablage kopieren", - "lovelace_instruction": "Zum Hinzufügen zu deinen Lovelace-Einstellungen, benutze Folgendes", - "lovelace_no_js_type": "Konnte die Art dieses Plugins nicht erkennen. Prüfe das Repository.", - "newest": "neueste", - "note_appdaemon": "du musst es dann noch in die Datei 'apps.yaml' hinzufügen", - "note_installed": "Wird installiert nach", - "note_integration": "du musst es dann noch in die Datei 'configuration.yaml' hinzufügen", - "note_plugin": "du musst es dann noch in deine Lovelace-Einstellungen ('ui-lovelace.yaml' oder im Raw-Konfigurationseditor) hinzufügen", - "note_plugin_post_107": "Du musst es noch zu deiner Lovelace-Konfiguration hinzufügen ('configuration.yaml' oder der Ressourceneditor '\/config\/lovelace\/resources')", - "open_issue": "Problem melden", - "open_plugin": "Plugin öffnen", - "reinstall": "Neu installieren", - "repository": "Repository", - "restart_home_assistant": "Home Assistant neu starten", - "show_beta": "Beta anzeigen", - "uninstall": "Deinstallieren", - "update_information": "Aktualisierungsinformationen", - "upgrade": "Aktualisieren" - }, - "settings": { - "add_custom_repository": "BENUTZERDEFINIERTES REPOSITORY HINZUFÜGEN", - "adding_new_repo": "Hinzufügen eines neuen Repository '{repo}'", - "adding_new_repo_category": "Mit der Kategorie '{category}'.", - "bg_task_custom": "Custom Repositorys werden ausgeblendet, während Hintergrundaufgaben ausgeführt werden.", - "category": "Kategorie", - "compact_mode": "Kompakter Modus", - "custom_repositories": "BENUTZERDEFINIERTE REPOSITORIES", - "delete": "Löschen", - "display": "Anzeige", - "grid": "Gitter", - "hacs_repo": "HACS repo", - "hidden_repositories": "versteckte Repositories", - "missing_category": "Du musst eine Kategorie auswählen.", - "open_repository": "Repository öffnen", - "reload_data": "Daten neu laden", - "reload_window": "Fenster neu laden", - "repository_configuration": "Repository Konfiguration", - "save": "Speichern", - "table": "Tabelle", - "table_view": "Tabellenansicht", - "unhide": "einblenden", - "upgrade_all": "Alles aktualisieren" - }, - "store": { - "ascending": "Aufsteigend", - "clear_new": "Alle neuen Repositories als gesehen markieren", - "descending": "Absteigend", - "last_updated": "Zuletzt aktualisiert", - "name": "Name", - "new_repositories": "Neue Repositories", - "pending_upgrades": "Ausstehende Upgrades", - "placeholder_search": "Suchbegriff eingeben…", - "sort": "Sortieren", - "stars": "Sterne", - "status": "Status" - }, - "time": { - "ago": "vor", - "day": "Tag", - "days": "Tage", - "hour": "Stunde", - "hours": "Stunden", - "minute": "Minute", - "minutes": "Minuten", - "month": "Monat", - "months": "Monate", - "one": "Eins", - "one_day_ago": "vor einem Tag", - "one_hour_ago": "vor einer Stunde", - "one_minute_ago": "vor einer Minute", - "one_month_ago": "vor einem Monat", - "one_second_ago": "vor einer Sekunde", - "one_year_ago": "vor einem Jahr", - "second": "Sekunde", - "seconds": "Sekunden", - "x_days_ago": "vor {x} Tagen", - "x_hours_ago": "vor {x} Stunden", - "x_minutes_ago": "vor {x} Minuten", - "x_months_ago": "vor {x} Monaten", - "x_seconds_ago": "vor {x} Sekunden", - "x_years_ago": "vor {x} Jahren", - "year": "Jahr", - "years": "Jahre" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/en.json b/custom_components/hacs/.translations/en.json deleted file mode 100644 index 387c89f..0000000 --- a/custom_components/hacs/.translations/en.json +++ /dev/null @@ -1,199 +0,0 @@ -{ - "common": { - "about": "About", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon Apps", - "background_task": "Background task running, this page will reload when it's done.", - "check_log_file": "Check your log file for more details.", - "continue": "Continue", - "disabled": "Disabled", - "documentation": "Documentation", - "hacs_is_disabled": "HACS is disabled", - "installed": "installed", - "integration": "Integration", - "integrations": "Integrations", - "manage": "manage", - "netdaemon": "NetDaemon", - "netdaemon_apps": "NetDaemon Apps", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Python Script", - "python_scripts": "Python Scripts", - "repositories": "Repositories", - "settings": "settings", - "theme": "Theme", - "themes": "Themes", - "version": "Version" - }, - "config": { - "abort": { - "single_instance_allowed": "Only a single configuration of HACS is allowed." - }, - "error": { - "auth": "Personal Access Token is not correct." - }, - "step": { - "user": { - "data": { - "appdaemon": "Enable AppDaemon apps discovery & tracking", - "netdaemon": "Enable NetDaemon apps discovery & tracking", - "python_script": "Enable python_scripts discovery & tracking", - "sidepanel_icon": "Side panel icon", - "sidepanel_title": "Side panel title", - "theme": "Enable Themes discovery & tracking", - "token": "GitHub Personal Access Token" - }, - "description": "If you need help with the configuration have a look here: https:\/\/hacs.xyz\/docs\/configuration\/start\/", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Are you sure you want to add this to your Lovelace resources?", - "bg_task": "Action is disabled while background tasks is running.", - "cancel": "Cancel", - "continue": "Are you sure you want to continue?", - "delete": "Are you sure you want to delete \"{item}\"?", - "delete_installed": "'{item}' is installed, you need to uninstall it before you can delete it.", - "exist": "{item} already exists", - "generic": "Are you sure?", - "home_assistant_is_restarting": "Hold on, Home Assistant is now restarting.", - "home_assistant_version_not_correct": "You are running Home Assistant version '{haversion}', but this repository requires minimum version '{minversion}' to be installed.", - "no": "No", - "no_upgrades": "No upgrades pending", - "ok": "OK", - "overwrite": "Doing this will overwrite it.", - "reload_data": "This reloads the data of all repositories HACS knows about, this will take some time to finish.", - "restart_home_assistant": "Are you sure you want to restart Home Assistant?", - "uninstall": "Are you sure you want to uninstall \"{item}\"?", - "upgrade_all": "This will upgrade all of these repositories, make sure that you have read the release notes for all of them before proceeding.", - "yes": "Yes" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Enable AppDaemon apps discovery & tracking", - "country": "Filter with country code.", - "debug": "Enable debug.", - "experimental": "Enable experimental features", - "netdaemon": "Enable NetDaemon apps discovery & tracking", - "not_in_use": "Not in use with YAML", - "release_limit": "Number of releases to show.", - "sidepanel_icon": "Side panel icon", - "sidepanel_title": "Side panel title" - } - } - } - }, - "repository_banner": { - "config_flow": "This integration supports config_flow, that means that you now can go to the integration section of your UI to configure it.", - "config_flow_title": "UI Configuration supported", - "integration_not_loaded": "This integration is not loaded in Home Assistant.", - "no_restart_required": "No restart required", - "not_loaded": "Not loaded", - "plugin_not_loaded": "This plugin is not added to your Lovelace resources.", - "restart": "You need to restart Home Assistant.", - "restart_pending": "Restart pending" - }, - "repository": { - "add_to_lovelace": "Add to Lovelace", - "authors": "Authors", - "available": "Available", - "back_to": "Back to", - "changelog": "Change log", - "downloads": "Downloads", - "flag_this": "Flag this", - "frontend_version": "Frontend version", - "github_stars": "GitHub stars", - "goto_integrations": "Go to integrations", - "hide": "Hide", - "hide_beta": "Hide beta", - "install": "Install", - "installed": "Installed", - "lovelace_copy_example": "Copy the example to your clipboard", - "lovelace_instruction": "When you add this to your lovelace configuration use this", - "lovelace_no_js_type": "Could not determine the type of this plugin, check the repository.", - "newest": "newest", - "note_appdaemon": "you still need to add it to your 'apps.yaml' file", - "note_installed": "When installed, this will be located in", - "note_integration": "you still need to add it to your 'configuration.yaml' file", - "note_plugin": "you still need to add it to your lovelace configuration ('ui-lovelace.yaml' or the raw UI config editor)", - "note_plugin_post_107": "you still need to add it to your lovelace configuration ('configuration.yaml' or the resource editor '\/config\/lovelace\/resources')", - "open_issue": "Open issue", - "open_plugin": "Open plugin", - "reinstall": "Reinstall", - "repository": "Repository", - "restart_home_assistant": "Restart Home Assistant", - "show_beta": "Show beta", - "uninstall": "Uninstall", - "update_information": "Update information", - "upgrade": "Update" - }, - "settings": { - "add_custom_repository": "ADD CUSTOM REPOSITORY", - "adding_new_repo": "Adding new repository '{repo}'", - "adding_new_repo_category": "With category '{category}'.", - "bg_task_custom": "Custom repositories are hidden while background tasks is running.", - "category": "Category", - "compact_mode": "Compact mode", - "custom_repositories": "CUSTOM REPOSITORIES", - "delete": "Delete", - "display": "Display", - "grid": "Grid", - "hacs_repo": "HACS repo", - "hidden_repositories": "hidden repositories", - "missing_category": "You need to select a category", - "open_repository": "Open repository", - "reload_data": "Reload data", - "reload_window": "Reload window", - "repository_configuration": "Repository configuration", - "save": "Save", - "table": "Table", - "table_view": "Table view", - "unhide": "unhide", - "upgrade_all": "Upgrade all" - }, - "store": { - "ascending": "ascending", - "clear_new": "Clear all new repositories", - "descending": "descending", - "last_updated": "Last updated", - "name": "Name", - "new_repositories": "New Repositories", - "pending_upgrades": "Pending upgrades", - "placeholder_search": "Please enter a search term...", - "sort": "sort", - "stars": "Stars", - "status": "Status" - }, - "time": { - "ago": "ago", - "day": "day", - "days": "days", - "hour": "hour", - "hours": "hours", - "minute": "minute", - "minutes": "minutes", - "month": "month", - "months": "months", - "one": "One", - "one_day_ago": "one day ago", - "one_hour_ago": "one hour ago", - "one_minute_ago": "one minute ago", - "one_month_ago": "one month ago", - "one_second_ago": "one second ago", - "one_year_ago": "one year ago", - "second": "second", - "seconds": "seconds", - "x_days_ago": "{x} days ago", - "x_hours_ago": "{x} hours ago", - "x_minutes_ago": "{x} minutes ago", - "x_months_ago": "{x} months ago", - "x_seconds_ago": "{x} seconds ago", - "x_years_ago": "{x} years ago", - "year": "year", - "years": "years" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/es.json b/custom_components/hacs/.translations/es.json deleted file mode 100644 index ccc56f4..0000000 --- a/custom_components/hacs/.translations/es.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "common": { - "about": "Acerca de", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon Apps", - "background_task": "Ejecutando tareas en segundo plano. Se refrescará automaticamente esta página al finalizar.", - "check_log_file": "Compruebe el archivo de registro para obtener más detalles.", - "continue": "Continuar", - "disabled": "Deshabilitado", - "documentation": "Documentación", - "hacs_is_disabled": "HACS está deshabilitado", - "installed": "instalado", - "integration": "Integración", - "integrations": "Integraciones", - "manage": "Administrar", - "netdaemon": "NetDaemon", - "netdaemon_apps": "NetDaemon Apps", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Python Script", - "python_scripts": "Python Scripts", - "repositories": "Repositorios", - "settings": "configuraciones", - "theme": "Tema", - "themes": "Temas", - "version": "Versión" - }, - "config": { - "abort": { - "single_instance_allowed": "Sólo se permite una única configuración de HACS." - }, - "error": { - "auth": "El token de acceso personal no es correcto." - }, - "step": { - "user": { - "data": { - "appdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de AppDaemon", - "netdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de NetDaemon", - "python_script": "Habilitar el descubrimiento y seguimiento en python_scripts", - "sidepanel_icon": "Ícono del panel lateral", - "sidepanel_title": "Título del panel lateral", - "theme": "Habilitar el descubrimiento y seguimiento de temas", - "token": "Token de acceso personal de GitHub" - }, - "description": "Si necesitas ayuda con la configuración, visita la siguiente pagina: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "¿Está seguro de que desea agregar esto a sus recursos de Lovelace?", - "bg_task": "La acción está deshabilitada mientras se ejecutan tareas en segundo plano.", - "cancel": "Cancelar", - "continue": "Estás seguro de que quieres continuar?", - "delete": "¿Seguro que quieres eliminar '{item}'?", - "delete_installed": "'{item}' está instalado, debe desinstalarlo antes de poder eliminarlo.", - "exist": "{item} ya existe", - "generic": "¿Estás seguro?", - "home_assistant_is_restarting": "Espera, Home Assistant se está reiniciando.", - "home_assistant_version_not_correct": "Está ejecutando la versión '{haversion}' de Home Assistant, pero este repositorio requiere la instalación de la versión '{minversion}' mínima.", - "no": "No", - "no_upgrades": "No hay actualizaciones pendientes", - "ok": "OK", - "overwrite": "Si lo hace, se sobrescribirá.", - "reload_data": "Esto recarga los datos de todos los repositorios que HACS conoce, esto tardará algún tiempo en finalizar.", - "restart_home_assistant": "¿Está seguro de que desea reiniciar Home Assistant?", - "uninstall": "¿Está seguro de que desea desinstalar '{item}'?", - "upgrade_all": "Esto actualizará todos estos repositorios, asegúrese de que ha leído las notas de la versión de todos ellos antes de continuar.", - "yes": "Si" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de AppDaemon", - "country": "Filtrar por el código de país.", - "debug": "Habilitar depuración.", - "experimental": "Habilitar funciones experimentales", - "netdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de NetDaemon", - "not_in_use": "No usarse con YAML", - "release_limit": "Número de versiones a mostrar.", - "sidepanel_icon": "Icono del panel lateral", - "sidepanel_title": "Título del panel lateral" - } - } - } - }, - "repository_banner": { - "config_flow": "Esta integración soporta config_flow, lo que significa que ahora puede ir a la sección de integración de su UI para configurarlo.", - "config_flow_title": "Configuración de UI soportada", - "integration_not_loaded": "Esta integración no se carga en Home Assistant.", - "no_restart_required": "No es necesario reiniciar", - "not_loaded": "No está cargado", - "plugin_not_loaded": "Este plugin no se añade a sus recursos de Lovelace.", - "restart": "Es necesario reiniciar Home Assistant.", - "restart_pending": "Reinicio pendiente" - }, - "repository": { - "add_to_lovelace": "Añadir a Lovelace", - "authors": "Autores", - "available": "Disponible", - "back_to": "Volver a", - "changelog": "Registro de cambios", - "downloads": "Descargas", - "flag_this": "Marcar esto", - "frontend_version": "Versión del frontend", - "github_stars": "Estrellas de GitHub", - "goto_integrations": "Ir a integraciones", - "hide": "Ocultar", - "hide_beta": "Ocultar beta", - "install": "Instalar", - "installed": "Instalado", - "lovelace_copy_example": "Copiar el ejemplo al clipboard", - "lovelace_instruction": "Agregue lo siguiente en su configuración de lovelace", - "lovelace_no_js_type": "No se pudo determinar el tipo de plugin, revise el repositorio.", - "newest": "más nuevo", - "note_appdaemon": "deberá agregar esto a su archivo 'apps.yaml'", - "note_installed": "Al instalarse se encontrará en", - "note_integration": "deberá agregar esto a su archivo 'configuration.yaml'", - "note_plugin": "deberá agregar esto a su configuración de lovelace ('ui-lovelace.yaml' o en el editor UI de lovelace)", - "open_issue": "Abrir issue", - "open_plugin": "Abrir plugin", - "reinstall": "Reinstalar", - "repository": "Repositorio", - "restart_home_assistant": "Reiniciar Home Assistant", - "show_beta": "Mostrar beta", - "uninstall": "Desinstalar", - "update_information": "Actualizar información", - "upgrade": "Actualizar" - }, - "settings": { - "add_custom_repository": "AGREGAR REPOSITORIO PERSONALIZADO", - "adding_new_repo": "Añadiendo un nuevo repositorio '{repo}'.", - "adding_new_repo_category": "Con la categoría '{category}'.", - "bg_task_custom": "Los repositorios personalizados están ocultos mientras se ejecutan las tareas en segundo plano.", - "category": "Categoría", - "compact_mode": "Modo compacto", - "custom_repositories": "REPOSITORIOS PERSONALIZADOS", - "delete": "Eliminar", - "display": "Mostrar", - "grid": "Cuadrícula", - "hacs_repo": "HACS repo", - "hidden_repositories": "repositorios ocultos", - "missing_category": "Es necesario seleccionar una categoría", - "open_repository": "Abrir repositorio", - "reload_data": "Recargar datos", - "reload_window": "Recargar ventana", - "repository_configuration": "Configuración del repositorio", - "save": "Grabar", - "table": "Tabla", - "table_view": "Vista de la tabla", - "unhide": "mostrar", - "upgrade_all": "Actualizar todo" - }, - "store": { - "ascending": "ascendente", - "clear_new": "Borrar todos los nuevos repositorios", - "descending": "descendente", - "last_updated": "Última actualización", - "name": "Nombre", - "new_repositories": "Nuevos Repositorios", - "pending_upgrades": "Actualizaciones pendientes", - "placeholder_search": "Por favor escriba una palabra clave de búsqueda...", - "sort": "ordenar", - "stars": "Estrellas", - "status": "Estado" - }, - "time": { - "ago": "hace", - "day": "dia", - "days": "dias", - "hour": "hora", - "hours": "horas", - "minute": "minuto", - "minutes": "minutos", - "month": "mes", - "months": "meses", - "one": "Uno", - "one_day_ago": "hace un día", - "one_hour_ago": "hace una hora", - "one_minute_ago": "hace un minuto", - "one_month_ago": "hace un mes", - "one_second_ago": "hace un segundo", - "one_year_ago": "hace un año", - "second": "segundo", - "seconds": "segundos", - "x_days_ago": "hace {x} dias", - "x_hours_ago": "hace {x} horas", - "x_minutes_ago": "hace {x} minutos", - "x_months_ago": "hace {x} meses", - "x_seconds_ago": "hace {x} segundos", - "x_years_ago": "hace {x} años", - "year": "año", - "years": "años" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/fr.json b/custom_components/hacs/.translations/fr.json deleted file mode 100644 index 2d2f487..0000000 --- a/custom_components/hacs/.translations/fr.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "common": { - "about": "À propos de", - "appdaemon": "AppDaemon", - "appdaemon_apps": "Applications AppDaemon", - "background_task": "Tache de fond en cours, cette page se rechargera une fois terminée", - "check_log_file": "Consultez votre fichier journal pour plus de détails.", - "continue": "Continuer", - "disabled": "Désactivé", - "documentation": "Documentation", - "hacs_is_disabled": "HACS est désactivé", - "installed": "installés", - "integration": "Intégration", - "integrations": "Intégrations", - "manage": "gérer", - "netdaemon": "NetDaemon", - "netdaemon_apps": "Applications NetDaemon", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Script Python", - "python_scripts": "Scripts Python", - "repositories": "Dépôts", - "settings": "paramètres", - "theme": "Thème", - "themes": "Thèmes", - "version": "Version" - }, - "config": { - "abort": { - "single_instance_allowed": "Une seule configuration de HACS est autorisée." - }, - "error": { - "auth": "Le jeton personnel d'accès est invalide." - }, - "step": { - "user": { - "data": { - "appdaemon": "Activer la découverte et le suivi des applications AppDaemon", - "netdaemon": "Activer la découverte et le suivi des applications NetDaemon", - "python_script": "Activer la découverte et le suivi des scripts python", - "sidepanel_icon": "Icône de la barre latérale", - "sidepanel_title": "Titre de la barre latérale", - "theme": "Activer la découverte et le suivi des thèmes", - "token": "Jeton d'accès personnel GitHub" - }, - "description": "Si vous avez besoin d'aide pour la configuration, jetez un œil ici: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Êtes-vous sûr de vouloir l'ajouter à vos ressources Lovelace ?", - "bg_task": "L'action est désactivée pendant l'exécution de tâches en arrière-plan.", - "cancel": "Annuler", - "continue": "Es-tu sur de vouloir continuer?", - "delete": "Êtes-vous sûr de vouloir supprimer '{item}'?", - "delete_installed": "'{item}' est installé, vous devez le désinstaller avant de pouvoir le supprimer.", - "exist": "{item} existe déjà", - "generic": "Êtes-vous sûr?", - "home_assistant_is_restarting": "Attendez, Home Assistant redémarre maintenant.", - "home_assistant_version_not_correct": "Vous utilisez la version '{haversion}' de Home Assistant, mais ce référentiel nécessite l'installation de la version minimale '{minversion}'.", - "no": "Non", - "no_upgrades": "Aucune mise à niveau en attente", - "ok": "OK", - "overwrite": "En faisant cela, cela l'écrasera.", - "reload_data": "Cela recharge les données de tous les dépôts dont HACS a connaissance, cela prendra un certain temps à terminer.", - "restart_home_assistant": "Voulez-vous vraiment redémarrer Home Assistant ?", - "uninstall": "Êtes-vous sûr de vouloir désinstaller '{item}'?", - "upgrade_all": "Cela mettra à niveau tous ces dépôts, assurez-vous d'avoir lu les notes de publication pour chacun d'entre eux avant de continuer.", - "yes": "Oui" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Activer la découverte et le suivi des applications AppDaemon", - "country": "Filtrer par code pays.", - "debug": "Activez le débogage.", - "experimental": "Activer les fonctionnalités expérimentales", - "netdaemon": "Activer la découverte et le suivi des applications NetDaemon", - "not_in_use": "Non utilisé avec YAML", - "release_limit": "Nombre de recensés à afficher.", - "sidepanel_icon": "Icône de la barre latérale", - "sidepanel_title": "Titre de la barre latérale" - } - } - } - }, - "repository_banner": { - "config_flow": "Cette intégration prend en charge config_flow, ce qui signifie que vous pouvez maintenant accéder à la section d'intégration de votre interface utilisateur pour le configurer.", - "config_flow_title": "Configuration de l'interface utilisateur prise en charge", - "integration_not_loaded": "Cette intégration n'est pas chargée dans Home Assistant.", - "no_restart_required": "Aucun redémarrage requis", - "not_loaded": "Non chargé", - "plugin_not_loaded": "Ce plugin n'est pas ajouté à vos ressources Lovelace.", - "restart": "Vous devez redémarrer Home Assistant.", - "restart_pending": "Redémarrage en attente" - }, - "repository": { - "add_to_lovelace": "Ajouter à Lovelace", - "authors": "Auteurs", - "available": "Disponible", - "back_to": "Retour", - "changelog": "Change log", - "downloads": "Téléchargements", - "flag_this": "Marquer", - "frontend_version": "Version de l'interface", - "github_stars": "Étoiles GitHub", - "goto_integrations": "Aller aux intégrations", - "hide": "Masquer", - "hide_beta": "Masquer les bêta", - "install": "Installer", - "installed": "Installé", - "lovelace_copy_example": "Copier cet exemple dans le presse-papier", - "lovelace_instruction": "Quand vous l'ajoutez à votre configuration lovelace, utilisez", - "lovelace_no_js_type": "Impossible de déterminer le type de plugin, veuillez vérifier le dépôt", - "newest": "nouveau", - "note_appdaemon": "vous devez toujours l'ajouter à votre fichier 'apps.yaml'", - "note_installed": "Une fois installé, il se trouvera dans", - "note_integration": "Vous devez toujours l'ajouter à votre fichier 'configuration.yaml'", - "note_plugin": "Vous devez toujours l'ajouter à votre configuration lovelace ('ui-lovelace.yaml' ou l'éditeur de configuration de l'interface)", - "open_issue": "Ouvrir un ticket", - "open_plugin": "Ouvrir le plugin", - "reinstall": "Réinstaller", - "repository": "Dépôt", - "restart_home_assistant": "Redémarrer Home Assistant", - "show_beta": "Afficher les bêta", - "uninstall": "Désinstaller", - "update_information": "Mettre à jour les informations", - "upgrade": "Mettre à jour" - }, - "settings": { - "add_custom_repository": "AJOUTER UN DÉPÔT PERSONNALISÉ", - "adding_new_repo": "Ajout d'un nouveau dépôt '{repo}'", - "adding_new_repo_category": "Avec la catégorie '{category}'.", - "bg_task_custom": "Les dépôts personnalisés sont masqués pendant l'exécution de tâches en arrière-plan.", - "category": "Catégorie", - "compact_mode": "Mode compact", - "custom_repositories": "DÉPÔTS PERSONNALISÉS", - "delete": "Supprimer", - "display": "Affichage", - "grid": "Grille", - "hacs_repo": "Dépôt HACS", - "hidden_repositories": "dépôts cachés", - "missing_category": "Vous devez sélectionner une catégorie", - "open_repository": "Ouvrir dépôt", - "reload_data": "Recharger les données", - "reload_window": "Recharger la fenêtre", - "repository_configuration": "Configuration de dépôt", - "save": "Enregistrer", - "table": "Tableau", - "table_view": "Vue table", - "unhide": "Afficher", - "upgrade_all": "Tout mettre à jour" - }, - "store": { - "ascending": "ascendant", - "clear_new": "Effacer tous les nouveaux dépôts", - "descending": "descendant", - "last_updated": "Dernière mise à jour", - "name": "Nom", - "new_repositories": "Nouveaux dépôts", - "pending_upgrades": "Mises à niveau en attente", - "placeholder_search": "Veuillez entrer un terme de recherche...", - "sort": "Trier", - "stars": "Étoiles", - "status": "Statut" - }, - "time": { - "ago": "il y a", - "day": "jour", - "days": "jours", - "hour": "heure", - "hours": "heures", - "minute": "minute", - "minutes": "minutes", - "month": "mois", - "months": "mois", - "one": "Un", - "one_day_ago": "il y a 1 jour", - "one_hour_ago": "il y a 1 heure", - "one_minute_ago": "il y a 1 minute", - "one_month_ago": "il y a 1 mois", - "one_second_ago": "il y a 1 seconde", - "one_year_ago": "il y a 1 an", - "second": "seconde", - "seconds": "secondes", - "x_days_ago": "il y a {x} jours", - "x_hours_ago": "il y a {x} heures", - "x_minutes_ago": "il y a {x} minutes", - "x_months_ago": "il y a {x} mois", - "x_seconds_ago": "il y a {x} secondes", - "x_years_ago": "il y a {x} ans", - "year": "an", - "years": "ans" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/hu.json b/custom_components/hacs/.translations/hu.json deleted file mode 100644 index 657e261..0000000 --- a/custom_components/hacs/.translations/hu.json +++ /dev/null @@ -1,194 +0,0 @@ -{ - "common": { - "about": "Névjegy", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon Appok", - "background_task": "Éppen háttérfeladat fut, ez az oldal frissülni fog, ha kész.", - "check_log_file": "További részletekért ellenőrizd a naplófájlt.", - "continue": "Folytatás", - "disabled": "Tiltva", - "documentation": "Dokumentáció", - "hacs_is_disabled": "A HACS le van tiltva", - "installed": "Telepített", - "integration": "Integráció", - "integrations": "Integrációk", - "manage": "kezelés", - "plugin": "Bővítmény", - "plugins": "Bővítmények", - "python_script": "Python Szkript", - "python_scripts": "Python Szkriptek", - "repositories": "Tárolók", - "settings": "beállítások", - "theme": "Téma", - "themes": "Témák", - "version": "Verzió" - }, - "config": { - "abort": { - "single_instance_allowed": "Csak egyetlen HACS-konfiguráció megengedett." - }, - "error": { - "auth": "A Személyes Hozzáférési Token nem megfelelő." - }, - "step": { - "user": { - "data": { - "appdaemon": "AppDaemon appok felfedezésének és nyomon követésének engedélyezése", - "python_script": "Python szkriptek felfedezésének és nyomon követésének engedélyezése", - "sidepanel_icon": "Oldalsó panel ikon", - "sidepanel_title": "Oldalsó panel cím", - "theme": "Témák felfedezésének és nyomon követésének engedélyezése", - "token": "GitHub Személyes Hozzáférési Token" - }, - "description": "Ha segítségre van szükséged a konfigurációval kapcsolatban, akkor tekintsd meg ezt az oldalt: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Biztosan hozzá szeretnéd ezt adni a Lovelace erőforrásokhoz?", - "bg_task": "A művelet le van tiltva, amíg háttérfeladat fut.", - "cancel": "Mégse", - "continue": "Biztosan folytatni szeretnéd?", - "delete": "Biztosan törölni szeretnéd a(z) '{item}'-t?", - "delete_installed": "A(z) '{item}' telepítve van, törlés előtt el kell távolítanod.", - "exist": "{item} már létezik", - "generic": "Biztos vagy benne?", - "home_assistant_is_restarting": "Várj, a Home Assistant éppen újraindul.", - "home_assistant_version_not_correct": "A Home Assistant '{haversion}' verzióját használod, de ehhez a tárolóhoz legalább a(z) '{minversion}' verzióra van szükség.", - "no": "Nem", - "no_upgrades": "Nincs függőben lévő frissítés", - "ok": "OK", - "overwrite": "Ezzel felül fogod írni.", - "reload_data": "Ez újratölti a HACS által ismert összes tároló adatát, ami némi időbe telhet.", - "restart_home_assistant": "Biztosan újraindítod a Home Assistant programot?", - "uninstall": "Biztosan el szeretnéd távolítani a(z) '{item}'-t?", - "upgrade_all": "Ez frissíteni fogja az összes tárolót. Győzödj meg róla, hogy elolvastad az összes kiadási megjegyzést, mielőtt továbblépnél.", - "yes": "Igen" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "AppDaemon appok felfedezésének és nyomon követésének engedélyezése", - "country": "Szűrés országkóddal.", - "debug": "Hibakeresés engedélyezése.", - "experimental": "Kísérleti funkciók engedélyezése", - "not_in_use": "YAML-lel nem használható", - "release_limit": "Megjelenítendő kiadások száma.", - "sidepanel_icon": "Oldalsó panel ikon", - "sidepanel_title": "Oldalsó panel cím" - } - } - } - }, - "repository_banner": { - "config_flow": "Ez az integráció támogatja a config_flow-t, vagyis a felhasználói felületen az integrációk menüben lehet konfigurálni.", - "config_flow_title": "A felhasználói felület konfigurációja támogatott", - "integration_not_loaded": "Ez az integráció nincs betöltve a Home Assistantban.", - "no_restart_required": "Nincs szükség újraindításra", - "not_loaded": "Nincs betöltve", - "plugin_not_loaded": "Ez a bővítmény nincs hozzáadva a Lovelace erőforrásokhoz.", - "restart": "Indítsd újra a Home Assistant programot.", - "restart_pending": "Újraindítás függőben" - }, - "repository": { - "add_to_lovelace": "Hozzáadás a Lovelace-hez", - "authors": "Szerzők", - "available": "Elérhető", - "back_to": "Vissza -", - "changelog": "Változási napló", - "downloads": "Letöltések", - "flag_this": "Megjelölés", - "frontend_version": "Frontend verzió", - "github_stars": "GitHub csillagok", - "goto_integrations": "Ugrás az integrációkhoz", - "hide": "Elrejtés", - "hide_beta": "Béta elrejtése", - "install": "Telepítés", - "installed": "Telepített", - "lovelace_copy_example": "Példa másolása a vágólapra", - "lovelace_instruction": "Amikor hozzáadod ezt a lovelace konfigurációdhoz, használd ezt", - "lovelace_no_js_type": "Nem sikerült meghatározni a beépülő modul típusát, ellenőrizd a tárolót.", - "newest": "legújabb", - "note_appdaemon": "de még hozzá kell adnod az 'apps.yaml' fájlhoz", - "note_installed": "Telepítéskor a következő helyre kerül:", - "note_integration": "de még hozzá kell adnod a 'configuration.yaml' fájlhoz", - "note_plugin": "de még hozzá kell adnod a lovelace konfigurációhoz (az 'ui-lovelace.yaml' fájlban vagy a Lovelace felületen a konfiguráció szerkesztőben)", - "open_issue": "Probléma jelentése", - "open_plugin": "Bővítmény megnyitása", - "reinstall": "Újratelepítés", - "repository": "Tároló", - "restart_home_assistant": "Home Assistant újraindítása", - "show_beta": "Béta megjelenítése", - "uninstall": "Eltávolítás", - "update_information": "Frissítési információk", - "upgrade": "Frissítés" - }, - "settings": { - "add_custom_repository": "EGYÉNI TÁROLÓ HOZZÁADÁSA", - "adding_new_repo": "Új tároló hozzáadása '{repo}'", - "adding_new_repo_category": "A '{category}' kategóriával.", - "bg_task_custom": "Az egyéni tárolók rejtve vannak, amíg háttérfeladat fut.", - "category": "Kategória", - "compact_mode": "Kompakt mód", - "custom_repositories": "EGYÉNI TÁROLÓK", - "delete": "Törlés", - "display": "Megjelenítés", - "grid": "Rács", - "hacs_repo": "HACS tároló", - "hidden_repositories": "rejtett tárolók", - "missing_category": "Ki kell választanod egy kategóriát", - "open_repository": "Tároló megnyitása", - "reload_data": "Adatok újratöltése", - "reload_window": "Ablak újratöltése", - "repository_configuration": "Tároló konfiguráció", - "save": "Mentés", - "table": "Táblázat", - "table_view": "Táblázat nézet", - "unhide": "felfedés", - "upgrade_all": "Minden frissítése" - }, - "store": { - "ascending": "növekvő", - "clear_new": "Új tárolók megjelölése látottként", - "descending": "csökkenő", - "last_updated": "Utolsó frissítés", - "name": "Név", - "new_repositories": "Új tárolók", - "pending_upgrades": "Függőben lévő frissítések", - "placeholder_search": "Kérlek adj meg egy keresési kifejezést...", - "sort": "rendezés", - "stars": "Csillag", - "status": "Állapot" - }, - "time": { - "ago": "ezelőtt", - "day": "nap", - "days": "nap", - "hour": "óra", - "hours": "óra", - "minute": "perc", - "minutes": "perc", - "month": "hónap", - "months": "hónap", - "one": "Egy", - "one_day_ago": "egy nappal ezelőtt", - "one_hour_ago": "egy órával ezelőtt", - "one_minute_ago": "egy perccel ezelőtt", - "one_month_ago": "egy hónappal ezelőtt", - "one_second_ago": "egy másodperccel ezelőtt", - "one_year_ago": "egy évvel ezelőtt", - "second": "másodperc", - "seconds": "másodperc", - "x_days_ago": "{x} nappal ezelőtt", - "x_hours_ago": "{x} órával ezelőtt", - "x_minutes_ago": "{x} perccel ezelőtt", - "x_months_ago": "{x} hónappal ezelőtt", - "x_seconds_ago": "{x} másodperccel ezelőtt", - "x_years_ago": "{x} évvel ezelőtt", - "year": "év", - "years": "év" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/it.json b/custom_components/hacs/.translations/it.json deleted file mode 100644 index fc42529..0000000 --- a/custom_components/hacs/.translations/it.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "common": { - "about": "Informazioni su", - "appdaemon": "AppDaemon", - "appdaemon_apps": "Applicazioni AppDaemon", - "background_task": "Attività in esecuzione, questa pagina sarà ricaricata al termine.", - "check_log_file": "Controlla il tuo file di registro per maggiori dettagli.", - "continue": "Continua", - "disabled": "Disabilitato", - "documentation": "Documentazione", - "hacs_is_disabled": "HACS è disabilitato", - "installed": "Installati", - "integration": "Integrazione", - "integrations": "Integrazioni", - "manage": "gestione", - "netdaemon": "NetDaemon", - "netdaemon_apps": "Applicazioni NetDaemon", - "plugin": "Plugin", - "plugins": "Plugin", - "python_script": "Script python", - "python_scripts": "Script python", - "repositories": "Repository", - "settings": "Impostazioni", - "theme": "Tema", - "themes": "Temi", - "version": "Versione" - }, - "config": { - "abort": { - "single_instance_allowed": "È consentita una sola configurazione di HACS." - }, - "error": { - "auth": "Il token di accesso personale non è corretto." - }, - "step": { - "user": { - "data": { - "appdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni AppDaemon", - "netdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni NetDaemon", - "python_script": "Abilita il rilevamento e il monitoraggio dei python_scripts", - "sidepanel_icon": "Icona nel pannello laterale", - "sidepanel_title": "Titolo nel pannello laterale", - "theme": "Abilita individuazione e tracciamento dei temi", - "token": "Token di accesso personale GitHub" - }, - "description": "Se hai bisogno di aiuto con la configurazione dai un'occhiata qui: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Sei sicuro di voler aggiungerlo alle tue risorse Lovelace?", - "bg_task": "L'azione è disabilitata mentre sono in esecuzione attività in background.", - "cancel": "Annulla", - "continue": "Sei sicuro di voler continuare?", - "delete": "Sei sicuro di voler disinstallare '{item}'?", - "delete_installed": "'{item}' è installato, è necessario disinstallarlo prima di poterlo eliminare.", - "exist": "{item} esiste già", - "generic": "Sei sicuro?", - "home_assistant_is_restarting": "Aspetta, Home Assistant si sta riavviando.", - "home_assistant_version_not_correct": "Stai eseguendo la versione Home Assistant '{haversion}', ma questo repository richiede l'installazione della versione minima '{minversion}'.", - "no": "No", - "no_upgrades": "Nessun aggiornamento in sospeso", - "ok": "OK", - "overwrite": "In questo modo lo sovrascriverà.", - "reload_data": "Questo ricarica i dati di tutte le repository di cui HACS è a conoscenza, ci vorrà del tempo per finire.", - "restart_home_assistant": "Sei sicuro di voler riavviare Home Assistant?", - "uninstall": "Sei sicuro di voler disinstallare '{item}'?", - "upgrade_all": "Questa azione aggiornerà tutti i repository, assicurati di aver letto le note di rilascio prima di procedere.", - "yes": "Sì" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni AppDaemon", - "country": "Filtra con prefisso internazionale.", - "debug": "Abilita debug.", - "experimental": "Abilita funzionalità sperimentali", - "netdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni NetDaemon", - "not_in_use": "Non in uso con YAML", - "release_limit": "Numero di versioni da mostrare.", - "sidepanel_icon": "Icona nel pannello laterale", - "sidepanel_title": "Titolo nel pannello laterale" - } - } - } - }, - "repository_banner": { - "config_flow": "Questa integrazione supporta config_flow, questo significa che è ora possibile passare alla sezione \"IntegrazionI\" dell'interfaccia utente per la configurazione.", - "config_flow_title": "Configurazione dell'interfaccia utente supportata", - "integration_not_loaded": "Questa integrazione non è caricata in Home Assistant.", - "no_restart_required": "Non è necessario riavviare", - "not_loaded": "Non caricato", - "plugin_not_loaded": "Questo plugin non è stato aggiunto alle risorse di Lovelace.", - "restart": "È necessario riavviare Home Assistant.", - "restart_pending": "Riavvio in attesa" - }, - "repository": { - "add_to_lovelace": "Aggiungi a Lovelace", - "authors": "Autori", - "available": "Disponibile", - "back_to": "Torna a", - "changelog": "Change log", - "downloads": "Downloads", - "flag_this": "Spunta questo", - "frontend_version": "Frontend versione", - "github_stars": "GitHub stelle", - "goto_integrations": "Vai alle Integrazioni", - "hide": "Nascondi", - "hide_beta": "Nascondi beta", - "install": "Installa", - "installed": "Installato", - "lovelace_copy_example": "Copia l'esempio negli appunti", - "lovelace_instruction": "Quando lo aggiungi nella configurazione di lovelace, usa questo", - "lovelace_no_js_type": "Impossibile determinare il tipo di plugin, verificare il repository.", - "newest": "Più recente", - "note_appdaemon": "dovrai aggiungerlo nel file 'apps.yaml'", - "note_installed": "Una volta installato, si troverà in", - "note_integration": "dovrai aggiungerlo nel file 'configuration.yaml'", - "note_plugin": "devi aggiungere la configurazione nel file 'ui-lovelace.yaml' oppure via Editor RAW della UI.", - "open_issue": "Segnala anomalia", - "open_plugin": "Apri plugin", - "reinstall": "Reinstalla", - "repository": "Archivio Software (Repository)", - "restart_home_assistant": "Riavvia Home Assistant", - "show_beta": "Visualizza beta", - "uninstall": "Rimuovi", - "update_information": "Aggiorna informazioni", - "upgrade": "Aggiorna" - }, - "settings": { - "add_custom_repository": "AGGIUNGI REPOSITORY PERSONALIZZATA", - "adding_new_repo": "Aggiunta di un nuovo repository '{repo}'", - "adding_new_repo_category": "Con la categoria '{category}'.", - "bg_task_custom": "I repository personalizzati sono nascosti mentre sono in esecuzione attività in background.", - "category": "Categoria", - "compact_mode": "Modalità compatta", - "custom_repositories": "REPOSITORY PERSONALIZZATE", - "delete": "Cancella", - "display": "Visualizza", - "grid": "Griglia", - "hacs_repo": "HACS repo", - "hidden_repositories": "repository nascosti", - "missing_category": "Devi selezionare una categoria", - "open_repository": "Apri il repository", - "reload_data": "Ricarica i dati", - "reload_window": "Ricarica la finestra", - "repository_configuration": "Configurazione del repository", - "save": "Salva", - "table": "Tabella", - "table_view": "Vista tabella", - "unhide": "Mostra", - "upgrade_all": "Aggiorna tutto" - }, - "store": { - "ascending": "ascendente", - "clear_new": "Ripulisci i nuovi repository", - "descending": "discendente", - "last_updated": "Ultimo aggiornamento", - "name": "Nome", - "new_repositories": "Nuovi repository", - "pending_upgrades": "Aggiornamenti in sospeso", - "placeholder_search": "Inserire un termine di ricerca", - "sort": "Ordinare", - "stars": "Stelle", - "status": "Stato" - }, - "time": { - "ago": "fa", - "day": "giorno", - "days": "giorni", - "hour": "ora", - "hours": "ore", - "minute": "minuto", - "minutes": "minuti", - "month": "mese", - "months": "mesi", - "one": "Un", - "one_day_ago": "un giorno fa", - "one_hour_ago": "un'ora fa", - "one_minute_ago": "un minuto fa", - "one_month_ago": "un mese fa", - "one_second_ago": "un secondo fa", - "one_year_ago": "un anno fa", - "second": "secondo", - "seconds": "secondi", - "x_days_ago": "{x} giorni fa", - "x_hours_ago": "{x} ore fa", - "x_minutes_ago": "{x} minuti fa", - "x_months_ago": "{x} mesi fa", - "x_seconds_ago": "{x} secondi fa", - "x_years_ago": "{x} anni fa", - "year": "anno", - "years": "anni" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/nb.json b/custom_components/hacs/.translations/nb.json deleted file mode 100644 index 0a7ef22..0000000 --- a/custom_components/hacs/.translations/nb.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "common": { - "about": "Om", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon Apper", - "background_task": "Bakgrunnsoppgaven kjører. Denne siden lastes inn på nytt når den er ferdig.", - "check_log_file": "Sjekk loggfilen din for mer informasjon.", - "continue": "Fortsett", - "disabled": "Deaktivert", - "documentation": "dokumentasjon", - "hacs_is_disabled": "HACS er deaktivert", - "installed": "Installert", - "integration": "Integrasjon", - "integrations": "Integrasjoner", - "manage": "manage", - "netdaemon": "NetDaemon", - "netdaemon_apps": "NetDaemon Apper", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Python-skript", - "python_scripts": "Python-skript", - "repositories": "Repositories", - "settings": "Innstillinger", - "theme": "Tema", - "themes": "Temaer", - "version": "Versjon" - }, - "config": { - "abort": { - "single_instance_allowed": "Bare en konfigurasjon av HACS er tillatt." - }, - "error": { - "auth": "Personlig tilgangstoken er ikke korrekt." - }, - "step": { - "user": { - "data": { - "appdaemon": "Aktiver oppdagelse og sporing av AppDaemon-apper", - "netdaemon": "Aktiver oppdagelse og sporing av NetDaemon-apper", - "python_script": "Aktiver oppdagelse og sporing av python_scripts", - "sidepanel_icon": "Sidepanel ikon", - "sidepanel_title": "Sidepanel tittel", - "theme": "Aktiver oppdagelse og sporing av temaer", - "token": "GitHub Personal Access Token" - }, - "description": "Hvis du trenger hjelp med konfigurasjonen, ta en titt her: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Er du sikker på at du vil legge dette til i dine Lovelace resources?", - "bg_task": "Handlingen er deaktivert mens bakgrunnsoppgaver kjører.", - "cancel": "Avbryt", - "continue": "Er du sikker på at du vil fortsette?", - "delete": "Er du sikker på at du vil fjerne '{item}'?", - "delete_installed": "'{item}' er installert, du må avinstallere det før du kan slette det.", - "exist": "{item} eksisterer allerede", - "generic": "Er du sikker?", - "home_assistant_is_restarting": "Vent, Home Assistant starter nå på nytt.", - "home_assistant_version_not_correct": "Du kjører Home Assistant '{haversion}', men denne krever minimum versjon '{minversion}' for å bli installert.", - "no": "Nei", - "no_upgrades": "Ingen oppgraderinger tilgjengelig", - "ok": "OK", - "overwrite": "Å gjøre dette vil overskrive det.", - "reload_data": "Dette laster inn dataene til alle repositories HACS vet om, dette vil ta litt tid å fullføre.", - "restart_home_assistant": "Er du sikker på at du vil starte Home Assistant på nytt?", - "uninstall": "Er du sikker på at du vil avinstallere '{item}'?", - "upgrade_all": "Dette vil oppgradere alle disse repositorene, sørg for at du har lest utgivelses notatene for dem alle før du fortsetter.", - "yes": "Ja" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Aktiver oppdagelse og sporing av AppDaemon-apper", - "country": "Filtrer med landskode.", - "debug": "Aktiver debug", - "experimental": "Aktiver eksperimentelle funksjoner", - "netdaemon": "Aktiver oppdagelse og sporing av NetDaemon-apper", - "not_in_use": "Ikke i bruk med YAML", - "release_limit": "Antall utgivelser som skal vises.", - "sidepanel_icon": "Sidepanel ikon", - "sidepanel_title": "Sidepanel tittel" - } - } - } - }, - "repository_banner": { - "config_flow": "Denne integrasjonen støtter config_flow, det betyr at du nå kan gå til integrasjoner i brukergrensesnittet for å konfigurere den.", - "config_flow_title": "UI konfigurasjon støttet", - "integration_not_loaded": "Integrasjonen er ikke lastet inn i Home Assistant.", - "no_restart_required": "Ingen omstart kreves", - "not_loaded": "Ikke lastet inn", - "plugin_not_loaded": "Denne pluginen er ikke lagt til i lovelace under \"resource\" delen av konfigurasjonen.", - "restart": "Du må restart Home Assistant", - "restart_pending": "Restart er nødvendig" - }, - "repository": { - "add_to_lovelace": "Legg til i Lovelace", - "authors": "Laget av", - "available": "Tilgjengelig", - "back_to": "Tilbake til", - "changelog": "Endringslogg", - "downloads": "Nedlastinger", - "flag_this": "Flag dette", - "frontend_version": "Frontend versjon", - "github_stars": "GitHub-stjerner", - "goto_integrations": "Gå til integrasjoner", - "hide": "Skjul", - "hide_beta": "Skjul beta", - "installed": "Installert", - "lovelace_copy_example": "Kopier eksemplet til utklippstavlen", - "lovelace_instruction": "Når du legger til dette i lovelace-konfigurasjonen din, bruk dette", - "lovelace_no_js_type": "Kunne ikke bestemme typen for denne plugin, sjekk repository.", - "newest": "Nyeste", - "note_appdaemon": "du må fortsatt legge den til i 'apps.yaml' filen", - "note_installed": "Når det er installert, vil dette ligge i", - "note_integration": "du må fortsatt legge den til 'configuration.yaml' filen", - "note_plugin": "du må fortsatt legge den til i lovelace-konfigurasjonen ('ui-lovelace.yaml' eller den rå UI-konfigurasjonsredigereren)", - "open_issue": "Meld et problem", - "open_plugin": "Åpne plugin", - "reinstall": "Installer på nytt", - "repository": "Repository", - "restart_home_assistant": "Start Home Assistant på nytt", - "show_beta": "Vis beta", - "uninstall": "Avinstaller", - "update_information": "Oppdater informasjon", - "upgrade": "Oppdater" - }, - "settings": { - "add_custom_repository": "LEGG TIL REPOSITORY", - "adding_new_repo": "Legger til ny repository '{repo}'", - "adding_new_repo_category": "Med kategori '{category}'.", - "bg_task_custom": "Custom repositories er skjult mens bakgrunnsoppgaver kjører.", - "category": "Kategori", - "compact_mode": "Kompakt modus", - "custom_repositories": "TILPASSEDE REPOSITORIER", - "delete": "Slett", - "display": "Vise", - "grid": "Nett", - "hacs_repo": "HACS repo", - "hidden_repositories": "Gjemte repositories", - "missing_category": "Du må velge en kategori", - "open_repository": "Åpne repository", - "reload_data": "Last inn data på nytt", - "reload_window": "Last inn vinduet på nytt", - "repository_configuration": "Repository konfigurasjon", - "save": "Lagre", - "table": "Tabell", - "table_view": "Tabellvisning", - "unhide": "Vis igjen", - "upgrade_all": "Oppgradere alle" - }, - "store": { - "ascending": "stigende", - "clear_new": "Tøm nye repositories", - "descending": "synkende", - "last_updated": "Sist oppdatert", - "name": "Navn", - "new_repositories": "Nye repositories", - "pending_upgrades": "Venter på oppgradering", - "placeholder_search": "Skriv inn et søkeord ...", - "sort": "sorter", - "stars": "Stjerner", - "status": "Status" - }, - "time": { - "ago": "siden", - "day": "dag", - "days": "dager", - "hour": "time", - "hours": "timer", - "minute": "minutt", - "minutes": "minutter", - "month": "måned", - "months": "måneder", - "one": "En", - "one_day_ago": "for en dag siden", - "one_hour_ago": "en time siden", - "one_minute_ago": "ett minutt siden", - "one_month_ago": "en måned siden", - "one_second_ago": "ett sekund siden", - "one_year_ago": "ett år siden", - "second": "sekund", - "seconds": "sekunder", - "x_days_ago": "{x} dager siden", - "x_hours_ago": "{x} timer siden", - "x_minutes_ago": "{x} minutter siden", - "x_months_ago": "{x} måneder siden", - "x_seconds_ago": "{x} sekunder siden", - "x_years_ago": "{x} år siden", - "year": "år", - "years": "år" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/nl.json b/custom_components/hacs/.translations/nl.json deleted file mode 100644 index 5a8de78..0000000 --- a/custom_components/hacs/.translations/nl.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "common": { - "about": "Over", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon Apps", - "background_task": "Achtergrond taak is draaiende, de pagina herhaalt zichzelf wanneer dit klaar is.", - "check_log_file": "Controleer het logbestand voor meer details.", - "continue": "Doorgaan", - "disabled": "Uitgeschakeld", - "documentation": "Documentatie", - "hacs_is_disabled": "HACS is uitgeschakeld", - "installed": "geinstalleerd", - "integration": "Integratie", - "integrations": "Integraties", - "manage": "beheer", - "netdaemon": "NetDaemon", - "netdaemon_apps": "NetDaemon Apps", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Python Script", - "python_scripts": "Python Scripts", - "repositories": "Repositories", - "settings": "instellingen", - "theme": "Thema", - "themes": "Themas", - "version": "Versie" - }, - "config": { - "abort": { - "single_instance_allowed": "Je kunt maar een enkele configuratie van HACS tegelijk hebben." - }, - "error": { - "auth": "Persoonlijke Toegang Token is niet correct." - }, - "step": { - "user": { - "data": { - "appdaemon": "Zet AppDaemon apps ontdekken & traceren aan", - "netdaemon": "Zet NetDaemon apps ontdekken & traceren aan", - "python_script": "Zet python_scripts ontdekken & traceren aan", - "sidepanel_icon": "Zijpaneel icoon", - "sidepanel_title": "Zijpaneel titel", - "theme": "Zet Themes ontdekken & traceren aan", - "token": "GitHub Persoonlijke Toegang Token" - }, - "description": "Als je hulp nodig hebt met de configuratie, kun je hier verder kijken: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Weet u zeker dat u dit wilt toevoegen aan uw Lovelace bronnen?", - "bg_task": "Actie is geblokkeerd terwijl achtergrondtaken actief zijn.", - "cancel": "Annuleer", - "continue": "Weet je zeker dat je wilt doorgaan?", - "delete": "Weet u zeker dat u '{item}' wilt verwijderen?", - "delete_installed": "'{item}' is geïnstalleerd, je dient het eerst te deïnstalleren voordat je het kan verwijderen.", - "exist": "{item} bestaat al.", - "generic": "Weet je het zeker?", - "home_assistant_is_restarting": "Een moment alstublieft, Home Assistant is aan het herstarten.", - "no": "Nee", - "no_upgrades": "Geen upgrades in afwachting.", - "ok": "Oké", - "overwrite": "Door dit te doen, wordt het overschreven.", - "reload_data": "Dit zal alle bekende data herladen van alle repositories van HACS. Dit kan even duren", - "restart_home_assistant": "Weet u zeker dat u Home Assistant opnieuw wilt starten?", - "uninstall": "Weet u zeker dat u '{item}' wilt verwijderen?", - "upgrade_all": "Hiermee worden al deze repositories geüpgraded. Zorg ervoor dat u de release-opmerkingen van allen heeft gelezen voordat u doorgaat.", - "yes": "Ja" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Zet AppDaemon apps ontdekken & traceren aan", - "country": "Filter met land code.", - "debug": "Schakel debug in.", - "experimental": "Zet experimentele functies aan", - "netdaemon": "Zet NetDaemon apps ontdekken & traceren aan", - "not_in_use": "Niet in gebruik met YAML", - "release_limit": "Aantal releases om te laten zien.", - "sidepanel_icon": "Zijpaneel icoon", - "sidepanel_title": "Zijpaneel titel" - } - } - } - }, - "repository_banner": { - "config_flow": "Deze integratie ondersteunt config_flow, wat betekent dat u via uw \"Instellingen\" naar \"Integraties\" kunt gaan om het te configureren.", - "config_flow_title": "UI-configuratie ondersteund", - "integration_not_loaded": "Deze integratie wordt niet geladen in Home Assistant.", - "no_restart_required": "Geen herstart vereist", - "not_loaded": "Niet geladen", - "plugin_not_loaded": "Deze plugin wordt niet toegevoegd aan je Lovelace resources.", - "restart": "U moet Home Assistant opnieuw starten.", - "restart_pending": "Wachten op herstart" - }, - "repository": { - "add_to_lovelace": "Toevoegen aan Lovelace", - "authors": "Auteurs", - "available": "Beschikbaar", - "back_to": "Terug naar", - "changelog": "Changelog", - "downloads": "Downloads", - "flag_this": "Vlag dit", - "frontend_version": "Frontend versie", - "github_stars": "GitHub-sterren", - "goto_integrations": "Ga naar integraties", - "hide": "Verberg", - "hide_beta": "Verberg beta", - "install": "Installeer", - "installed": "Geinstalleerd", - "lovelace_copy_example": "Kopier het voorbeeld naar je klembord", - "lovelace_instruction": "Wanneer je dit gaat toevoegen aan je lovelace configuratie gebruik dit", - "lovelace_no_js_type": "Kon niet achterhalen welk type plugin dit is, controleer de repository van de plugin.", - "newest": "nieuwste", - "note_appdaemon": "je moet het nog steeds toevoegen aan je 'apps.yaml' bestand", - "note_installed": "Wanneer geïnstalleerd, staat het in", - "note_integration": "je moet het nog steeds toevoegen aan je 'configuration.yaml' bestand", - "note_plugin": "je moet het nog steeds toevoegen aan je lovelace configuratie ('ui-lovelace.yaml') of raw UI config editor.", - "open_issue": "Meld probleem", - "open_plugin": "Open plugin", - "reinstall": "Herinstalleer", - "repository": "Repository", - "restart_home_assistant": "Start Home Assistant opnieuw", - "show_beta": "Laat beta zien", - "uninstall": "Verwijder", - "update_information": "Update informatie", - "upgrade": "Update" - }, - "settings": { - "add_custom_repository": "VOEG EIGEN REPOSITORY TOE", - "adding_new_repo": "Nieuwe repository '{repo}' toevoegen", - "adding_new_repo_category": "Met categorie '{category}'.", - "bg_task_custom": "Aangepaste repositories zijn verborgen terwijl de achtergrondtaken actief zijn.", - "category": "Categorie", - "compact_mode": "Compacte modus", - "custom_repositories": "EIGEN REPOSITORIES", - "delete": "Verwijder", - "display": "Weergave", - "grid": "Rooster", - "hacs_repo": "HACS repo", - "hidden_repositories": "verborgen repositories", - "missing_category": "Je moet een categorie selecteren.", - "open_repository": "Open repository", - "reload_data": "Herlaad data", - "reload_window": "Herlaad venster", - "repository_configuration": "Repository configuratie", - "save": "Opslaan", - "table": "Tabel", - "table_view": "Tabelweergave", - "unhide": "zichtbaar maken", - "upgrade_all": "Upgrade alles" - }, - "store": { - "ascending": "oplopend", - "clear_new": "Wissen van alle nieuwe repositories", - "descending": "Aflopend", - "last_updated": "Laatste update", - "name": "Naam", - "new_repositories": "Nieuwe Repositories", - "pending_upgrades": "Upgrades in afwachting", - "placeholder_search": "Typ iets om te zoeken...", - "sort": "sorteer", - "stars": "Sterren", - "status": "Status" - }, - "time": { - "ago": "geleden", - "day": "dag", - "days": "dagen", - "hour": "uur", - "hours": "uren", - "minute": "minuut", - "minutes": "minuten", - "month": "maand", - "months": "maanden", - "one": "Eén", - "one_day_ago": "een dag geleden", - "one_hour_ago": "een uur geleden", - "one_minute_ago": "een minuut geleden", - "one_month_ago": "een maand geleden", - "one_second_ago": "een seconde geleden", - "one_year_ago": "een jaar geleden", - "second": "seconde", - "seconds": "seconden", - "x_days_ago": "{x} dagen geleden", - "x_hours_ago": "{x} uur geleden", - "x_minutes_ago": "{x} minuten geleden", - "x_months_ago": "{x} maanden geleden", - "x_seconds_ago": "{x} seconden geleden", - "x_years_ago": "{x} jaar geleden", - "year": "jaar", - "years": "jaren" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/pl.json b/custom_components/hacs/.translations/pl.json deleted file mode 100644 index b76329d..0000000 --- a/custom_components/hacs/.translations/pl.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "common": { - "about": "O", - "appdaemon": "AppDaemon", - "appdaemon_apps": "Aplikacje AppDaemon", - "background_task": "Wykonywanie zadania w tle, ta strona zostanie odświeżona, gdy zadanie zostanie ukończone.", - "check_log_file": "Sprawdź plik dziennika, aby uzyskać więcej informacji.", - "continue": "Kontynuuj", - "disabled": "Wyłączony", - "documentation": "Dokumentacja", - "hacs_is_disabled": "HACS jest wyłączony", - "installed": "zainstalowane", - "integration": "Integracja", - "integrations": "Integracje", - "manage": "zarządzaj", - "netdaemon": "NetDaemon", - "netdaemon_apps": "Aplikacje NetDaemon", - "plugin": "Wtyczka", - "plugins": "Wtyczki", - "python_script": "Skrypt Python", - "python_scripts": "Skrypty Python", - "repositories": "Repozytoria", - "settings": "ustawienia", - "theme": "Motyw", - "themes": "Motywy", - "version": "Wersja" - }, - "config": { - "abort": { - "single_instance_allowed": "Dozwolona jest tylko jedna konfiguracja HACS." - }, - "error": { - "auth": "Osobisty token dostępu jest nieprawidłowy." - }, - "step": { - "user": { - "data": { - "appdaemon": "Włącz wykrywanie i śledzenie aplikacji AppDaemon", - "netdaemon": "Włącz wykrywanie i śledzenie aplikacji NetDaemon", - "python_script": "Włącz wykrywanie i śledzenie skryptów Python", - "sidepanel_icon": "Ikona w panelu bocznym", - "sidepanel_title": "Tytuł w panelu bocznym", - "theme": "Włącz wykrywanie i śledzenie motywów", - "token": "Osobisty token dostępu GitHub" - }, - "description": "Jeśli potrzebujesz pomocy w konfiguracji, przejdź na stronę: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Na pewno chcesz dodać to do swoich zasobów Lovelace?", - "bg_task": "Akcja jest wyłączona podczas wykonywania zadań w tle.", - "cancel": "Anuluj", - "continue": "Na pewno chcesz kontynuować?", - "delete": "Na pewno chcesz usunąć '{item}'?", - "delete_installed": "'{item}' jest zainstalowany, musisz go odinstalować zanim będziesz mógł go usunąć.", - "exist": "{item} już istnieje", - "generic": "Jesteś pewny?", - "home_assistant_is_restarting": "Poczekaj, Home Assistant jest teraz ponownie uruchamiany.", - "home_assistant_version_not_correct": "Używasz Home Assistant'a w wersji '{haversion}', a to repozytorium wymaga wersji minimum '{minversion}'.", - "no": "Nie", - "no_upgrades": "Brak oczekujących aktualizacji", - "ok": "Ok", - "overwrite": "Spowoduje to zastąpienie istniejącej kopii.", - "reload_data": "To przeładowuje dane wszystkich repozytoriów, o których wie HACS, może to trochę potrwać.", - "restart_home_assistant": "Na pewno chcesz ponownie uruchomić Home Assistant'a?", - "uninstall": "Na pewno chcesz odinstalować '{item}'?", - "upgrade_all": "To uaktualni wszystkie te repozytoria, upewnij się, że przeczytałeś uwagi do wydania dla wszystkich z nich przed kontynuacją.", - "yes": "Tak" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Włącz wykrywanie i śledzenie aplikacji AppDaemon", - "country": "Filtruj według kodu kraju", - "debug": "Włącz debugowanie.", - "experimental": "Włącz funkcje eksperymentalne", - "netdaemon": "Włącz wykrywanie i śledzenie aplikacji NetDaemon", - "not_in_use": "Nieużywany z YAML", - "release_limit": "Liczba wydań do wyświetlenia", - "sidepanel_icon": "Ikona w panelu bocznym", - "sidepanel_title": "Tytuł w panelu bocznym" - } - } - } - }, - "repository_banner": { - "config_flow": "Ta integracja obsługuje config_flow, co oznacza, że możesz teraz przejść do sekcji integracji w interfejsie użytkownika, aby ją skonfigurować.", - "config_flow_title": "Obsługiwana konfiguracja poprzez interfejs użytkownika", - "integration_not_loaded": "Ta integracja nie jest załadowana do Home Assistant'a.", - "no_restart_required": "Ponowne uruchomienie nie jest wymagane", - "not_loaded": "Nie załadowano", - "plugin_not_loaded": "Ta wtyczka nie jest dodana do zasobów Lovelace.", - "restart": "Musisz ponownie uruchomić Home Assistant'a.", - "restart_pending": "Oczekiwanie na ponowne uruchomienie" - }, - "repository": { - "add_to_lovelace": "Dodaj do Lovelace", - "authors": "Autorzy", - "available": "Dostępna", - "back_to": "Wróć do", - "changelog": "Lista zmian", - "downloads": "Ilość pobrań", - "flag_this": "Oflaguj", - "frontend_version": "Wersja frontendu", - "github_stars": "Gwiazdki GitHub", - "goto_integrations": "Przejdź do integracji", - "hide": "Ukryj", - "hide_beta": "Ukryj wydania beta", - "install": "Zainstaluj", - "installed": "Zainstalowano", - "lovelace_copy_example": "Skopiuj przykład do schowka", - "lovelace_instruction": "Interfejs użytkownika użyje tej wtyczki po dodaniu konfiguracji", - "lovelace_no_js_type": "Nie można określić typu tej wtyczki, sprawdź repozytorium.", - "newest": "najnowsza", - "note_appdaemon": "musisz jeszcze dodać aplikację do pliku 'apps.yaml'", - "note_installed": "Po zainstalowaniu dodatek będzie znajdować się w", - "note_integration": "musisz jeszcze dodać integrację do pliku 'configuration.yaml'", - "note_plugin": "musisz jeszcze dodać wtyczkę do konfiguracji interfejsu użytkownika (plik 'ui-lovelace.yaml' lub edytor interfejsu użytkownika)", - "open_issue": "Powiadom o problemie", - "open_plugin": "Otwórz dodatek", - "reinstall": "Przeinstaluj", - "repository": "Repozytorium", - "restart_home_assistant": "Uruchom ponownie Home Assistant'a", - "show_beta": "Wyświetl wydania beta", - "uninstall": "Odinstaluj", - "update_information": "Informacje o aktualizacji", - "upgrade": "Uaktualnij" - }, - "settings": { - "add_custom_repository": "DODAJ REPOZYTORIUM NIESTANDARDOWE", - "adding_new_repo": "Dodawanie nowego repozytorium '{repo}'", - "adding_new_repo_category": "Z kategorią '{category}'.", - "bg_task_custom": "Niestandardowe repozytoria są ukryte podczas wykonywania zadań w tle.", - "category": "Kategoria", - "compact_mode": "Tryb kompaktowy", - "custom_repositories": "REPOZYTORIA NIESTANDARDOWE", - "delete": "Usuń", - "display": "Sposób wyświetlania", - "grid": "Siatka", - "hacs_repo": "Repozytorium HACS", - "hidden_repositories": "ukryte repozytoria", - "missing_category": "Musisz wybrać kategorię", - "open_repository": "Otwórz repozytorium", - "reload_data": "Wczytaj ponownie dane", - "reload_window": "Załaduj ponownie okno", - "repository_configuration": "Konfiguracja repozytorium", - "save": "Zapisz", - "table": "tabela", - "table_view": "Widok tabeli", - "unhide": "pokaż", - "upgrade_all": "Uaktualnij wszystkie" - }, - "store": { - "ascending": "rosnąco", - "clear_new": "Wyczyść wszystkie nowe repozytoria", - "descending": "malejąco", - "last_updated": "Ostatnia aktualizacja", - "name": "Nazwa", - "new_repositories": "Nowe repozytoria", - "pending_upgrades": "Oczekujące aktualizacje", - "placeholder_search": "Wprowadź wyszukiwane hasło...", - "sort": "sortowanie", - "stars": "Gwiazdki", - "status": "Status" - }, - "time": { - "ago": "temu", - "day": "dzień", - "days": "dni", - "hour": "godzina", - "hours": "godziny", - "minute": "minuta", - "minutes": "minuty", - "month": "miesiąc", - "months": "miesięcy", - "one": "Jeden", - "one_day_ago": "jeden dzień temu", - "one_hour_ago": "jedna godzina temu", - "one_minute_ago": "jedna minuta temu", - "one_month_ago": "jeden miesiąc temu", - "one_second_ago": "jedna sekunda temu", - "one_year_ago": "jeden rok temu", - "second": "sekunda", - "seconds": "sekundy", - "x_days_ago": "{x} dni temu", - "x_hours_ago": "{x} godzin(-y) temu", - "x_minutes_ago": "{x} minut(-y) temu", - "x_months_ago": "{x} miesi(-ące\/-ęcy) temu", - "x_seconds_ago": "{x} sekund(-y) temu", - "x_years_ago": "{x} lat(-a) temu", - "year": "rok", - "years": "lata" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/pt-BR.json b/custom_components/hacs/.translations/pt-BR.json deleted file mode 100644 index e7089af..0000000 --- a/custom_components/hacs/.translations/pt-BR.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "common": { - "about": "Sobre", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon Apps", - "background_task": "Tarefa em segundo plano em execução, esta página será recarregada quando terminar.", - "continue": "Continuar", - "disabled": "Desativado", - "documentation": "Documentação", - "hacs_is_disabled": "HACS está desativado", - "installed": "instalado", - "integration": "Integração", - "integrations": "Integrações", - "manage": "gerenciar", - "netdaemon": "NetDaemon", - "netdaemon_apps": "NetDaemon Apps", - "plugin": "Plugin", - "plugins": "Plugins", - "python_script": "Python Script", - "python_scripts": "Python Scripts", - "repositories": "Repositórios", - "settings": "configurações", - "theme": "Tema", - "themes": "Temas", - "version": "Versão" - }, - "config": { - "abort": { - "single_instance_allowed": "Apenas uma configuração do HACS é permitida." - }, - "error": { - "auth": "Token de acesso pessoal incorreto." - }, - "step": { - "user": { - "data": { - "appdaemon": "Habilitar AppDaemon apps descoberta & rastreamento", - "netdaemon": "Habilitar NetDaemon apps descoberta & rastreamento", - "python_script": "Habilitar python_scripts descoberta & rastreamento", - "sidepanel_icon": "Icone do painel lateral", - "sidepanel_title": "Titulo do painel lateral", - "theme": "Habilitar temas descoberta & rastreamento", - "token": "GitHub Token de acesso pessoal" - }, - "description": "Se você preciar de ajuda com a configuração olhe aqui: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "cancel": "Cancelar", - "continue": "Tem certeza que quer continuar?", - "delete": "Tem certeza de que deseja excluir '{item}'?", - "exist": "{item} já existe", - "home_assistant_is_restarting": "Espere, o Home Assistant está agora a reiniciar.", - "no": "Não", - "ok": "OK", - "restart_home_assistant": "Tem certeza de que deseja reiniciar o Home Assistant?", - "uninstall": "Tem certeza de que deseja desinstalar '{item}'?", - "yes": "Sim" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Habilitar AppDaemon apps descoberta & rastreamento", - "country": "Filtro pelo código do país.", - "experimental": "Ativar recursos experimentais", - "netdaemon": "Habilitar NetDaemon apps descoberta & rastreamento", - "not_in_use": "Não está em uso com o YAML", - "release_limit": "Número de lançamentos a serem exibidos.", - "sidepanel_icon": "Icone do painel lateral", - "sidepanel_title": "Titulo do painel lateral" - } - } - } - }, - "repository_banner": { - "integration_not_loaded": "Esta integração não é carregada no Home Assistant.", - "no_restart_required": "Não é necessário reiniciar", - "not_loaded": "Não carregado", - "restart": "Você precisa reiniciar o Home Assistant.", - "restart_pending": "Reiniciar pendente" - }, - "repository": { - "add_to_lovelace": "Adicionar a Lovelace", - "authors": "Autores", - "available": "Disponível", - "back_to": "Voltar para", - "changelog": "Changelog", - "downloads": "Downloads", - "flag_this": "Sinalizar isso", - "frontend_version": "Versão Frontend", - "github_stars": "Estrelas de GitHub", - "goto_integrations": "Ir para integrações", - "hide": "Esconder", - "hide_beta": "Esconder beta", - "install": "Instalar", - "installed": "Instalado", - "lovelace_copy_example": "Copie este exemplo para seu clipboard", - "lovelace_instruction": "Quando você adicionar isso à sua configuração do lovelace, use este", - "lovelace_no_js_type": "Não foi possível determinar o tipo deste plug-in, verifique o repositório.", - "newest": "O mais novo", - "note_appdaemon": "Você ainda precisa adicioná-lo ao seu arquivo 'apps.yaml'", - "note_installed": "Quando instalado, ele estará localizado em", - "note_integration": "Você ainda precisa adicioná-lo ao seu arquivo 'configuration.yaml'", - "note_plugin": "você ainda precisará adicioná-lo à sua configuração do lovelace ('ui-lovelace.yaml' ou o editor de configuração da interface do usuário)", - "open_issue": "Open issue", - "open_plugin": "Plugin aberto", - "reinstall": "Reinstalar", - "repository": "Repositório", - "restart_home_assistant": "Reiniciar Home Assistant", - "show_beta": "Mostrar beta", - "uninstall": "Desinstalar", - "update_information": "Atualizar informação", - "upgrade": "Melhorar" - }, - "settings": { - "add_custom_repository": "ADICIONAR REPOSITÓRIO PERSONALIZADO", - "adding_new_repo": "Adicionando novo repositório '{repo}'", - "adding_new_repo_category": "Com a categoria '{category}'.", - "category": "Categoria", - "compact_mode": "Modo compacto", - "custom_repositories": "REPOSITÓRIOS PERSONALIZADOS", - "delete": "Deletar", - "display": "Display", - "grid": "Grade", - "hacs_repo": "HACS repo", - "hidden_repositories": "repositórios ocultos", - "missing_category": "Você precisa selecionar uma categoria", - "open_repository": "Repositório aberto", - "reload_data": "Recarregar dados", - "reload_window": "Recarregar janela", - "repository_configuration": "Configuração do Repositório", - "save": "Salvar", - "table": "Tabela", - "table_view": "Vista de mesa", - "unhide": "reexibir", - "upgrade_all": "Atualizar tudo" - }, - "store": { - "ascending": "ascendente", - "clear_new": "Limpar todos os novos repositórios", - "descending": "descendente", - "last_updated": "Última atualização", - "name": "Nome", - "new_repositories": "Novos Repositórios", - "pending_upgrades": "Atualizações pendentes", - "placeholder_search": "Por favor insira um termo de pesquisa...", - "stars": "Estrelas", - "status": "Status" - }, - "time": { - "ago": "atrás", - "day": "dia", - "days": "dias", - "hour": "hora", - "hours": "horas", - "minute": "minuto", - "minutes": "minutos", - "month": "mês", - "months": "meses", - "one": "Um", - "one_day_ago": "um dia atrás", - "one_hour_ago": "uma hora atrás", - "one_minute_ago": "um minuto atrás", - "one_month_ago": "um mês atrás", - "one_second_ago": "um segundo atrás", - "one_year_ago": "um ano atrás", - "second": "segundo", - "seconds": "segundos", - "x_days_ago": "{x} dias atrás", - "x_hours_ago": "{x} horas atrás", - "x_minutes_ago": "{x} minutos atrás", - "x_months_ago": "{x} meses atrás", - "x_seconds_ago": "{x} segundos atrás", - "x_years_ago": "{x} anos atrás", - "year": "ano", - "years": "anos" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/ru.json b/custom_components/hacs/.translations/ru.json deleted file mode 100644 index 7cde0bf..0000000 --- a/custom_components/hacs/.translations/ru.json +++ /dev/null @@ -1,193 +0,0 @@ -{ - "common": { - "about": "О проекте", - "appdaemon": "AppDaemon", - "appdaemon_apps": "Приложения AppDaemon", - "background_task": "Выполняется фоновая задача, страница перезагрузится по готовности.", - "check_log_file": "Проверьте логи для получения более подробной информации.", - "continue": "Продолжить", - "disabled": "Отключено", - "documentation": "Документация", - "hacs_is_disabled": "HACS отключен", - "installed": "установлено", - "integration": "Интеграция", - "integrations": "Интеграции", - "manage": "управлять", - "netdaemon": "NetDaemon", - "netdaemon_apps": "Приложения NetDaemon", - "plugin": "Плагин", - "plugins": "Плагины", - "python_script": "Скрипт Python", - "python_scripts": "Скрипты Python", - "repositories": "Репозитории", - "settings": "настройки", - "theme": "Тема", - "themes": "Темы", - "version": "Версия" - }, - "config": { - "abort": { - "single_instance_allowed": "Разрешена только одна настройка HACS." - }, - "error": { - "auth": "Неверный токен персонального доступа." - }, - "step": { - "user": { - "data": { - "appdaemon": "Включить поиск и отслеживание приложений AppDaemon", - "netdaemon": "Включить поиск и отслеживание приложений NetDaemon", - "python_script": "Включить поиск и отслеживание скриптов Python", - "sidepanel_icon": "Иконка в боковом меню", - "sidepanel_title": "Название в боковом меню", - "theme": "Включить поиск и отслеживание тем", - "token": "Персональный токен доступа GitHub" - }, - "description": "Если вам нужна помощь с настройкой, посмотрите: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "Вы уверены, что хотите добавить это в ресурсы Lovelace?", - "bg_task": "Действие отключено во время выполнения фоновых задач.", - "cancel": "Отмена", - "continue": "Вы уверены, что хотите продолжить?", - "delete": "Вы уверены, что хотите удалить '{item}'?", - "delete_installed": "'{item}' установлен, вам нужно нажать 'Удалить', чтобы удалить его.", - "exist": "{item} уже существует.", - "generic": "Вы уверены?", - "no": "Нет", - "no_upgrades": "Нет обновлений", - "ok": "ОК", - "overwrite": "После подтверждения файлы будут перезаписаны.", - "reload_data": "Выполняется перезагрузка данных всех репозиториев в HACS, это займет некоторое время.", - "restart_home_assistant": "Вы уверены, что хотите перезапустить Home Assistant?", - "uninstall": "Вы уверены, что хотите удалить '{item}'?", - "yes": "Да" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "Включить поиск и отслеживание приложений AppDaemon", - "country": "Фильтр по стране.", - "debug": "Включить отладку.", - "experimental": "Вкл. экспериментальные функции", - "netdaemon": "Включить поиск и отслеживание приложений NetDaemon", - "not_in_use": "Не используется с YAML", - "release_limit": "Число доступных версий.", - "sidepanel_icon": "Иконка в боковом меню", - "sidepanel_title": "Название в боковом меню" - } - } - } - }, - "repository_banner": { - "integration_not_loaded": "Эта интеграция не загружена в Home Assistant.", - "no_restart_required": "Перезагрузка не требуется", - "not_loaded": "Не загружено", - "plugin_not_loaded": "Этот плагин не добавлен к ресурсам Lovelace.", - "restart": "Вам нужно перезапустить Home Assistant.", - "restart_pending": "Ожидается перезапуск" - }, - "repository": { - "add_to_lovelace": "Добавить в Lovelace", - "authors": "Авторы", - "available": "Доступно", - "back_to": "Назад к", - "changelog": "Изменения", - "downloads": "Загрузки", - "flag_this": "Пожаловаться", - "frontend_version": "Версия", - "github_stars": "Звезды GitHub", - "goto_integrations": "Перейти к интеграции", - "hide": "Скрыть", - "hide_beta": "Скрыть бета", - "install": "Установить", - "installed": "Установлено", - "lovelace_copy_example": "Скопируйте пример в буфер обмена", - "lovelace_instruction": "Для добавления в конфигурацию Lovelace, используйте", - "lovelace_no_js_type": "Не удалось определить тип этого плагина, проверьте репозиторий.", - "newest": "новейшая", - "note_appdaemon": "вам всё ещё нужно добавить код для настройки приложения в файл 'apps.yaml'", - "note_installed": "После установки, файлы будут расположены в", - "note_integration": "вам всё ещё нужно добавить код для настройки интеграции в файл 'configuration.yaml'", - "note_plugin": "вам всё ещё нужно добавить код для настройки плагина в конфигурацию Lovelace ('ui-lovelace.yaml')", - "open_issue": "Сообщить о проблеме", - "open_plugin": "Открыть плагин", - "reinstall": "Переустановить", - "repository": "Репозиторий", - "restart_home_assistant": "Перезагрузка Home Assistant", - "show_beta": "Показать бета", - "uninstall": "Удалить", - "update_information": "Обновить информацию", - "upgrade": "Обновить" - }, - "settings": { - "add_custom_repository": "ДОБАВИТЬ СВОЙ РЕПОЗИТОРИЙ", - "adding_new_repo": "Добавление нового репозитория '{repo}'", - "adding_new_repo_category": "С категорией '{category}'.", - "bg_task_custom": "Свои репозитории скрыты во время выполнения фоновых задач.", - "category": "Категория", - "compact_mode": "Компактный режим", - "custom_repositories": "СВОИ РЕПОЗИТОРИИ", - "delete": "Удалить", - "display": "Вид", - "grid": "Сетка", - "hacs_repo": "Репозиторий HACS", - "hidden_repositories": "Скрытые репозитории", - "missing_category": "Вы должны выбрать категорию", - "open_repository": "Открыть репозиторий", - "reload_data": "Перезагрузить", - "reload_window": "Перезагрузить окно", - "repository_configuration": "Конфигурация репозитория", - "save": "Сохранить", - "table": "Таблица", - "table_view": "Таблица", - "unhide": "Показать", - "upgrade_all": "Обновить всё" - }, - "store": { - "ascending": "по возрастанию", - "clear_new": "Очистить все новые репозитории", - "descending": "по убыванию", - "last_updated": "Последнее обновление", - "name": "Название", - "new_repositories": "Новые репозитории", - "pending_upgrades": "Ожидается обновление", - "placeholder_search": "Пожалуйста, введите условие для поиска...", - "sort": "Сортировка", - "stars": "Звезды", - "status": "Статус" - }, - "time": { - "ago": "назад", - "day": "день", - "days": "дней", - "hour": "час", - "hours": "часов", - "minute": "минута", - "minutes": "минут", - "month": "месяц", - "months": "месяца", - "one": "Одна", - "one_day_ago": "один день назад", - "one_hour_ago": "час назад", - "one_minute_ago": "минуту назад", - "one_month_ago": "месяц назад", - "one_second_ago": "одну секунду назад", - "one_year_ago": "один год назад", - "second": "секунда", - "seconds": "секунд", - "x_days_ago": "{x} дней назад", - "x_hours_ago": "{x} часов назад", - "x_minutes_ago": "{x} минут назад", - "x_months_ago": "{x} месяцев назад", - "x_seconds_ago": "{x} секунд назад", - "x_years_ago": "{x} лет назад", - "year": "год", - "years": "лет" - } -} \ No newline at end of file diff --git a/custom_components/hacs/.translations/zh-Hans.json b/custom_components/hacs/.translations/zh-Hans.json deleted file mode 100644 index 73ecd24..0000000 --- a/custom_components/hacs/.translations/zh-Hans.json +++ /dev/null @@ -1,198 +0,0 @@ -{ - "common": { - "about": "关于", - "appdaemon": "AppDaemon", - "appdaemon_apps": "AppDaemon应用", - "background_task": "后台任务正在运行,完成后将重新加载此页面。", - "check_log_file": "请查看日志文件以了解更多信息。", - "continue": "继续", - "disabled": "禁用", - "documentation": "文档", - "hacs_is_disabled": "HACS已禁用", - "installed": "已安装", - "integration": "自定义组件", - "integrations": "自定义组件", - "manage": "管理", - "netdaemon": "NetDaemon应用", - "netdaemon_apps": "NetDaemon应用", - "plugin": "Lovelace插件", - "plugins": "Lovelace插件", - "python_script": "Python脚本", - "python_scripts": "Python脚本", - "repositories": "仓库数量", - "settings": "设置", - "theme": "主题", - "themes": "主题", - "version": "版本" - }, - "config": { - "abort": { - "single_instance_allowed": "仅允许单个HACS配置。" - }, - "error": { - "auth": "个人访问令牌不正确。" - }, - "step": { - "user": { - "data": { - "appdaemon": "启用AppDaemon应用发现和跟踪", - "netdaemon": "启用NetDaemon应用发现和跟踪", - "python_script": "启用python_scripts发现和跟踪", - "sidepanel_icon": "侧面板图标", - "sidepanel_title": "侧面板标题", - "theme": "启用主题发现和跟踪", - "token": "GitHub个人访问令牌" - }, - "description": "如果您需要有关配置的帮助,请在此处查看:https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" - } - }, - "title": "HACS (Home Assistant Community Store)" - }, - "confirm": { - "add_to_lovelace": "您确定要将此添加到Lovelace资源中吗?", - "bg_task": "后台任务正在运行时,操作被禁用。", - "cancel": "取消", - "continue": "你确定你要继续吗?", - "delete": "是否确实要删除\"{item}\"?", - "delete_installed": "已安装 '{item}',需要先将其卸载,然后才能将其删除。", - "exist": "{item}已经存在", - "generic": "你确定吗?", - "home_assistant_is_restarting": "请等待,Home Assistant现在正在重新启动。", - "home_assistant_version_not_correct": "您正在运行Home Assistant的版本为'{haversion}',但是这个需要安装最低版本是'{minversion}'。", - "no": "取消", - "no_upgrades": "暂无升级", - "ok": "确定", - "overwrite": "这样做会覆盖它。", - "reload_data": "这将重新加载HACS知道的所有仓库的数据,这需要一些时间才能完成。", - "restart_home_assistant": "您确定要重新启动Home Assistant吗?", - "uninstall": "您确定要卸载“ {item} ”吗?", - "upgrade_all": "这将升级所有这些仓库,请确保在继续之前已阅读所有仓库的发行说明。", - "yes": "确定" - }, - "options": { - "step": { - "user": { - "data": { - "appdaemon": "启用AppDaemon应用发现和跟踪", - "country": "用国家代码过滤。", - "debug": "启用调试。", - "experimental": "启用实验功能", - "netdaemon": "启用NetDaemon应用发现和跟踪", - "not_in_use": "不适用于 YAML", - "release_limit": "要显示的发行数量。", - "sidepanel_icon": "侧面板图标", - "sidepanel_title": "侧面板标题" - } - } - } - }, - "repository_banner": { - "config_flow": "此组件支持config_flow,这意味着您现在可以转到UI的“集成”部分进行配置。", - "config_flow_title": "支持UI配置", - "integration_not_loaded": "此集成未加载到Home Assistant中。", - "no_restart_required": "无需重启", - "not_loaded": "未加载", - "plugin_not_loaded": "该插件未添加到您的Lovelace资源中。", - "restart": "您需要重新启动Home Assistant。", - "restart_pending": "重新启动待处理" - }, - "repository": { - "add_to_lovelace": "添加到Lovelace", - "authors": "作者", - "available": "版本号", - "back_to": "返回", - "changelog": "更新日志", - "downloads": "下载", - "flag_this": "标记", - "frontend_version": "前端版本", - "github_stars": "GitHub星级", - "goto_integrations": "转到组件", - "hide": "隐藏", - "hide_beta": "隐藏测试版", - "install": "安装", - "installed": "已安装版本", - "lovelace_copy_example": "复制样例代码到你的剪贴板", - "lovelace_instruction": "您仍然需要将下列代码添加到lovelace配置中(“ ui-lovelace.yaml”或原始配置编辑器):", - "lovelace_no_js_type": "无法确定此插件的类型,请检查仓库。", - "newest": "最新", - "note_appdaemon": "您仍然需要将其添加到“ apps.yaml”文件中", - "note_installed": "安装后,它将位于", - "note_integration": "您仍然需要将其添加到“ configuration.yaml”文件中", - "note_plugin": "您仍然需要将其添加到lovelace配置中(“ ui-lovelace.yaml”或原始配置编辑器)ui-lovelace.yaml", - "open_issue": "提交问题", - "open_plugin": "打开插件", - "reinstall": "重新安装", - "repository": "仓库", - "restart_home_assistant": "重新启动Home Assistant", - "show_beta": "显示测试版", - "uninstall": "卸载", - "update_information": "更新信息", - "upgrade": "升级" - }, - "settings": { - "add_custom_repository": "添加自定义仓库", - "adding_new_repo": "添加新的仓库 '{repo}'", - "adding_new_repo_category": "类别为 '{category}'。", - "bg_task_custom": "自定义仓库在后台任务运行时被隐藏。", - "category": "类别", - "compact_mode": "紧凑模式", - "custom_repositories": "自定义仓库", - "delete": "删除", - "display": "视图方式", - "grid": "网格", - "hacs_repo": "HACS仓库", - "hidden_repositories": "隐藏的仓库", - "missing_category": "您需要选择一个类别", - "open_repository": "打开仓库", - "reload_data": "重载数据", - "reload_window": "重新加载窗口", - "repository_configuration": "仓库数据配置", - "save": "保存", - "table": "列表", - "table_view": "表视图", - "unhide": "取消隐藏", - "upgrade_all": "升级全部" - }, - "store": { - "ascending": "升序", - "clear_new": "清除所有新仓库标记", - "descending": "降序", - "last_updated": "最近更新时间", - "name": "名称", - "new_repositories": "新仓库", - "pending_upgrades": "待升级", - "placeholder_search": "搜索项目...", - "sort": "排序", - "stars": "星级", - "status": "状态" - }, - "time": { - "ago": "过去", - "day": "天", - "days": "天", - "hour": "小时", - "hours": "小时", - "minute": "分", - "minutes": "分", - "month": "月", - "months": "月", - "one": "一", - "one_day_ago": "一天前", - "one_hour_ago": "一个小时之前", - "one_minute_ago": "一分钟前", - "one_month_ago": "一个月前", - "one_second_ago": "一秒钟前", - "one_year_ago": "一年前", - "second": "秒", - "seconds": "秒", - "x_days_ago": "{x}天前", - "x_hours_ago": "{x}小时前", - "x_minutes_ago": "{x}分钟前", - "x_months_ago": "{x}个月前", - "x_seconds_ago": "{x}秒前", - "x_years_ago": "{x}年前", - "year": "年", - "years": "年" - } -} \ No newline at end of file diff --git a/custom_components/hacs/__init__.py b/custom_components/hacs/__init__.py index 1b94fbc..5a738a4 100644 --- a/custom_components/hacs/__init__.py +++ b/custom_components/hacs/__init__.py @@ -1,224 +1,30 @@ -""" -Custom element manager for community created elements. - -For more details about this integration, please refer to the documentation at -https://hacs.xyz/ -""" - -import voluptuous as vol -from aiogithubapi import AIOGitHub -from homeassistant import config_entries -from homeassistant.const import EVENT_HOMEASSISTANT_START -from homeassistant.const import __version__ as HAVERSION -from homeassistant.components.lovelace import system_health_info -from homeassistant.exceptions import ConfigEntryNotReady, ServiceNotFound -from homeassistant.helpers.aiohttp_client import async_create_clientsession -from homeassistant.helpers.event import async_call_later - -from custom_components.hacs.configuration_schema import ( - hacs_base_config_schema, - hacs_config_option_schema, -) -from custom_components.hacs.const import DOMAIN, ELEMENT_TYPES, STARTUP, VERSION -from custom_components.hacs.constrains import check_constans, check_requirements -from custom_components.hacs.hacsbase.configuration import Configuration -from custom_components.hacs.hacsbase.data import HacsData -from custom_components.hacs.setup import ( - add_sensor, - load_hacs_repository, - setup_frontend, -) - -from custom_components.hacs.globals import get_hacs - -from custom_components.hacs.helpers.network import internet_connectivity_check - -SCHEMA = hacs_base_config_schema() -SCHEMA[vol.Optional("options")] = hacs_config_option_schema() -CONFIG_SCHEMA = vol.Schema({DOMAIN: SCHEMA}, extra=vol.ALLOW_EXTRA) - - -async def async_setup(hass, config): - """Set up this integration using yaml.""" - hacs = get_hacs() - if DOMAIN not in config: - return True - hass.data[DOMAIN] = config - hacs.hass = hass - hacs.session = async_create_clientsession(hass) - hacs.configuration = Configuration.from_dict( - config[DOMAIN], config[DOMAIN].get("options") - ) - hacs.configuration.config = config - hacs.configuration.config_type = "yaml" - await startup_wrapper_for_yaml() - hass.async_create_task( - hass.config_entries.flow.async_init( - DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={} - ) - ) - return True - - -async def async_setup_entry(hass, config_entry): - """Set up this integration using UI.""" - hacs = get_hacs() - conf = hass.data.get(DOMAIN) - if config_entry.source == config_entries.SOURCE_IMPORT: - if conf is None: - hass.async_create_task( - hass.config_entries.async_remove(config_entry.entry_id) - ) - return False - hacs.hass = hass - hacs.session = async_create_clientsession(hass) - hacs.configuration = Configuration.from_dict( - config_entry.data, config_entry.options - ) - hacs.configuration.config_type = "flow" - hacs.configuration.config_entry = config_entry - config_entry.add_update_listener(reload_hacs) - startup_result = await hacs_startup() - if not startup_result: - hacs.system.disabled = True - raise ConfigEntryNotReady - hacs.system.disabled = False - return startup_result - - -async def startup_wrapper_for_yaml(): - """Startup wrapper for yaml config.""" - hacs = get_hacs() - startup_result = await hacs_startup() - if not startup_result: - hacs.system.disabled = True - hacs.hass.components.frontend.async_remove_panel( - hacs.configuration.sidepanel_title.lower() - .replace(" ", "_") - .replace("-", "_") - ) - hacs.logger.info("Could not setup HACS, trying again in 15 min") - async_call_later(hacs.hass, 900, startup_wrapper_for_yaml()) - return - hacs.system.disabled = False - - -async def hacs_startup(): - """HACS startup tasks.""" - hacs = get_hacs() - if not check_requirements(): - return False - if hacs.configuration.debug: - try: - await hacs.hass.services.async_call( - "logger", "set_level", {"hacs": "debug"} - ) - except ServiceNotFound: - hacs.logger.error( - "Could not set logging level to debug, logger is not enabled" - ) - - lovelace_info = await system_health_info(hacs.hass) - hacs.logger.debug(f"Configuration type: {hacs.configuration.config_type}") - hacs.version = VERSION - hacs.logger.info(STARTUP) - hacs.system.config_path = hacs.hass.config.path() - hacs.system.ha_version = HAVERSION - - hacs.system.lovelace_mode = lovelace_info.get("mode", "yaml") - hacs.system.disabled = False - hacs.github = AIOGitHub( - hacs.configuration.token, async_create_clientsession(hacs.hass) - ) - hacs.data = HacsData() - - # Check HACS Constrains - if not await hacs.hass.async_add_executor_job(check_constans): - if hacs.configuration.config_type == "flow": - if hacs.configuration.config_entry is not None: - await async_remove_entry(hacs.hass, hacs.configuration.config_entry) - return False - - # Set up frontend - await setup_frontend() - - if not await hacs.hass.async_add_executor_job(internet_connectivity_check): - hacs.logger.critical("No network connectivity") - return False - - # Load HACS - if not await load_hacs_repository(): - if hacs.configuration.config_type == "flow": - if hacs.configuration.config_entry is not None: - await async_remove_entry(hacs.hass, hacs.configuration.config_entry) - return False - - # Restore from storefiles - if not await hacs.data.restore(): - hacs_repo = hacs.get_by_name("hacs/integration") - hacs_repo.pending_restart = True - if hacs.configuration.config_type == "flow": - if hacs.configuration.config_entry is not None: - await async_remove_entry(hacs.hass, hacs.configuration.config_entry) - return False - - # Add aditional categories - hacs.common.categories = ELEMENT_TYPES - if hacs.configuration.appdaemon: - hacs.common.categories.append("appdaemon") - if hacs.configuration.netdaemon: - hacs.common.categories.append("netdaemon") - if hacs.configuration.python_script: - hacs.configuration.python_script = False - if hacs.configuration.config_type == "yaml": - hacs.logger.warning( - "Configuration option 'python_script' is deprecated and you should remove it from your configuration, HACS will know if you use 'python_script' in your Home Assistant configuration, this option will be removed in a future release." - ) - if hacs.configuration.theme: - hacs.configuration.theme = False - if hacs.configuration.config_type == "yaml": - hacs.logger.warning( - "Configuration option 'theme' is deprecated and you should remove it from your configuration, HACS will know if you use 'theme' in your Home Assistant configuration, this option will be removed in a future release." - ) - - # Setup startup tasks - if hacs.configuration.config_type == "yaml": - hacs.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_START, hacs.startup_tasks()) - else: - async_call_later(hacs.hass, 5, hacs.startup_tasks()) - - # Show the configuration - hacs.configuration.print() - - # Set up sensor - await hacs.hass.async_add_executor_job(add_sensor) - - # Mischief managed! - return True - - -async def async_remove_entry(hass, config_entry): - """Handle removal of an entry.""" - hacs = get_hacs() - hacs.logger.info("Disabling HACS") - hacs.logger.info("Removing recuring tasks") - for task in hacs.recuring_tasks: - task() - hacs.logger.info("Removing sensor") - try: - await hass.config_entries.async_forward_entry_unload(config_entry, "sensor") - except ValueError: - pass - hacs.logger.info("Removing sidepanel") - try: - hass.components.frontend.async_remove_panel("hacs") - except AttributeError: - pass - hacs.system.disabled = True - hacs.logger.info("HACS is now disabled") - - -async def reload_hacs(hass, config_entry): - """Reload HACS.""" - await async_remove_entry(hass, config_entry) - await async_setup_entry(hass, config_entry) +""" +HACS gives you a powerful UI to handle downloads of all your custom needs. + +For more details about this integration, please refer to the documentation at +https://hacs.xyz/ +""" +import voluptuous as vol + +from .const import DOMAIN +from .helpers.functions.configuration_schema import ( + hacs_config_combined, +) +from .operational.setup import ( + async_setup as hacs_yaml_setup, + async_setup_entry as hacs_ui_setup, +) + +CONFIG_SCHEMA = vol.Schema({DOMAIN: hacs_config_combined()}, extra=vol.ALLOW_EXTRA) + + +async def async_setup(hass, config): + """Set up this integration using yaml.""" + + return await hacs_yaml_setup(hass, config) + + +async def async_setup_entry(hass, config_entry): + """Set up this integration using UI.""" + + return await hacs_ui_setup(hass, config_entry) diff --git a/custom_components/hacs/__pycache__/__init__.cpython-37.pyc b/custom_components/hacs/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index ac153cabfcbf93c7c63601d3ba3918bd6d63dfc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6129 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 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/__pycache__/base.cpython-38.pyc b/custom_components/hacs/__pycache__/base.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d3ca0b86ef4a6efabd26b4091257107d1c9bf161 GIT binary patch literal 3818 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 O;P@q!hFx>kKmISUAs3|p diff --git a/custom_components/hacs/__pycache__/config_flow.cpython-38.pyc b/custom_components/hacs/__pycache__/config_flow.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..608509e84f88b43259a066d3e4862360d57574e2 GIT binary patch literal 4672 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 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/__pycache__/configuration_schema.cpython-37.pyc b/custom_components/hacs/__pycache__/configuration_schema.cpython-37.pyc deleted file mode 100644 index 5f6254616baf160a9c0d214885b2c25dc785746c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1874 zcmcgt&u<$=6yDii{+&39(>N(mg9^xqBqw@66~c8k#KN&H+ZA*rSZ#L3$(Hr5J3EFX zayUS*Nc;ggv`79L?sMhTBUetmH@1~FQm-9rzIpS#-FY+beY1X4D&;k3|Ni~!j}O55 z2PeyBf$=r;^fv&ixf&%@kBF;NgPM^sHeHiw)N(CqyEe_Z8JcyoH0S1M-p$j3TcAa^ zNK0;suDC07)m;rWS{~`F%+^?$u3Z~+oo+n0-1Sf!5n#)-a;?)%dIz`-m~CAXTBY01 zO}9ezfmYwSynEDW4rL)Lydr>gBJFm!*=Re`63I_2 zmaAz<*~E`oMl4&gX zg|sL;nVz~O&LZZg%!|TtC}feHPZ$r!EEaxrSU02%0bs+jFdw~9l>8!X&cu|*fsWgT zhT|IaCiJuhrkHBoSw5-KKmM<^f(pY&aI6D^YVWK-AS+qSiz6N)zT& zONrQ4A~r$9=8KHTz9hYU} zvC|ng+Pn(y@b?gQ0HofExq*fe!gu3+uwdN}(T!!C!#(tGtHLa5GT+XkY+ryoEdoGk zNJZaK#o$;gSW4v_eb_LN30>tJ9}wE_8f2INlJn|}9B4n3uk;z2>Dy3`nf_3Fw*DPh zkSlWeQ8O8zgfXkZr|vE30VUR8*?DjS)CoFLr;+x2%ePNedHqzNQCy!kV2Kp|ao|8U zvGqJD>wFhx^-Y=aJemZaC-ZPOrV}wurm0G)v>-KbH}HrYKbCw0VI84@u!(>w=35B& z5k5xv1mRNvY2sHw)!^n7D)4K&R#j02$vzw>bQ-a*P|0Zxz|^y(Xxv;4u$D8UdaqLa EAGNUIEC2ui diff --git a/custom_components/hacs/__pycache__/const.cpython-37.pyc b/custom_components/hacs/__pycache__/const.cpython-37.pyc deleted file mode 100644 index be9484662e58bb0dca677573604482eaf6fdb22d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3235 zcmeHJX>{XO8MWmli8Gl%TT0ngy3GKI%ak>gQd^c~#geSCWTz2O)sw4<4YB1~^324; zw6t_*D`lr0XxYoYL(9JJE&C#N%D$I<%6gSd!kqGF&VgSXoOAu8_wIM^{XY2|r8qT} zOYn31trwg-Go47>7T;`q4)Nk_BG@M+5=b}+B%LG#M?lI+A?>7*aWcqaY*}!!Ugp{a z#t#b4m?vC6enWzH6TF*14wF33VGE|P71Ov2?s{FunZz;9798uP$UE*}%9+C5a6Img z6L1gQ6ZgWsaUa|l_k)NNaethIlW_`8#c9}v?RWqlhzDT?v&iFgJQzE01|EWk;$e6= z9)U+<4v)f_cr?z!V{kSKC_+LBGRja;fr@!lp+Sd%8cgg&9Tsdf(8L1H!6F;hgFQQA5X#oT!0JlWL$)c z@f2KwOK}-4#}&8|SK+C68lH}4;F-7@&%!l$HlBm$;(2&JUVsWs@XK@Q|#pm#Od;wp?m+)nL1z*M2@O4D^2EK`J z;oJBQzKieS`?w81zz^{w{1`vMPw_MS9KXOX@hkiqzrpSJEq;gJ;}7^F{)9i{5bnTV z@K^i|f5$(rP9i}h34urvX(B^pi7{fFm>_b*B(a5jo@w=W_2!hVqxhVPF?y%PhS4AuI2j2E5y++QSKD*!8ck}TdhJhz2zr#Ct;XSX|nxYX+P{7~HGiNnByxR+BA4|l%NXU_KHLotl`tNu{z zd1QvZIPkj6a3^~Fl)B>n1G&*KH}ju=a!2*3bGfEB2>9sc#L+G5jWKKMa5V9VDd*JP zYdy4EOy26Pit7(#z43qR(C>Raverpr+Z%Xuxuf|3t&9Fw`j1itO>=}oIT8w7OAfCs zUYEGm2$k4LC`6smi3Oq&2?d)g`9+JU5UeQ{iA6#t;YLL#VJrH7k)SiD!E1wch8YP4^$dwC^BVLw$T#R|P}AT{b!Io1rN%mw^Dvpkq@GC) zlRR@V5=_>Z%wy8eq@KxqCiQocQ)g|RT6NadW&WOcZjoChSEH{UKR;?WnRkJ@7V}%| z#bT{RPCecu&x-5lXR$|%bFnyAtH_o54!KIq<1A&a8gp7Do-u>P--^ZFEP6}ywKzwc zS?u{pusIK#dp5OgYTK-{sb@34O>dj^HuKo@w>kR;ISqOhsHM=mL9Yh0H>la5N0WUv zIg2K1o8&f`ae@8G^jPG+L$0%6Y@d$CTo?Y5>pBxgL6=>v#*ydNF0D$fZbxHHxo)X7 zBg&R)dOsbSeQ-EEkqf-TosE){IDEoVZttcDzuqRfiF*BdyK z>jEyBg=k9FWL-9F*IqO^sH|G9)80A$PI?Q4x}h3MG-la_y4^6NZ0w-PHzo!W|504~ zy5lsyoqD@F%NuXXBi@~B?^vD&Azy>h_?cZF!xir=zTp$War`GUo!tBrgk(0AOKug$ gQ$lk6Z+tvGxn=WiWB32-|H1?|pZmXl0f(>t3Al7+vj6}9 diff --git a/custom_components/hacs/__pycache__/const.cpython-38.pyc b/custom_components/hacs/__pycache__/const.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62b3011107e7a8018312f25455a3727a85e4ae57 GIT binary patch literal 3411 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 BMAQHP diff --git a/custom_components/hacs/__pycache__/enums.cpython-38.pyc b/custom_components/hacs/__pycache__/enums.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a404437d843b63027a8c9f8ad98745a7ae860b2 GIT binary patch literal 1398 zcmaJ>&5qkP5SC^A+v+xp21S7$T%bTnQL<@BhHRj1}ci8GQWc5Ilpy ze+EMoq>vF&%Tc8wb)+m5GA7zPg=ln4)!#Lw0iqKF5aWpGfSAMr#5y7dAU1ITagK;d zCW$hi9-b^|+0b^Dvzm8V-SLaR;80~g5?WCARW5^%pAiJlVDRt3Pz8*hrvbVI% zaw?3b>~}>i?3#ARm2R`|Zdr|a-WE+K^zN2cRG2Mn^OlluJ`onibyiV~g@bX$NMA~S zit(>~R^HDb+uLleyx=+t(PR~-!6Nq36$I8gdKs)eKS0K2y@|qEPIQuu6$&gM$xAh-L&Q;!f3h1FR(%kMj{p#0$aCC%E|G5_J*{!|D#*NFEhz2GqZS!DV$l2nNL=XJn4PfPbOM q25D94j(OJ(jWCqYpzN^rvF(NR!+pd5D8EueQZ#isKA)Uh=hi5tK z`~#=t*U(=}MG0jpX7+r>ArdRi>~kL9H?z;{^(xT#_5Jhf-vZzVZ^}V?ct&C#ks3n4 zf(jHaArML^hh|4fRiRlbw+sGGNH6U`(hkWB5^JA=xvx-(7{_7`2q~I*XyjFMz z$lmGvWNROD`LZ7xU*_@+&A~xR)fy%OHTdPU{UWNgKw+Vh%~Aa7OUW<4v8Zwizy9F3unmJtvi`N+!7C zUtoZWdS~gSk|~=znX~SJN=Gxll<&=>0?tvH?ram9F>`fqk(h_1ETwk~N>_MJiLgEq zT4uOSv9M_KN}PO%v@i57*=qg*mv~^F^&Y8vBqhNI+#Ua8*tPToPp&jt(X897H`adi zHI8HpP~#O->dWGQuY%fk&4Sus6sM6c3TR7Krp+1Wds`V=X4nOwM=k84i&e2NG`CF0 ouV^lswlTT2ZRYlFs}Y!BZ5?~9aiUJ+sLj0Fk0DVy-mWMA0JmA@GXMYp diff --git a/custom_components/hacs/__pycache__/http.cpython-37.pyc b/custom_components/hacs/__pycache__/http.cpython-37.pyc deleted file mode 100644 index 6a4a911d14dde5d61b3cc58d8eb2aada9aa03322..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2895 zcmb7G&2JmW6`%d&he(N{6n_+rJ1yD@axKa^#VFh&vizX{u3=bCo$R5DC1*q~z1(GH zmX^3CeQ~Y@dgv{1ApyPg@93qs-uK#5j=koT-y2epoj3)$#13cP%)a-VH}C!C-Dx)K z49|c6{pwFigR%e6;QVpX`2?>%K_ghegeC063M~9Pi9K=x$0C`VxFav{Oy5iVQ6;FD zzMoV_wV*bt2X!;9BrBsv(6HD8CaR+L6%#e-y>@~o#&xlR@rrC>yeb-^`4tP+gnh`i zSBuSqz5PQz$n!CmsTgN*n(NLtz{DPHTc!Un>-VKXZ~Y(}$vv%OokwZ@$5=)(QvMXeF(uV+xs(qPsQ;q%}Xv6sO&E?gw?U7aqll)Wx-3 z8kqj>OC`UYNS#YzCV$)c&AjIT06p|isQ!spucFD>%zkZsic>gXux}Sv5292g(iq{- zWS1|s=??j7o%Ts)LM8P$OSRl~N>|I|sH`j(maFgD{|8e|82a|tyS*$uiu=30iO#c8 z*vm#xAaN81`z{?%XFM$(m}nC`!pZES^|nJgn0y2;Rl_iiMluY`dKiu}F#)2mY=q&L zlPFovRKieXJ&2nGCe@SKtq2YRMICMq)p2_$58V;>Jns1(|1gMvG<3E|Bpcc zLo_E-o=R*7R+d{^V1M$Ej2P4iZGi7d%F&9d;2=t0Pr(4A5+o5W-Bo&eoW!}Jyeccl zkxGG9)iB!j6versh*uzSR=VW3(oYOCem^-VYd*xQX(_XP>ym{Ov3#ev^1kR^mDqhAcy_#}znToa|NsmH~9v?4lz=ZYyQWM(NjGf}9IkQxE zh7cb*>K6VH(7As`jAtLPxeZAL!S7Irr|so@^)pOa>iX&>58NT}0484z$ zoagd0>o1mSEV0&4TQfFu&R`2^Ntl3s?uwO}J6soy+e|b~Z}m2txrfYr)fQ_r2MAkl z%ub)N^p?x=jpudf4JR&hdy@mc~@RP9T-Yf5O_61Ar&tY-6DcWM=&+xI?}b51uxr>Gq*udSU&#ic{3UQlw}y?5uc2mE}dsK>}GWJ+hKi(mZMRdBJU^pRO3 zslT;$i?!Y3L(;x zSr`i!i&0UGqP(mOWF)|=r6-ljR9U-s(vxFizp{G%`L=J^L0yBEpl0?+Cttdzt%8P3 zNuX|G{`;h1cm{HP3k^uau7fn%)~emM8&<;*NXxOUy0vMyot9O#SFKHq=;zv3?6&1w z+*ziF0cKdVCm4(PCv=IQKHRZn!>=4l7aZv=Bf5kR8y4I+lxZw_e5KoIMBtm PY4Z)MWjCBwqt*Tn>;WHt diff --git a/custom_components/hacs/__pycache__/sensor.cpython-37.pyc b/custom_components/hacs/__pycache__/sensor.cpython-37.pyc deleted file mode 100644 index 411e8824838d1fee31c8f05eb303fb69f05d3808..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3197 zcmbVO&2JM&6yHyOZek};C~XQ0{SdBdCjrwWsA{1El^ie)mqtpWrsJ-1Jj@yO=?2GZ)lzbl9&L)b8cF?2Z> z1uu^J{y?m{d=hz%ONc`ep-;J3CO-G#XOvAGKkCQ7DQpS;`S;cJY|pwk=1CknUL20% z2zK%HA!xIn{kk=ph_jhcj)!-dcPuN_0V4V{(YA*>7`s=_AS zl@r|vedc?VOGPrWjO(W>GN5u3YC1!?0=x%fej(CZF!&k}s&rKdFpa2Pofb{Kg`VNyYjc`DmuW`WUQox?&m@#DyLd+|8QLM|<#J6g{tsX!oApP*;^K4n}OksDHBg>E$N zyB-LmOqgLzXdo*5X9s-FL0ZFLCLj^zapXT8Q^zN)2y=!wA)X9QgZ^n#Rx?mCV%~#w zT;`}zQ8js|=g;}^>MaLnr2d_&b7LO=P zChS9mg#OAA^eS5gf`en;lvG2LZx(xU2ut#w%>39jh(Sy!046Pv0x3$*7J)C3vgAv^ zS4dUzW#BEcB>4*P%cLgxD)4o(B6*9ff(OsYV4a4;v}7y@2TTST4w&jhJyaZJ072oX z(^RkSMUbd&;HMPwTN4F`CvF06WposCKZzM+aYJPv!1hzNL0kK<+i^aDkJ3PjR#4M3 zCncgaTQLt&#Dk`Mjawc8NgzNEv;&Ve7DfEEsl@V0bN zeo}xre1BCrRDXq{^>ppAl2q}{5$b16HCz2*L_iZe8PaJb0|l3`WO-1s_=G1kgePb_ z=Azbfy(a?}k0atFE`P$V05nk>(TMobz!{GQ%q3J9Akh^i59AJFHo?jlhH)e+(>GZ0 z3hoHE2|b_@y9{C)nzWv%%uPBuZAmMuE@O$ffu=!sDOv>ruAo_9)w)(!)AKpY=6B4k ziv<9O^f;P%YKzaO$?n=&7TSBeb`slTPGxsVhCZL!DBauG+T3zCJks21-|(94?LKMl z^fte2dfPXyx4oUMjdp*h+4FYV?cPSe*(dF5&9-}EyXkIhU2B4su5XjgcDwBnspaC> zVHW`0HjJ!38tK`bYV%6*PQ5Np^+F$Ga@%n~?{9{I`8E_-T)vs3(9>ED1ha3++&Sr5Fdl51dyfvLB@@egxhO0!r*@V@y}vXRaT(y@1YM z$*R*R$_HzL5Qeotl>6f#kYZRH=ND#@=Q3x__TaiEJIoNxPHhm-Ew3 zDud2(=lbo#+N>PuhH7C!QVqE4dU_@2f&bVH6_?=whkbZ5U%1aUZmUP#n?# z`aW;rVFtQCHYFiXIz5+DhPfq6hcF@Tc_I%m^1zZEUGT)o@IWWe0`gajFpAPw*#YEh UEXf>U8=3_sJgZmCl3B0)0~+)NPyhe` diff --git a/custom_components/hacs/__pycache__/sensor.cpython-38.pyc b/custom_components/hacs/__pycache__/sensor.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73d92a53f18099aed65c3fecc14b92e55d411b7f GIT binary patch literal 4186 zcma)9Uvt~W5yt@_NP?88f3@tyQ4qID&9tIqhmNE)?ZgwiiDqhg#%eMhb}|?UccdVL z0DT84B}RFY9{UM8ey|_?Ir7{uaIbypSIBg#?jArIl*CTJx#a?PxA(icH;Wz&B`_R?bPcMyu35*ShtuG+LwebB)%Ser9wR;C+EE!uujyfcHge9cay^ z^wt53M9iIG=q0_F4;&!Q*AKruX#E=?X-y)n$59d_Z6J11o)&xJRBZkoUbXJoNnM9Uu2Nd63tk{RmAg0XfwZ?M&;FGlKg$ z(BA0jjRTg9hK`uU)#|(<(neqz;HD`{u$U0C?D}!k3;J@!6XVEtJxX1MkOL;V4VMPO zk53pMyFt{8-)MXhmi_7ORyJwNABiL$fQZ2`j$oSD!X0d7tG9+@xt2*42+Eykse<@% zBbv!^8@_>j{viKRW6=VQ72b|;jb1U?fAbw(OEAs>;UQj_p= z21)0u!28f<9)$G;G;tdU)4BxwO{b)5Fq7)kIM>cdw?s|wic+TNM{4PG1(oh(FODCA z%3D4G5t9e`GPEzDiFbe`+NpL-dioJbKt9zIgBsL)iT+kfKYRv?#Ia{*$dt<>Wt;~{IB9JL^;qbPVWVs1b=dL%Ca0rxTm3wxI}UCWRv?^%a&0{urb zZK6+pKczQNMVW?Lh2b4QTyb55>AKQ(T>v)@O^z1kYe29G3rK~Sx=z%$`R4uy3@n;EGj`WxU~F1aX1AzC%MdimtU@cas=if$ zw`ooBHjJ&)1;y8ZU!)Di*MVQ6%ZgtBeub_oei8UJ`U}N3fWJZ46~9Dp(pxVHeTTk# zZg!XHO>l|Z97AO8WqB}~<7IfLl$YU=qvTX`wLbWoOE7XY^|ZDh!KwGcAZ4viQ{`Z{tmCGUAHwqyH;ZI4wuA z(utj69EL&EcOu3pqpd$?YhJf{O&rCekh(bIw+KK*79(@3OeL+tkShqL!KMzAPqkOt zYb}eWpp*BW!13@xXEdas3gN^(CpltybOkLDh@V;CwTh`{ktiE+Z;DM%*9)pCj+p_6 zz_5LV6VYoY;NYJWK;HRAp(`MByofsA^B@7JXhXCo{}?1p;<4(31dy2+I^V}m;$F7w zdcHds1iINICGY`4Qie7vB)wDE&727{8^b7qla<}S1qQR&59E{_lVhE)pcg35G~kQz zNc--iAM{iG*yI~POUD*`Egx5&Tkx(O8?UOT`fJF4FE$bz-_OvqYZFhpq8d;|o#W}3 z7VcyuWCIgzpT{HEV&aMC9KBdJhAg7sfUuuF_b8Jlcz}nQIbr=6vWx{uZ-4~4D7JNGw1PoM14d+m1H zr*jW1e-Ci}30merrhbvG6&z@qTz=NxHuAmUsl4frKSZ6+xo6viJNb-=;DBwV8 z^C6%Ifw#fzCQ08ffKbQd5{f2K&TwsrD5(j5DS%iifKZ+DQ50fkQ4s-DXO|$PRrJ8m zkjzzwG;!j01t>**VBgO|Qq%?*GeIN+<_V}U22f12(&hYUBr9Fga04!h^#RW(F+$3#kEWC3G%XhXNYP-mxlU7=l;gSH zbboyqt8$QLn8e1|L`?ks$GosAGYMdh0L+yZSJmr`Q7~ZPVfqmlz}gy<92f;M!7Br+ zlwjxrXTxlF(j)cQbz_COtt)YK!Wgk0Gh&Qs_8b>bp9$aH2#9_Ge z7>5w{^V}Swu!y%{hv-JMoVIKX*xly2wKxkvUHIao$#9B%@zIuiob*8mq>nv zq1ti3yH zX2v(Q=@(<8Zr|(cg-GY>E-yjs6D64ch#Z)9=t0jo`gWW7>xI7Tus7cyH6q zQ>VMNcy!9tY?_?S_)z5)m&sWrlB0vCr@hZ%gHD&|`t!qIoF2b>0%C*aa#+1!Vh)}7 zC>zSd4~J}u6T7a!qv_zSci%ZUM&oeARC*3JAh%H#Y+A`&Rc7|KV53|rChn<+_(lAA z@F0Yrz#~6}Mmbl`r88eFoohnL4Y?#sfNc_rC(1?8uH0jXxA91Ms88?%P#H z4cf^H98t}Ciw>f;pL4}VB13Rbbn9Mk5&x1;cy+-OCPXFPf3}Fs1j^2`S)uyglh=Q+ z_}yXbG; zg+N*j#%!pxk1A2eMe34CG}iI3;%E72W)H7xW}^^%naWJG=%A=ZBPMh>6uHWWS<&^h zj~M9Y-4}F&t%LJ#j`$jU;frtY+m-i+GpVXF9aiPE;*2Y~ZxEE23%%)F@8HQz@ZJm0 z7?zOVfyQwI*CkC7l8$LZknGJ! zKZYz%AkEo_N@NL~T`-yC+=gC$mN1?Tz-dNRl`J^`M}Q-_>gjkoSLYQ^C15>OIy{45 zQH)a^3uaJ)P%4=`0XL!0ZP-8ocSxxNi??pxA&t7S1TQy-5R;=y3RhgU!5B1wtZyYf#G6n2iiW-cFZ{TojhcimkaW&BG)w&Sz2KGmG7$#k0f~KgR zrv_ae-7}r*=JTwWu|uSr2b|KxZ`iXufiKIiKx6YHA_4KqOMU=3vgNgj!0}tBAgdsc zd;*W5dQ%Ubl3Y5adx_+UHPI`5q+L~mGT<$}aWHnS>q{sK2)YVE(@2}QdSdm|r#1Ko zI67_u9oIkhX!uwE($AgC+R|UvXyZ-C%xZx}L~#Wmf75Qz7K}$U{&N6(KwU8IKz{v0 zZC=?q9ok;a2I-M=4VBg8sf)H(1jo*tQdb;8?;9cNetgy*Cl@eA!<^T)z zK1Y)1x;i=nGJCepv>oidSIkB^f1b08x7jEgVg-(DB3#Hco92rcDeUoj&1Z`p!)a%E z!Q}p0`*`8?#GXtQ;_!=4S$W{k9T7ih?fdLxlw17G!euP69a5kra zz*bj~7l5j6K08cTR~_*R80n2w@k+&v^PG>wH&Od_n=`j_2eZ6TY0fQ3Hh(wp%oa2Y zLidJ(`#QUvOQeRG=ogC-fel7_7bu8zyx%%GNh(odB9{;RD-OL z#<~3mKWul`%Q>>7xmekY$M+vtb#_wcWHz(JHB>97>=d817nDM2X5S zl~#s)ih<_POYb?@M?XwofY+Y#3M~+HW+^3--S$)x91eGPW@oW5u_9LCT@q_z9o}WZTBLUE_gCA7~@2qhEpf}BucXVz1@=c;?c zF&YPrPv}!81c|oWQxA}q+f+7qRMYrvj6dLyerVrswS(oaAG8~f_rl%%_JfDbcFoP5 zR%7?ze(-5L(plOMSEYlT>#@#cKj(l+HyzYi zz`vE9C>{q+h_)9*t%A{?x9f2_IP7-nSN5snvbx4f_``ATb!28NSHMhHK@eJ@6~@^H z^_a)hI*crUiHW`i^A3E>fqCmPn3uq^o5}$T1J(u2R~isYJJP+ciW1i>wFJ|_ohwlr zJp6PhRfKs*i9cn9+CYETP+;PPSOYCz!px?tK7s-5fhdwIGj$#G|1&M@rde8*?~$nA zMT4j#)ikHvGwkh01NA;GeGkQ@h)Wo;S@s#sF2=hFBQZihyHH2s8BU|OiD}?dEQhs8 zz9cVbj{`sL=7QO*6EWDtm-0BACR~)~yucKp(n^5%$ z$c21xLu4a9`VokXyoA)XUz}#p))d;BLR+(!WWqu#vk{&%XTr{5<`;4Z?Jk^CuNX(z zPOOP_#?Q&HDF^__o3q|tJ17!5m>oQ0lg2_k7$;I38L4l9F!O;`ya6v+=n;`DNqmgTzF}nWd_$E^3KX9(O2C~5amySA;3G|fq z<0R5rYJciVVdN$tQz1BRhC?|Jxiw6OHM^L=B|&9sU=HJ;a@b8Wsfv@isHmy!yMCN5 z{~Xgm_7UpgVM95s(lX<3I9JI1h0erf2&-W_HgR#_KxtUh6hLjk;2eQWTbVsKk#IA7 zim;|X4C4&F0;z6;tS#kk7>YCwLsfzA+{R;AA*~9MVzlPeP}qtFRTHYQ{>RO|?S3lY due5`$sw=;fo&pebJA|Xlt_*S*7`r z3>Lf;Qk(D`dXf>Xx1L9{>7+~*L}BS+ojj*)@>=wAcKBp}lDwK6MhCNLijdG;7#$ej zV3>#CEUAd4f>!hs`RrInI3L{;r*cl&895+=JtHTqVuBs}`awn_QZaz#ZuVX%E&{ud zftHqMr4)hSmIrgKiohV00PtdRrOQCb`$p=F=h@~@VYu-YJT)O8@5=TP(~+wgAkrQ> z1YGyIS(j;&!&xJ@b{E{3njOn^?KMZ#zLw2Id?9)pHvGCfPE|S2j>hQ<5=D}#VyQ}5 zS~JGu#!Yp&T-P@*Iwy!ZX);~tVH#bXTXaZ$>QX&~Scq!b4FVN zLEDzJNBgprnp+u&b;*k?Z8@{*5@UwHlR;(!2qL@lESF|&tSl}V(C^xK diff --git a/custom_components/hacs/__pycache__/ws_api_handlers.cpython-37.pyc b/custom_components/hacs/__pycache__/ws_api_handlers.cpython-37.pyc deleted file mode 100644 index d994e9aa00c18ad3f7b006497b8b02526b615970..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8698 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 diff --git a/custom_components/hacs/api/__init__.py b/custom_components/hacs/api/__init__.py new file mode 100644 index 0000000..f8017b3 --- /dev/null +++ b/custom_components/hacs/api/__init__.py @@ -0,0 +1 @@ +"""Initialize HACS API""" diff --git a/custom_components/hacs/api/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/api/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3841d57b969e7e34e6ed3046de53eae4cf82cdf0 GIT binary patch literal 168 zcmWIL<>g`k0{@<~@lrtgF^Gc<7=auIATH(r5-AK(3@MDk44O<;!k&4VC7FpinN_I@ z9*)kz3XTDuewvK8*yH0<@{{A^S27ea0d;_hUwZn<`FUxX>H5i~#U=T<@yYqQ1^IcY rc_qdA8Hvfo`iTXZ`tk9ZKojHR^$IF)aoFVMr8nu5XVW1mKDWz>=XqOAV58}@M6C}P_#e`Bui5CV-ZjYj7Zx`BvBzL2M(0U z9g9ALGxj+;_XW6i>ML|m*V2~G(?7^O}?@k(g~F5EatGxY+m)@ZVk`hlNr2AdcO@3bYnWm~w*4sTpz z?t-_)o7Y&-({4;a@%Y9kw7?4-^f-Bk2L9#f<(tF)0n2zIRDUd0pG9YxyhsF}h%i#I zi6fSTO5{?Nz4ZgRkfpN71`1v<6$baW)q=E zC3Te0MPhEnfotiMX>I*TW|@dgEHmq*dQzuS&FT|cq56u{794)P9zozZo{XXbkf&iJ z(_CgEGkR15hVhKnYWmkyhxyz-`Aa_J5j0@R-o~m2%3#|z!;s4;4DGY2 zOa;^*Yr`@#oQfnDN)J~xhBKDLoYg%wZ?0_b;T?%))}P6w$W0*&Z5{CK&fkB&?{md* z_@Mt`)&E@u<07kJ>S6sd*M`zry-}D0fYZYE?%y^fspf3Gx8l5rl>qNJugYhMb+u7( nHmM2JqgXRtpklrKRPrJb&oP|zHW&)TfE?+%6jQwOn>c>}&n4S1{MyH5sZ50}Ycj2}7W{#$zPq?8LT=Bp_m*7iXH(TrBo?t1;++kHP61PIp2m+#RnLFl%5++RLCUc%N17>;I$ zV@@)h6M`_xof*ZQ$DMf)ak5_S&-~U$v#mUs1sDnc{E_fiec`PJ++Abt!EMNUYb@yb zc1pnG^R3V52H)V>4kxE*99)I3-W-L8altd8!ZWEtmI!tcWs=2NRK_Ox0eH~y7~8=o z@j**=AxspPDL6*4UKXrPio~T(wXxJJOL4WG#7s}SLQM%v8_S%g(q_4Eda%F2*2iFK zvI2r@eC=?;ofKcw+TrxVS5G)SMF!W-4L(Ag9wN2R-I{W8fNH!V##zytaPKjy=^7}4 zB#=MuT|Mncg>zxTT<|mw%Pd|3NhTMq&2wNqQ4JF7ic5h1`uLqCDIdGmfv;49*Y>q2 zc$5pR6-Q5ijP&Q1l&%_27jDc sPaBmM^HzdZ?R#b$WU>B1E_s!S7Z_f87YxO_*vEa+cPOTK?-y}?19hGhH~;_u literal 0 HcmV?d00001 diff --git a/custom_components/hacs/api/__pycache__/get_critical_repositories.cpython-38.pyc b/custom_components/hacs/api/__pycache__/get_critical_repositories.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8d31941e9027af4f0987c3cc3775d14dbd779801 GIT binary patch literal 781 zcmZWnF^|(Q6t*3=&6T#|kdW@SPDoi&i5UU6-7y>p#L~qC+ieYT9Bk)Mt1^|5AJLBd z4rcyP22cBfyg}K?D!X`f zHNGq=QA#_W89Sa!&$v}y@uFl_*2XDstdj0Gd>UP6I1E3__s;N-aImNq6do3CS#egH zLa>=NnmKqw2I`l!T%9fo?k0y_c@jJQ3ko@^`vJo}2JmPNN;c#Q337?PB1a2F{w@Nd_c4XXj$oeM3q8h!;f#g;jVYVj zH>PnRfq6Jv7%hRba=xg1en)1nF#nHa9!cg4S=Q3JeAZO0OqaK}xp!)`ziBWBggF^b v?|ud7A3YA~Yx${BR*IHkYs>)*fptiqXd{|ZKE<$d(K?D#JRpOF;s^LIJ4oh$ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/api/__pycache__/hacs_config.cpython-38.pyc b/custom_components/hacs/api/__pycache__/hacs_config.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ff8083dd9e1198ec20654e7702f4ca9598d2f0e1 GIT binary patch literal 1097 zcmZXTy>iqr5XU83{y3j~`KVwBa1}GxT!ucwKnE0ofuV8ZaV*90VM|7m1DCl}q2w8u zOUZLk^8#wAcm*m}J|~wMbmP6>?r7!zuGSxtBto#>eEXF>CJ6nu&fVpM^Bf-a8Vp4# zqL?~0P96AnYqudO!8S%}uklkKBSsbhBkPbk>k;*?G4(49HlX2GbcL^Q8d2vxnvE_V zymguO@Tl)#G`d2U?#f-@OH!drZ$%c)%A@!Q0l7dB(ON zz}~@fLY()uX`owklblMyHRCjE1Z8IT)-6QS=7lypf}e<7(u$WE z6`Yv>@>L~xx_7&meQ8;t8BMq8vf~_5j7!Cvy$2lODvRy#yIp6WT4|jRdnhsLF1%n4O!*AVrRBnjKI% zwkEa*_)um8hbPvFRo@4uR9-Ua*w92W)zvy{Ho=*1JQx_IK(yc-iUwuIZB%JUKSyw$ z9XVU7?W+EPRYjJW=v>rYt2@yt;{uy|XQE-CzfwBqdcKvhDfi$%d2=go zuFi7FHn%d#)shz(^aTn~%hqR>hVkkn_f=MK1a`efxY(BSIDWeij4yu*!lU`o%v>FKMw{WBl+;-cl^|0?2irZi%-EzI{2QH zWD_R2baFm%$hx^(h>7496S-IT6Q45>mpu?|08YCny)~D9#!2^N@SUyricdo6d}71B z#mTFW@1nO!DRZc!3spz6B*nOfLS2LWOV!xvkek6bI7gL!1sx|^1vxbR(8-%a@{c)@;o=y9$@Q zb+u@{CG~pDmhRfIc*ovdJbnW@%1Yv0Bsx=Nl$@(ZN1L)_cf3Z%$>izKuPqnjCshTd zG(D~2OH`%lBZYARh{+Uc+5t2BHDHtLEjaXy7tazz6Q-&xA+-gKTj1362+&Y!BW%T= ztbR!EZ=Z|_zR0HIv_Y+kI8{ZZO1g&QZa6f_&e@J}RLxB{wk;4)7PN{9!9#w)>qGK~ z4@@tPrAp)2gx4x>D&43CjZ40HG*bl-N7_zO>d~&`T}vnbQ5jh(Bb+5Q+)ZXb!Fiby z1thWv=5V)q%2v=%F~J#JHCYX^JPaP0P6Fe`JY;t(PDZ;*Ofd{?EF@8PD3D zR;|)gIdb3waA=Qw4$j<>ubjB>0XXrVCv8(oB>(*UY>(d`Kj*tnr|H1&tDk-k{|4z# z94!6<7<>#(eF>n>$e}Lv5_jZ5Ur8#dKk{AF`AIbmMuF=vKdv$Vyw1Gy2CZJWG~h0< zM(aO1XYQFhYEti8XV5s={^aW~`k#}GCQSCnLiVR5QlVtIP+SYinfeQkbY2Wx(>Y=X zN<=T24oS{IQJ;W_>y59Eb9T~0d4F+lRKd;jUmf}<(0+rap1O{soipdOGONVysXuW} zt26((^RxTCa~jNo*o$$-kE<^Oxd!7Pu32pz*sxL)*s@Z^vTfM`cCEBzxop|9T(MjQ zuElq(eAn`x<+|mDzGC@=*EmG5^Y)pzo zhX;~m(bTjkQ<3C39&G|DQo=LSpyDV?1fj|_#vBgLNXkrS%%##-CpN}Kl9V(4lv8F_ zk>P*7-W~?_)Pbg#? zhz_EQSVAl#dWapw3St$phPZ>ci$D+LI^g=~t1Mk6PHy6SOF*E*GH>!eZregNT*pi^ zNWE{fRJ9qC4{)k|=?o@I_HoNL>brSykZ?6E^P=QKR4!w7CgcQ40HTXG{1K`*FTe3W z>FVB`laKL)UEFmQ##<>f0m<>g;N8e*m@=ivgh>x~btO|+I&GX&zpT#vvM%w$;EyV5_m05{TueG;F3{mj}7MnJ_Q?1{liqyB4HoFAD5%>{`=rv+iO>Rcg6h|NONKL literal 0 HcmV?d00001 diff --git a/custom_components/hacs/api/__pycache__/hacs_repository.cpython-38.pyc b/custom_components/hacs/api/__pycache__/hacs_repository.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..84390f40d40cd4e1503f2af4b01f26afece3b379 GIT binary patch literal 2756 zcmaJ@&5zs073Yu?Nl_o(mDd}u*|nPRBR!-2M8mq39aMKh)y%qoi-)6<=eZM&F=>nkku>ra_E})wfB_Y4jaPtkj)& zeNXf3v^nwnzNQ;#YtrtwHSMIGNw?o62J^?~n7>$K&SIUq$Ao&IdbD{=m_0tPX}_iu z3(^*CKQtbY2c*A2&3nd9XLj}0ukQr6ql~7E2g95P`%x@H&ZfCYiku()3r9BI-y!nC ztvh#rkrcPf0W9A8JZ94($ukH#hioA7_<$8*G)=&0j#%-_d^BPl3lED>`IL5XG-b1M zioUV(FP$h>j-s0R9E9cmtBccwo$}f?%{0W8R=-?o{T&q~iF?`c!nhQ9XDnq#$S9~&nYRa1Od8RO2POMQgj zO~|oH4A_4O`!?+Vc47}+3SQ^Hd`$S-3HSLEV0~`o9X9HIU2XG00GKd-ld-g&L}-6>DK zV?;4~2-${~{heN*n-7(T^W%+o0nK?M8$$X5q$Yo(GU>%F?SFhhbC65D+a~VvOcrkb z_!ej5o%N9G7wDGG^`$4k2XbtU8;gspy8}o5M@II@Hj1qSAG<#y#=J$0nOC)xf{xQ7$>fAbcJ3mUaI^Dq{dR^K04?I+UfXV6ww8M}mcL%0x_Xu#?9G;o!Z8SNs&i2toREdrm4cMyEvx)J&GM zxn)r6_trP&XEp+2OFN4uOxlB-9!al=Mxk2oSbQChcn^cLz(o`DqoUx+pv3k&rZjh7 zmE`k38vG(up1yt~&a+`Mx)GP6$R}Z(Po{asvO?U@fk98y#HpUTJ3W&AUYdwPDc}4R z+z8d;9LM~RNAUY$_Oh})ZL*iUiyDK}{66mc+olyPLT@tg_NB4rdrv}$&-RnUj z0t7C&AEgPMU3(`lQ>q!IV8N>A28T(pAKZC2n3=&(o|2uJuTRf7&$)CbOo(X2q?yp2 zZQj6Hxs5?uz*1lJ`*3g+CP+VJgK{KoU?h^wVVR~nrgFW8&8Z7X2dF4YA=|ZxhDkQe zrKLf$gbbsy6N#fNhOEYrHGqX*^M(m$TzwsQM-BM!8q(ox0x(N&2^k^Xd@yElA!L`b zVN|9#DUY&vU$zBM6&H+#$gynH7j4z94|!5BuD~U`w@y<3o(@Ix*{4r--UcXFOG^}7 zHq@<{a-Pv}88xmxXR^zgP}?t=J8cw1BybzRC&NKh4y}nm@KoTXlj<)h-&S{11=)91 zvJhllnf=aTBu>J@D8Hh%XtI;fvW8|oY$=KU>Yj>z3rz6{gW)^GAvUp1pZH`AvyJ(Z z>49!zRKG6do1oj!QT!feMK9Mi7h{*KY1+r=IL=eA>60Gu%{J+p8&=O;gLRK=;N2#h z@Thj&G}Zq6Eu3WMf^@@>=5ZLx>-+fxB`;7SQC95kvvdlW@79$TYA8oKV(wBT*U!q# z_`dY-=V>`D${e_{K+ATN3Dg`>X`cLEY8=x1v-P4o6GYF*}uK q)r`1KwzoIQX_>N{s#6LDAR949{*W=emddtgn8X7g?+@mGP4|B~>=5Jt literal 0 HcmV?d00001 diff --git a/custom_components/hacs/api/__pycache__/hacs_repository_data.cpython-38.pyc b/custom_components/hacs/api/__pycache__/hacs_repository_data.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a45178a5ea6352b181a66d60fa45bd49fe61b0c GIT binary patch literal 2845 zcma(TOOG5!xvILSr@QB|JNxpk*CCx?vl%JungbF<5yF6FTZ(LCgB<44>7A;b?b&_| zRn=basD}fC5C;_Hh(sdIaN{o^ZrqUg19iv+4vCZl=Lig6RgXPmI|+2FtH1hQ^}V}) z+G_a-u6w`zTX?>P(BJiD^=iWA`|yar!a`9GQA|mKd)T~5Y7vA{YWHfmqBy-eP^`pF zy`E=qcH*avUc?K?2c5fRawl+n* z9o86LVvX4@tIzhRKgYBI&}G`3V^$mP8@ROsw<|cdfpd+n{|Zg<6!)%B@&I)^*pOoq`Y)`;7nXO+VnF!@eC7%&dhefC)Ls4rI!I^lU57CZs+CTD|KNXA#_ zfObJesO?j>ycn_ZmWGd(wjOC)l>>UgDCI8+%GQ*=rQKLuI-@%0e}PB*gCHc)6iuv> z%pj+8Jh2T#0IC_t0>~+CYM;6MI<+65vPNrXV0U<2}Tdmw?Xs7{e}B3 z`Wec|yC}P|l%;-Y{~poCM;2{PUnUKR_OBt@ zubhDbiuVlcT6lVl`ced_oLcNGH)*P2-@HO3hj`kvZ3n$ z`2M7AEE}eFp5}ZHa@?5W$~&m9?b)l#Sd@4c$k%4CmxNy4Lpo1dR%fcaKmJD5>jAV< zkTM!a!6=C?p!fUvBeSL{41+`7fWF|mLAdUeyUL0v9s8!?80R_fwE?lxo*V;P*D@l? zV^2qes;5kdXu!tJ`_E=da4_CE2-st2aS_B>AWvB!VA!z3E~(Ch8H~njFSPf#bHB*4 zI2#1lgc()WM9&j25`>H-QwoY)nLH&*-ys%4<7{8ln4&4 ziGv^(L6*zlQIy1V{MrxmBB2IR2o@}-NN^s@)8NjJf-wo+_#Ah+wo;LpcE8SD4O$RX z?r6yA=V=ON*y1c6DTlKZqN^HuejQ(gS(Gwf*L<}kKZkJI8up$|OV#>8B-nCm9T!6R zk+{hE;YrMyvV`QYTnObtq{BEn$(0)#yIq}9j7RMo?G?%ld|^S-K4c3cchE|qsu zB#B{GwJLN?!4lS&jD|8As7^70amiMatGq=x8C9*yjs-&1Of_&Aph&fENXg^lLb98> zL{y_@v#fHO`5n3Fu_}}Yr-4uCvDro zgb=)roqyX;cD-unKe3K~kNU zX{7&1`H%9X7|9}s(zF0p%_z+PzM6jxd-RBQmM!HJ>w_eJ?q0Ye4s00jU;_%rp z5mlqSoVn-aPKwObjyO!r{|2vV&%gOUPFqT)vFKNA{PzDX^94UFRfk{s5*6Y!;>?T< zepQ>-u6hMJ0;*9k)mi>f=~)6ZS-JPw&kJaOfbN<9t|(CsOahpQCfTOGSC;Q;z3$s- rPK$(nPtRv@9Tpo(YQq#`6N=N^R+BiE4JGFyf?dr~J^v#AA?|+xecC_; literal 0 HcmV?d00001 diff --git a/custom_components/hacs/api/__pycache__/hacs_settings.cpython-38.pyc b/custom_components/hacs/api/__pycache__/hacs_settings.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a34546900333ed0f240d97f76e5cc35b58b72dd7 GIT binary patch literal 1607 zcmZux-EP}96ecBEmSx$AlMGnV4iNSi1Q;A(yV=E1Y}ij)plFJ=+W-NBK#R03N1|Mk zN)W@jNrqkZ83y}u53$|sdN07+UF{VH?B|ej({`BvQitCm4-dcZkYD$DA%gGt`ETjZ zK0?2n!KE%^;Bkv~86<(lyrNTvNoeg>(JiAS z!ia@eDCx0a)@Q+Li}|Z<>aQ_vgEgSR8Z&RUW9^Q$cXJoKA?<#TuJ9F3_Nem&9Yl+_ zK6`XOzDGDMn25)fh^Hizsbos!d?NpVRMEo&tb1o{B&+O%sg%rf06G(<9#oSF6EFy1 zkeY?sQ|EKG=o$O)MG)#w%xb`I+xdv_MBw%6!BqJw*&`Vy}OYG<_zC*8S)%$zlbSOZXI zckzCsN(|h_C8Ifs=LIHFKb~j*Fm2-%gDp9kqlD+?+V^T<#bh3gGk#cq> z+9r$JpPMmk5CasC3o?nvqAG1li`%cK80RwPm5NVEk<&%jR!mm$aXxvdotz%*2@gW* zcKYD>=;+JGx<%Qjp6FgPrxwJPR1e~F#W+pNiZanLOSZ4E%^7qg*u0XtvZbdzN)*v9 zq^N^UMV@OHXx5!^T@)!NC9uJSsDyS$wbUVz=R8ZtxnR26*k>YFOap;tXC@_qi{O~e z`+~{3P%pp(Uwcy`AqM2lS#uBEQcetA_8(lb!1Dv?}Vksw*JW1zW%Q zdgPbqY>2#h{-HVlbq3>_TQ=peu+NY%FN(f%gSBRroD#uoMG3=>?l&@mSU|31dhqgn zgG9{5(!pc)tOm)!I-Z+iBZc;*GUctijLTZEv;bNxWmdaax_8Vb!6#6Zb;0h!lZK>G b*TXy52ogc_T@QOsfE*kE4(%`JAE*5<{Ql?v literal 0 HcmV?d00001 diff --git a/custom_components/hacs/api/__pycache__/hacs_status.cpython-38.pyc b/custom_components/hacs/api/__pycache__/hacs_status.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6fe6e8e86ca8e442c576f2dc8e52752e4bef1055 GIT binary patch literal 924 zcmZWnJ8u**5Vm){k9#Ew1xkS8Dx_=fM2{lqfFd9s%{7*@cJ}t-U2psdy694clAl0v zDfu1L`~kOA`~yVAcrTDhc;#G9+m^R&W%8P7kVD}06LeIC6-litO{ z=Wkw)U$BNtp~ojmj~A>k)L3Sn`3cF;(FFU^M{#UaaVjijZ3RSLf*B$`v8z^G41zt| z{uK)Nw-T^=u)o1J?*S~jLYMJ6UgBlcVX(Fl(_tYdP(-m(uk;P^{^N!EBXw4=?= zI^xj*T9fPO8bi@BltC^YZ%Y`1u5m4R#m22=RCR@6EROdcR~N{5@^lQSor(1Sn>12Hs^fY!no zI9Nx40)fU!` HacsStage: + """Returns a HacsStage object.""" + return self._stage + + @stage.setter + def stage(self, value: HacsStage) -> None: + """Set the value for the stage property.""" + self._stage = value + + @property + def github(self) -> Optional[AIOGitHubAPI]: + """Returns a AIOGitHubAPI object.""" + return self._github + + @github.setter + def github(self, value: AIOGitHubAPI) -> None: + """Set the value for the github property.""" + self._github = value + + @property + def repository(self) -> Optional[AIOGitHubAPIRepository]: + """Returns a AIOGitHubAPIRepository object representing hacs/integration.""" + return self._repository + + @repository.setter + def repository(self, value: AIOGitHubAPIRepository) -> None: + """Set the value for the repository property.""" + self._repository = value + + @property + def default(self) -> Optional[AIOGitHubAPIRepository]: + """Returns a AIOGitHubAPIRepository object representing hacs/default.""" + return self._default + + @default.setter + def default(self, value: AIOGitHubAPIRepository) -> None: + """Set the value for the default property.""" + self._default = value + + @property + def hass(self) -> Optional[HomeAssistant]: + """Returns a HomeAssistant object.""" + return self._hass + + @hass.setter + def hass(self, value: HomeAssistant) -> None: + """Set the value for the default property.""" + self._hass = value diff --git a/custom_components/hacs/config_flow.py b/custom_components/hacs/config_flow.py index 7bf329d..0a0c5ef 100644 --- a/custom_components/hacs/config_flow.py +++ b/custom_components/hacs/config_flow.py @@ -1,102 +1,144 @@ -"""Adds config flow for HACS.""" -# pylint: disable=dangerous-default-value -import logging -import voluptuous as vol -from aiogithubapi import AIOGitHubException, AIOGitHubAuthentication -from homeassistant import config_entries -from homeassistant.core import callback -from homeassistant.helpers import aiohttp_client - -from .const import DOMAIN -from .configuration_schema import hacs_base_config_schema, hacs_config_option_schema - -from custom_components.hacs.globals import get_hacs -from custom_components.hacs.helpers.information import get_repository - -_LOGGER = logging.getLogger(__name__) - - -class HacsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): - """Config flow for HACS.""" - - VERSION = 1 - CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL - - def __init__(self): - """Initialize.""" - self._errors = {} - - async def async_step_user(self, user_input={}): - """Handle a flow initialized by the user.""" - self._errors = {} - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") - if self.hass.data.get(DOMAIN): - return self.async_abort(reason="single_instance_allowed") - - if user_input is not None: - if await self._test_token(user_input["token"]): - return self.async_create_entry(title="", data=user_input) - - self._errors["base"] = "auth" - return await self._show_config_form(user_input) - - return await self._show_config_form(user_input) - - async def _show_config_form(self, user_input): - """Show the configuration form to edit location data.""" - return self.async_show_form( - step_id="user", - data_schema=vol.Schema(hacs_base_config_schema(user_input, True)), - errors=self._errors, - ) - - @staticmethod - @callback - def async_get_options_flow(config_entry): - return HacsOptionsFlowHandler(config_entry) - - async def async_step_import(self, user_input): - """Import a config entry. - Special type of import, we're not actually going to store any data. - Instead, we're going to rely on the values that are in config file. - """ - if self._async_current_entries(): - return self.async_abort(reason="single_instance_allowed") - - return self.async_create_entry(title="configuration.yaml", data={}) - - async def _test_token(self, token): - """Return true if token is valid.""" - try: - session = aiohttp_client.async_get_clientsession(self.hass) - await get_repository(session, token, "hacs/org") - return True - except (AIOGitHubException, AIOGitHubAuthentication) as exception: - _LOGGER.error(exception) - return False - - -class HacsOptionsFlowHandler(config_entries.OptionsFlow): - """HACS config flow options handler.""" - - def __init__(self, config_entry): - """Initialize HACS options flow.""" - self.config_entry = config_entry - - async def async_step_init(self, user_input=None): - """Manage the options.""" - return await self.async_step_user() - - async def async_step_user(self, user_input=None): - """Handle a flow initialized by the user.""" - hacs = get_hacs() - if user_input is not None: - return self.async_create_entry(title="", data=user_input) - - if hacs.configuration.config_type == "yaml": - schema = {vol.Optional("not_in_use", default=""): str} - else: - schema = hacs_config_option_schema(self.config_entry.options) - - return self.async_show_form(step_id="user", data_schema=vol.Schema(schema)) +"""Adds config flow for HACS.""" +import voluptuous as vol +from aiogithubapi import ( + AIOGitHubAPIAuthenticationException, + AIOGitHubAPIException, + GitHubDevice, +) +from aiogithubapi.common.const import OAUTH_USER_LOGIN +from homeassistant import config_entries +from homeassistant.core import callback +from homeassistant.helpers import aiohttp_client + +from custom_components.hacs.const import DOMAIN +from custom_components.hacs.helpers.functions.configuration_schema import ( + hacs_config_option_schema, +) +from custom_components.hacs.helpers.functions.logger import getLogger +from custom_components.hacs.share import get_hacs + +from .base import HacsBase + +_LOGGER = getLogger() + + +class HacsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN): + """Config flow for HACS.""" + + VERSION = 1 + CONNECTION_CLASS = config_entries.CONN_CLASS_CLOUD_POLL + + def __init__(self): + """Initialize.""" + self._errors = {} + self.device = None + + async def async_step_device(self, user_input): + """Handle device steps""" + ## Vaiting for token + try: + activation = await self.device.async_device_activation() + return self.async_create_entry( + title="", data={"token": activation.access_token} + ) + except ( + AIOGitHubAPIException, + AIOGitHubAPIAuthenticationException, + ) as exception: + _LOGGER.error(exception) + self._errors["base"] = "auth" + return await self._show_config_form(user_input) + + async def async_step_user(self, user_input): + """Handle a flow initialized by the user.""" + self._errors = {} + if self._async_current_entries(): + return self.async_abort(reason="single_instance_allowed") + if self.hass.data.get(DOMAIN): + return self.async_abort(reason="single_instance_allowed") + + if user_input: + if [x for x in user_input if not user_input[x]]: + self._errors["base"] = "acc" + return await self._show_config_form(user_input) + + ## Get device key + if not self.device: + return await self._show_device_form() + + ## Initial form + return await self._show_config_form(user_input) + + async def _show_device_form(self): + """Device flow""" + self.device = GitHubDevice( + "395a8e669c5de9f7c6e8", + session=aiohttp_client.async_get_clientsession(self.hass), + ) + device_data = await self.device.async_register_device() + + return self.async_show_form( + step_id="device", + errors=self._errors, + description_placeholders={ + "url": OAUTH_USER_LOGIN, + "code": device_data.user_code, + }, + ) + + async def _show_config_form(self, user_input): + """Show the configuration form to edit location data.""" + if not user_input: + user_input = {} + return self.async_show_form( + step_id="user", + data_schema=vol.Schema( + { + vol.Required( + "acc_logs", default=user_input.get("acc_logs", False) + ): bool, + vol.Required( + "acc_addons", default=user_input.get("acc_addons", False) + ): bool, + vol.Required( + "acc_untested", default=user_input.get("acc_untested", False) + ): bool, + vol.Required( + "acc_disable", default=user_input.get("acc_disable", False) + ): bool, + } + ), + errors=self._errors, + ) + + @staticmethod + @callback + def async_get_options_flow(config_entry): + return HacsOptionsFlowHandler(config_entry) + + +class HacsOptionsFlowHandler(config_entries.OptionsFlow): + """HACS config flow options handler.""" + + def __init__(self, config_entry): + """Initialize HACS options flow.""" + self.config_entry = config_entry + + async def async_step_init(self, _user_input=None): + """Manage the options.""" + return await self.async_step_user() + + async def async_step_user(self, user_input=None): + """Handle a flow initialized by the user.""" + hacs: HacsBase = get_hacs() + if user_input is not None: + return self.async_create_entry(title="", data=user_input) + + if hacs.configuration.config_type == "yaml": + schema = {vol.Optional("not_in_use", default=""): str} + else: + schema = hacs_config_option_schema(self.config_entry.options) + del schema["frontend_repo"] + del schema["frontend_repo_url"] + + return self.async_show_form(step_id="user", data_schema=vol.Schema(schema)) diff --git a/custom_components/hacs/const.py b/custom_components/hacs/const.py index 47f44f2..c7411da 100644 --- a/custom_components/hacs/const.py +++ b/custom_components/hacs/const.py @@ -1,293 +1,299 @@ -"""Constants for HACS""" -NAME_LONG = "HACS (Home Assistant Community Store)" -NAME_SHORT = "HACS" -VERSION = "0.23.2" -DOMAIN = "hacs" -PROJECT_URL = "https://github.com/hacs/integration/" -CUSTOM_UPDATER_LOCATIONS = [ - "{}/custom_components/custom_updater.py", - "{}/custom_components/custom_updater/__init__.py", -] - -ISSUE_URL = f"{PROJECT_URL}issues" -DOMAIN_DATA = f"{NAME_SHORT.lower()}_data" - -ELEMENT_TYPES = ["integration", "plugin"] - -IFRAME = { - "title": "Community", - "icon": "mdi:alpha-c-box", - "url": "/community_overview", - "path": "community", - "require_admin": True, -} - - -# Messages -CUSTOM_UPDATER_WARNING = """ -This cannot be used with custom_updater. -To use this you need to remove custom_updater form {} -""" - -STARTUP = f""" -------------------------------------------------------------------- -HACS (Home Assistant Community Store) - -Version: {VERSION} -This is a custom integration -If you have any issues with this you need to open an issue here: -https://github.com/hacs/integration/issues -------------------------------------------------------------------- -""" - -LOCALE = [ - "ALL", - "AF", - "AL", - "DZ", - "AS", - "AD", - "AO", - "AI", - "AQ", - "AG", - "AR", - "AM", - "AW", - "AU", - "AT", - "AZ", - "BS", - "BH", - "BD", - "BB", - "BY", - "BE", - "BZ", - "BJ", - "BM", - "BT", - "BO", - "BQ", - "BA", - "BW", - "BV", - "BR", - "IO", - "BN", - "BG", - "BF", - "BI", - "KH", - "CM", - "CA", - "CV", - "KY", - "CF", - "TD", - "CL", - "CN", - "CX", - "CC", - "CO", - "KM", - "CG", - "CD", - "CK", - "CR", - "HR", - "CU", - "CW", - "CY", - "CZ", - "CI", - "DK", - "DJ", - "DM", - "DO", - "EC", - "EG", - "SV", - "GQ", - "ER", - "EE", - "ET", - "FK", - "FO", - "FJ", - "FI", - "FR", - "GF", - "PF", - "TF", - "GA", - "GM", - "GE", - "DE", - "GH", - "GI", - "GR", - "GL", - "GD", - "GP", - "GU", - "GT", - "GG", - "GN", - "GW", - "GY", - "HT", - "HM", - "VA", - "HN", - "HK", - "HU", - "IS", - "IN", - "ID", - "IR", - "IQ", - "IE", - "IM", - "IL", - "IT", - "JM", - "JP", - "JE", - "JO", - "KZ", - "KE", - "KI", - "KP", - "KR", - "KW", - "KG", - "LA", - "LV", - "LB", - "LS", - "LR", - "LY", - "LI", - "LT", - "LU", - "MO", - "MK", - "MG", - "MW", - "MY", - "MV", - "ML", - "MT", - "MH", - "MQ", - "MR", - "MU", - "YT", - "MX", - "FM", - "MD", - "MC", - "MN", - "ME", - "MS", - "MA", - "MZ", - "MM", - "NA", - "NR", - "NP", - "NL", - "NC", - "NZ", - "NI", - "NE", - "NG", - "NU", - "NF", - "MP", - "NO", - "OM", - "PK", - "PW", - "PS", - "PA", - "PG", - "PY", - "PE", - "PH", - "PN", - "PL", - "PT", - "PR", - "QA", - "RO", - "RU", - "RW", - "RE", - "BL", - "SH", - "KN", - "LC", - "MF", - "PM", - "VC", - "WS", - "SM", - "ST", - "SA", - "SN", - "RS", - "SC", - "SL", - "SG", - "SX", - "SK", - "SI", - "SB", - "SO", - "ZA", - "GS", - "SS", - "ES", - "LK", - "SD", - "SR", - "SJ", - "SZ", - "SE", - "CH", - "SY", - "TW", - "TJ", - "TZ", - "TH", - "TL", - "TG", - "TK", - "TO", - "TT", - "TN", - "TR", - "TM", - "TC", - "TV", - "UG", - "UA", - "AE", - "GB", - "US", - "UM", - "UY", - "UZ", - "VU", - "VE", - "VN", - "VG", - "VI", - "WF", - "EH", - "YE", - "ZM", - "ZW", -] +"""Constants for HACS""" +NAME_LONG = "HACS (Home Assistant Community Store)" +NAME_SHORT = "HACS" +VERSION = "1.8.0" +DOMAIN = "hacs" +PROJECT_URL = "https://github.com/hacs/integration/" +CUSTOM_UPDATER_LOCATIONS = [ + "{}/custom_components/custom_updater.py", + "{}/custom_components/custom_updater/__init__.py", +] + +ISSUE_URL = f"{PROJECT_URL}issues" +DOMAIN_DATA = f"{NAME_SHORT.lower()}_data" + +ELEMENT_TYPES = ["integration", "plugin"] + +PACKAGE_NAME = "custom_components.hacs" + +IFRAME = { + "title": "HACS", + "icon": "hacs:hacs", + "url": "/community_overview", + "path": "community", + "require_admin": True, +} + +VERSION_STORAGE = "6" +STORENAME = "hacs" + +# Messages +NO_ELEMENTS = "No elements to show, open the store to install some awesome stuff." + +CUSTOM_UPDATER_WARNING = """ +This cannot be used with custom_updater. +To use this you need to remove custom_updater form {} +""" + +STARTUP = f""" +------------------------------------------------------------------- +HACS (Home Assistant Community Store) + +Version: {VERSION} +This is a custom integration +If you have any issues with this you need to open an issue here: +https://github.com/hacs/integration/issues +------------------------------------------------------------------- +""" + +LOCALE = [ + "ALL", + "AF", + "AL", + "DZ", + "AS", + "AD", + "AO", + "AI", + "AQ", + "AG", + "AR", + "AM", + "AW", + "AU", + "AT", + "AZ", + "BS", + "BH", + "BD", + "BB", + "BY", + "BE", + "BZ", + "BJ", + "BM", + "BT", + "BO", + "BQ", + "BA", + "BW", + "BV", + "BR", + "IO", + "BN", + "BG", + "BF", + "BI", + "KH", + "CM", + "CA", + "CV", + "KY", + "CF", + "TD", + "CL", + "CN", + "CX", + "CC", + "CO", + "KM", + "CG", + "CD", + "CK", + "CR", + "HR", + "CU", + "CW", + "CY", + "CZ", + "CI", + "DK", + "DJ", + "DM", + "DO", + "EC", + "EG", + "SV", + "GQ", + "ER", + "EE", + "ET", + "FK", + "FO", + "FJ", + "FI", + "FR", + "GF", + "PF", + "TF", + "GA", + "GM", + "GE", + "DE", + "GH", + "GI", + "GR", + "GL", + "GD", + "GP", + "GU", + "GT", + "GG", + "GN", + "GW", + "GY", + "HT", + "HM", + "VA", + "HN", + "HK", + "HU", + "IS", + "IN", + "ID", + "IR", + "IQ", + "IE", + "IM", + "IL", + "IT", + "JM", + "JP", + "JE", + "JO", + "KZ", + "KE", + "KI", + "KP", + "KR", + "KW", + "KG", + "LA", + "LV", + "LB", + "LS", + "LR", + "LY", + "LI", + "LT", + "LU", + "MO", + "MK", + "MG", + "MW", + "MY", + "MV", + "ML", + "MT", + "MH", + "MQ", + "MR", + "MU", + "YT", + "MX", + "FM", + "MD", + "MC", + "MN", + "ME", + "MS", + "MA", + "MZ", + "MM", + "NA", + "NR", + "NP", + "NL", + "NC", + "NZ", + "NI", + "NE", + "NG", + "NU", + "NF", + "MP", + "NO", + "OM", + "PK", + "PW", + "PS", + "PA", + "PG", + "PY", + "PE", + "PH", + "PN", + "PL", + "PT", + "PR", + "QA", + "RO", + "RU", + "RW", + "RE", + "BL", + "SH", + "KN", + "LC", + "MF", + "PM", + "VC", + "WS", + "SM", + "ST", + "SA", + "SN", + "RS", + "SC", + "SL", + "SG", + "SX", + "SK", + "SI", + "SB", + "SO", + "ZA", + "GS", + "SS", + "ES", + "LK", + "SD", + "SR", + "SJ", + "SZ", + "SE", + "CH", + "SY", + "TW", + "TJ", + "TZ", + "TH", + "TL", + "TG", + "TK", + "TO", + "TT", + "TN", + "TR", + "TM", + "TC", + "TV", + "UG", + "UA", + "AE", + "GB", + "US", + "UM", + "UY", + "UZ", + "VU", + "VE", + "VN", + "VG", + "VI", + "WF", + "EH", + "YE", + "ZM", + "ZW", +] diff --git a/custom_components/hacs/constrains.py b/custom_components/hacs/constrains.py deleted file mode 100644 index 269cba8..0000000 --- a/custom_components/hacs/constrains.py +++ /dev/null @@ -1,99 +0,0 @@ -"""HACS Startup constrains.""" -# pylint: disable=bad-continuation -import os - -from .const import CUSTOM_UPDATER_LOCATIONS, CUSTOM_UPDATER_WARNING -from .helpers.misc import version_left_higher_then_right - -from custom_components.hacs.globals import get_hacs - -MINIMUM_HA_VERSION = "0.98.0" - - -def check_constans(): - """Check HACS constrains.""" - if not constrain_translations(): - return False - if not constrain_custom_updater(): - return False - if not constrain_version(): - return False - return True - - -def constrain_custom_updater(): - """Check if custom_updater exist.""" - hacs = get_hacs() - for location in CUSTOM_UPDATER_LOCATIONS: - if os.path.exists(location.format(hacs.system.config_path)): - msg = CUSTOM_UPDATER_WARNING.format( - location.format(hacs.system.config_path) - ) - hacs.logger.critical(msg) - return False - return True - - -def constrain_version(): - """Check if the version is valid.""" - hacs = get_hacs() - if not version_left_higher_then_right(hacs.system.ha_version, MINIMUM_HA_VERSION): - hacs.logger.critical( - f"You need HA version {MINIMUM_HA_VERSION} or newer to use this integration." - ) - return False - return True - - -def constrain_translations(): - """Check if traslations exist.""" - hacs = get_hacs() - if not os.path.exists( - f"{hacs.system.config_path}/custom_components/hacs/.translations" - ): - hacs.logger.critical("You are missing the translations directory.") - return False - return True - - -def check_requirements(): - """Check the requirements""" - missing = [] - try: - from aiogithubapi import AIOGitHubException # pylint: disable=unused-import - except ImportError: - missing.append("aiogithubapi") - - try: - from hacs_frontend import locate_gz # pylint: disable=unused-import - except ImportError: - missing.append("hacs_frontend") - - try: - import semantic_version # pylint: disable=unused-import - except ImportError: - missing.append("semantic_version") - - try: - from integrationhelper import Logger # pylint: disable=unused-import - except ImportError: - missing.append("integrationhelper") - - try: - import backoff # pylint: disable=unused-import - except ImportError: - missing.append("backoff") - - try: - import aiofiles # pylint: disable=unused-import - except ImportError: - missing.append("aiofiles") - - if missing: - hacs = get_hacs() - for requirement in missing: - hacs.logger.critical( - f"Required python requirement '{requirement}' is not installed" - ) - return False - return True diff --git a/custom_components/hacs/enums.py b/custom_components/hacs/enums.py new file mode 100644 index 0000000..887946a --- /dev/null +++ b/custom_components/hacs/enums.py @@ -0,0 +1,39 @@ +"""Helper constants.""" +# pylint: disable=missing-class-docstring +from enum import Enum + + +class HacsCategory(str, Enum): + APPDAEMON = "appdaemon" + INTEGRATION = "integration" + LOVELACE = "lovelace" + PLUGIN = "plugin" # Kept for legacy purposes + NETDAEMON = "netdaemon" + PYTHON_SCRIPT = "python_script" + THEME = "theme" + REMOVED = "removed" + + +class LovelaceMode(str, Enum): + """Lovelace Modes.""" + + STORAGE = "storage" + AUTO = "auto" + YAML = "yaml" + + +class HacsStage(str, Enum): + SETUP = "setup" + STARTUP = "startup" + WAITING = "waiting" + RUNNING = "running" + BACKGROUND = "background" + + +class HacsSetupTask(str, Enum): + WEBSOCKET = "WebSocket API" + FRONTEND = "Frontend" + SENSOR = "Sensor" + HACS_REPO = "Hacs Repository" + CATEGORIES = "Additional categories" + CLEAR_STORAGE = "Clear storage" diff --git a/custom_components/hacs/globals.py b/custom_components/hacs/globals.py deleted file mode 100644 index 91d8314..0000000 --- a/custom_components/hacs/globals.py +++ /dev/null @@ -1,29 +0,0 @@ -# pylint: disable=invalid-name, missing-docstring -hacs = [] -removed_repositories = [] - - -def get_hacs(): - if not hacs: - from custom_components.hacs.hacsbase import Hacs - - hacs.append(Hacs()) - - return hacs[0] - - -def is_removed(repository): - return repository in [x.repository for x in removed_repositories] - - -def get_removed(repository): - if not is_removed(repository): - from custom_components.hacs.repositories.removed import RemovedRepository - - removed_repo = RemovedRepository() - removed_repo.repository = repository - removed_repositories.append(removed_repo) - filter_repos = [ - x for x in removed_repositories if x.repository.lower() == repository.lower() - ] - return filter_repos[0] diff --git a/custom_components/hacs/hacsbase/__init__.py b/custom_components/hacs/hacsbase/__init__.py index 8795661..e69de29 100644 --- a/custom_components/hacs/hacsbase/__init__.py +++ b/custom_components/hacs/hacsbase/__init__.py @@ -1,355 +0,0 @@ -"""Initialize the HACS base.""" -# pylint: disable=unused-argument, bad-continuation -import json -import uuid -from datetime import timedelta - -from homeassistant.helpers.event import async_call_later, async_track_time_interval - -from aiogithubapi import AIOGitHubException, AIOGitHubRatelimit -from integrationhelper import Logger - -from custom_components.hacs.hacsbase.task_factory import HacsTaskFactory -from custom_components.hacs.hacsbase.exceptions import HacsException - -from custom_components.hacs.const import ELEMENT_TYPES -from custom_components.hacs.setup import setup_extra_stores -from custom_components.hacs.store import async_load_from_store, async_save_to_store -from custom_components.hacs.helpers.get_defaults import ( - get_default_repos_lists, - get_default_repos_orgs, -) - -from custom_components.hacs.helpers.register_repository import register_repository -from custom_components.hacs.globals import removed_repositories, get_removed, is_removed -from custom_components.hacs.repositories.removed import RemovedRepository - - -class HacsStatus: - """HacsStatus.""" - - startup = True - new = False - background_task = False - reloading_data = False - upgrading_all = False - - -class HacsFrontend: - """HacsFrontend.""" - - version_running = None - version_available = None - update_pending = False - - -class HacsCommon: - """Common for HACS.""" - - categories = [] - default = [] - installed = [] - skip = [] - - -class System: - """System info.""" - - status = HacsStatus() - config_path = None - ha_version = None - disabled = False - lovelace_mode = "storage" - - -class Developer: - """Developer settings/tools.""" - - template_id = "Repository ID" - template_content = "" - template_raw = "" - - @property - def devcontainer(self): - """Is it a devcontainer?""" - import os - - if "DEVCONTAINER" in os.environ: - return True - return False - - -class Hacs: - """The base class of HACS, nested thoughout the project.""" - - token = f"{str(uuid.uuid4())}-{str(uuid.uuid4())}" - hacsweb = f"/hacsweb/{token}" - hacsapi = f"/hacsapi/{token}" - repositories = [] - frontend = HacsFrontend() - repo = None - data_repo = None - developer = Developer() - data = None - configuration = None - logger = Logger("hacs") - github = None - hass = None - version = None - session = None - factory = HacsTaskFactory() - system = System() - recuring_tasks = [] - common = HacsCommon() - - @staticmethod - def init(hass, github_token): - """Return a initialized HACS object.""" - return Hacs() - - def get_by_id(self, repository_id): - """Get repository by ID.""" - try: - for repository in self.repositories: - if repository.information.uid == repository_id: - return repository - except Exception: # pylint: disable=broad-except - pass - return None - - def get_by_name(self, repository_full_name): - """Get repository by full_name.""" - try: - for repository in self.repositories: - if repository.data.full_name.lower() == repository_full_name.lower(): - return repository - except Exception: # pylint: disable=broad-except - pass - return None - - def is_known(self, repository_full_name): - """Return a bool if the repository is known.""" - return repository_full_name.lower() in [ - x.data.full_name.lower() for x in self.repositories - ] - - @property - def sorted_by_name(self): - """Return a sorted(by name) list of repository objects.""" - return sorted(self.repositories, key=lambda x: x.display_name) - - @property - def sorted_by_repository_name(self): - """Return a sorted(by repository_name) list of repository objects.""" - return sorted(self.repositories, key=lambda x: x.data.full_name) - - async def register_repository(self, full_name, category, check=True): - """Register a repository.""" - await register_repository(full_name, category, check=True) - - async def startup_tasks(self): - """Tasks tha are started after startup.""" - self.system.status.background_task = True - await self.hass.async_add_executor_job(setup_extra_stores) - self.hass.bus.async_fire("hacs/status", {}) - self.logger.debug(self.github.ratelimits.remaining) - self.logger.debug(self.github.ratelimits.reset_utc) - - await self.handle_critical_repositories_startup() - await self.handle_critical_repositories() - await self.load_known_repositories() - await self.clear_out_removed_repositories() - - self.recuring_tasks.append( - async_track_time_interval( - self.hass, self.recuring_tasks_installed, timedelta(minutes=30) - ) - ) - self.recuring_tasks.append( - async_track_time_interval( - self.hass, self.recuring_tasks_all, timedelta(minutes=800) - ) - ) - - self.hass.bus.async_fire("hacs/reload", {"force": True}) - await self.recuring_tasks_installed() - - self.system.status.startup = False - self.system.status.new = False - self.system.status.background_task = False - self.hass.bus.async_fire("hacs/status", {}) - await self.data.async_write() - - async def handle_critical_repositories_startup(self): - """Handled critical repositories during startup.""" - alert = False - critical = await async_load_from_store(self.hass, "critical") - if not critical: - return - for repo in critical: - if not repo["acknowledged"]: - alert = True - if alert: - self.logger.critical("URGENT!: Check the HACS panel!") - self.hass.components.persistent_notification.create( - title="URGENT!", message="**Check the HACS panel!**" - ) - - async def handle_critical_repositories(self): - """Handled critical repositories during runtime.""" - # Get critical repositories - instored = [] - critical = [] - was_installed = False - - try: - critical = await self.data_repo.get_contents("critical") - critical = json.loads(critical.content) - except AIOGitHubException: - pass - - if not critical: - self.logger.debug("No critical repositories") - return - - stored_critical = await async_load_from_store(self.hass, "critical") - - for stored in stored_critical or []: - instored.append(stored["repository"]) - - stored_critical = [] - - for repository in critical: - removed_repo = get_removed(repository["repository"]) - removed_repo.removal_type = "critical" - repo = self.get_by_name(repository["repository"]) - - stored = { - "repository": repository["repository"], - "reason": repository["reason"], - "link": repository["link"], - "acknowledged": True, - } - if repository["repository"] not in instored: - if repo is not None and repo.installed: - self.logger.critical( - f"Removing repository {repository['repository']}, it is marked as critical" - ) - was_installed = True - stored["acknowledged"] = False - # Uninstall from HACS - repo.remove() - await repo.uninstall() - stored_critical.append(stored) - removed_repo.update_data(stored) - - # Save to FS - await async_save_to_store(self.hass, "critical", stored_critical) - - # Resart HASS - if was_installed: - self.logger.critical("Resarting Home Assistant") - self.hass.async_create_task(self.hass.async_stop(100)) - - async def recuring_tasks_installed(self, notarealarg=None): - """Recuring tasks for installed repositories.""" - self.logger.debug( - "Starting recuring background task for installed repositories" - ) - self.system.status.background_task = True - self.hass.bus.async_fire("hacs/status", {}) - self.logger.debug(self.github.ratelimits.remaining) - self.logger.debug(self.github.ratelimits.reset_utc) - for repository in self.repositories: - if ( - repository.status.installed - and repository.data.category in self.common.categories - ): - self.factory.tasks.append(self.factory.safe_update(repository)) - - await self.factory.execute() - await self.handle_critical_repositories() - self.system.status.background_task = False - self.hass.bus.async_fire("hacs/status", {}) - await self.data.async_write() - self.logger.debug("Recuring background task for installed repositories done") - - async def recuring_tasks_all(self, notarealarg=None): - """Recuring tasks for all repositories.""" - self.logger.debug("Starting recuring background task for all repositories") - await self.hass.async_add_executor_job(setup_extra_stores) - self.system.status.background_task = True - self.hass.bus.async_fire("hacs/status", {}) - self.logger.debug(self.github.ratelimits.remaining) - self.logger.debug(self.github.ratelimits.reset_utc) - for repository in self.repositories: - if repository.data.category in self.common.categories: - self.factory.tasks.append(self.factory.safe_common_update(repository)) - - await self.factory.execute() - await self.load_known_repositories() - await self.clear_out_removed_repositories() - self.system.status.background_task = False - await self.data.async_write() - self.hass.bus.async_fire("hacs/status", {}) - self.hass.bus.async_fire("hacs/repository", {"action": "reload"}) - self.logger.debug("Recuring background task for all repositories done") - - async def clear_out_removed_repositories(self): - """Clear out blaclisted repositories.""" - need_to_save = False - for removed in removed_repositories: - if self.is_known(removed.repository): - repository = self.get_by_name(removed.repository) - if repository.status.installed and removed.removal_type != "critical": - self.logger.warning( - f"You have {repository.data.full_name} installed with HACS " - + f"this repository has been removed, please consider removing it. " - + f"Removal reason ({removed.removal_type})" - ) - else: - need_to_save = True - repository.remove() - - if need_to_save: - await self.data.async_write() - - async def get_repositories(self): - """Return a list of repositories.""" - repositories = {} - for category in self.common.categories: - repositories[category] = await get_default_repos_lists( - self.session, self.configuration.token, category - ) - org = await get_default_repos_orgs(self.github, category) - for repo in org: - repositories[category].append(repo) - - for category in repositories: - for repo in repositories[category]: - if repo not in self.common.default: - self.common.default.append(repo) - return repositories - - async def load_known_repositories(self): - """Load known repositories.""" - self.logger.info("Loading known repositories") - repositories = await self.get_repositories() - - for item in await get_default_repos_lists( - self.session, self.configuration.token, "removed" - ): - removed = get_removed(item["repository"]) - removed.reason = item.get("reason") - removed.link = item.get("link") - removed.removal_type = item.get("removal_type") - - for category in repositories: - for repo in repositories[category]: - if is_removed(repo): - continue - if self.is_known(repo): - continue - self.factory.tasks.append(self.factory.safe_register(repo, category)) - await self.factory.execute() - self.logger.info("Loading known repositories finished") diff --git a/custom_components/hacs/hacsbase/__pycache__/__init__.cpython-37.pyc b/custom_components/hacs/hacsbase/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index b9fec3b3a4d068b9da1b0acfe666ea4cedea1ab5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10796 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 Gh#3GbyCD$( literal 0 HcmV?d00001 diff --git a/custom_components/hacs/hacsbase/__pycache__/backup.cpython-37.pyc b/custom_components/hacs/hacsbase/__pycache__/backup.cpython-37.pyc deleted file mode 100644 index f1d211bcfaf7506f3a1e8c19f91dc76b1416c871..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3631 zcmc&$OOG7Y6~4FL{dC(Hk8K|5goLCMr#napA%wt;fe?X477!d-N{VvbJ2N$|?rPtv z4l|_zA#AbZ2Y`jtgV?ZThs1x#zP4F;mu#|NlYHk^bxrr!CTybI(mnV6sPmokox9oT zG#S!A|MuZu?)Dh_J2jTcMCCGa`W}Q}Vd7^<<1Fe=Y-eQAh?rsL*jrnYN7ss)!0Wa({ zmU4!Q^G33>BURoR?FM1$U)S?SAM>1TJ(FMn*w6<|=VczCrTUf%r&Bji7 zm9bl1V(f_~F3>8R@&ffmLfVuPl@{_9JQ-+a$UcmIvFHEICe!U`XB5uUESdOW zGMOb)In7e7S1qmwsT`GC4rd32=lgVH-#>*7QreLjP43{!Pi>ZPEoX$BMv`HaJ}vNm^Li4jjEAJw~{2(%pF$ z#f76LnUYc&P{Z478DDn(Z>FYk8__4G*yW^}p5yS&YNlnn#*9`72ug&v1r#EiUC8BV!+ z&1@WOqRr+TRBA(5Hyew907!4t<2G8)0c?T>bpY=!$WX`-Y%+xDt7~&V#yA;`p1Wdj zw9iLbvog3{7@WIkt?q+*d(jbkUzmcv$4(?&DEUXAUtY{Pmp%>-!=J(cRj7>B{SWyy{Fw7P(W!cj#N#A>LV`GYMUaariaO+a`Z6B3jJE4t@E+3rA^Ph?hHx1<{T;+={L`_I zjjby7*;VXws@UgNv9DtDEt$O>fE1>Aue5=>V;IsAl&GYvt>D+F=orW{ybE+pG%|Bx zA?_O+7}>M;9F)v=g$Yiy^41?75CQW?%IH3mDg_@REcsKJ34NQ<*1#*8O3sorf_EM0 z5UEYR@G?1>X>x1s$c!4WjKUH@78Dj?phMumC>e}s>ZFbdi!J1o*oPSoZyNdelYf6t zlp|znU-%M@HD7op`;oqYRW4Bs!9+1_{~Zt+kym4{xyKR5_{;3imp{NeW6>6fIcj`7 z2H&v{IS9!7VChTCwu$pQc$Jt$e>cb15Vw9XBGvwdfTZvcWVt;&2qv+jU);Kvn(pUZ z6bA}726jL|k){h54-P`Iu=Ohjp>8VD_oHlAQ`o@LpcEd#M^F{LG?}YV`qZIatLSBc z0b-k9N{z1FkOTQ5`%D+T!VPz30j_9~J z9l_UjR6sAbT^WN%OPc%@2G!3=kfrO*R?I*f1ldC2Jzs{f7w7?>5(!w`(_h=bXLeuw E4>@W*a{vGU diff --git a/custom_components/hacs/hacsbase/__pycache__/configuration.cpython-37.pyc b/custom_components/hacs/hacsbase/__pycache__/configuration.cpython-37.pyc deleted file mode 100644 index 7d4a15d5c52749330e4f012dfc7e3684a65b2ed6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2409 zcma)8%WvF77@x7dyWY({@@^h|xM}*>mTshq1Bxm{n?if2qKZN*SpwsDCL1?i+nJed z6U#w}^vYjQklG{v0{;Z}xpL}}D<{5hHd$EUfFu9rH=jR`zxU>TtJNTI{r>B>U;p9~ z@*8f>E(gdbaLfAujQE66W+l|8u-b_=uzj1N%t@R<#jjAp>5D3>u<8%=Az?Mo}qcHP z@`jPScS%hADc%H~y5TLu8=y1gw~c<&=y!~M%kXK#+rT^ijM14k@>wIFG3U&gb7qZv z-pJ>G&-)8Te*yTSzi9Y5qjS#aECFBkmyCP`_^Q8b_<7(L{1wA58vRwHe+l@Mf8NMF z;FtXihOZf&i$>=P@OA$Z_rS-iz^}3EuZe$|-C$i<*Z38%w8?J%Xt7)DgVzouZk*Kwy zMy6tPbF>CLRpMw{Pn~YnQOEvyAP*y@ zYniDpsXJM^lZArCX+L0D%8T}^SFbieZ7@t@b+nUu)%Lx|k994|h8P*$nf?hEf zu6S?3Evo<}c|joKw7By*S3{9{p~vD#d4lJHOFR%;(#v)|_`Wwvz*#3lpSnSS>Olb9 zkv!S`nTRWJ>>nRNII-(DqM=mTAix|zq*I9B9yGgw9kB0I?d-_>NY|k8U&t)IjwV_F z#3~lHCPv7q#@H*I2{pKlR}X+Fm%r0SFCU3}3_B%?+!Qyk69uSjCaqEeQQ#JB@C$K zfLJbdX#;MH7ITwvGMZgWw*_`<<5a3JjktDpvMkYdpDSU5djcIEr3EvhYm!5aE77fpEWw1X`=DnhIn zQ*NS-tb;I(VBDTXHis~euz;|Lu!OLTfNq*978j6RMDPH#4MjPs<{P+eLiAM}4LFQW zCVHrbbK!4*#cYy+cFRqGhUM5z`al0hnwIOhb&HyFx~{kbr)US$sb|0*-XHzXvBwig wqG>pN^Irq0XBrm)2U3rp{U0Jty=W>&CG#KVYxnLbUmxR4l^AwNJbGaN3G42C3IG5A diff --git a/custom_components/hacs/hacsbase/__pycache__/configuration.cpython-38.pyc b/custom_components/hacs/hacsbase/__pycache__/configuration.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9513610232af06ff99c347e29658874751539efe GIT binary patch literal 2533 zcmb7GNpsXj6mBiEZ^jG8HW;xO7?v?`NKurFf^vvCxJ-)WqDo37tJ`B?jWq7=0jwJ5 z5OYla0S7qd$K;%%f_4Iut`e+A3r7QGi z*6j-U3bcAS&{+^_E#qw!;X|j7A}EjHRKHn_2?m}r-+m1~`VO~!R}@5%J8O))eo2%8 z7XTOi3fW6!uadpI#$x8zL>)L4!VSVz;MDvk@$1BI5x+rrif|Kf%RfV$DY8$K{S4q~ ze}?Qcgl7rQ()x3>{v7eo68|jVd4HZb=KwGG=LjznXMs3NfY1AjWWNA-*@6HPUYL zJD^<^EBqe#T?cFDqr+GE-46xG^EF$-@SXc3QP3{X{--cO|72{x^YU*=dJ#M-p$fLMKbPd2*vb-VYI`>ge|Y^Lc2Uf zVVWxDq93Lr31Zl!ak0%z>#J4AIuUtE?~0y))vE+Xs}=~Gg3k6tYMdvVo2C?H0}Pp| zi}!sY;~oSsOuiJLy-Y)jM1)ENN!*Jyv}I8WJ&&RjGbVQC0I>uo?d(2) zA^XHWvqNiW{m6dx4(*}Mt-YdL+^=ivlg&XNT0?t*eTIR#U*`@)!Od^|EaOy<_BxOf z{GHshr!%^lWZh2Nl{l+q4aF>qWe|CBMLqOZ)K=S(ZO~0A*nGaR@$`jkfCk2E$ixSY zvnLKHS|f1-ZG}EMa2KGW7lB>2@Ml~3%w!CR*LF-}A}1q1uEOIHg$@87SRmuzyaz?I zp{1=OTP*@&5Dx1z>GHCMseEMp4q@ZYURicx3?WkwPwd(~Vh~I=w1WZ$Fen1Hc5Ddp z@}VJo(49XdmIfgNFhf7yRE#5awbFTNDo8@((BS||@%wMR9lI^=!6G_g@H zR$iKE?`@dGytCCVn`*4$ROv8{gmJgCEHMtejwJ3^;zmbV!GxHS5>UWewhQtO-k~gE zT>T1$Ex>^zZ{Zy*+EZz$pjTAd58#Gc1i_p|s|@LIZ5OUZyJ!{dI;3XS&gUmQ?u?&p zTU+hx*duA!6G33A0Zy<%g18<8`-3n!zEgre%Az2U*ptRlTAEf6gemNt8c@M4OrTsv z0UriTR!~qKx!8iEGOVK!3r#jrv`|c;m_{*!f?_a-=q!qP6z5PZpjbk29>oO|Skcl$ zaS6p05XON5rP7wzqNWb1&~elgFekah&N~%M3M&>iWd^-fk3dvy*Qs0I@f)%2rrRvr z7OmB;$RA)O<6?fzy>G@$2ggijS0s>O)u9)gP#tmd5u>PH%(vrd^0}Qs8sPv`9q4%Y o+>tnDo;fCd|GfS3-F2K_wm5~Uop1t$7fYTPInx3yh5QQ zoPYgx4l0AWDmnOwqVO+f79oN1(>Y1k!cES&OH0 zfgq&jMxZTEEwwPNgW>$^dVn@L&O!ye;3c;+Y6Y09{TS4iOVBw7)XMx~`X>UkqtQKY zZ`5^`O~O&Hc}fUVDIv{^coi-E4*|Ijmhs0ZOwMd02>r_-8HUk{#8-<&v|I)6$=v@Z zdLBk3_!L|Q;VQm!s-D?OoBq6-rb=dFGfiuQQj%1aTO}>*)b5~7Yl_^;P*1jprvIi? TtSw4XLiG-(T diff --git a/custom_components/hacs/hacsbase/__pycache__/data.cpython-37.pyc b/custom_components/hacs/hacsbase/__pycache__/data.cpython-37.pyc deleted file mode 100644 index 87fad747e940b6ce7f1b6bbb990ef1d68adf1ffc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4265 zcmZ`+OOxBi5ylKa5+wNC<#HdE;qf`_{wq-r4oK2P170FD=C@crd1r{WB z2jFU#1^Ti&L6|2!NF zT=_juyM;e|_GtIv?{{FN`oQT&kKCg$^5RgQfu@X_-#H_1-;Lr9SF28RHtO4vbK=@@ zXh$&3Rcom-2pwVX%W#;gJGwFlZfqZb1=W=9Fg$UEE$7xfH&PakXCv^kRp}1AD0by? z)>$7nJ^$d2@4zp*4#L%X3>?n|>*<0E{khgNgduY9%!#}xz%wsQa7a;^cnWmNq&9E0 z`+*Zh-97_L_`xTd{}g^v3q%Yza?TRXW-&k4;0Z4QWBSB;IPzl83A~BhofPqOT>++d zI)+38Dj$%0Wf40Dj>FN6$PM=YuE_={UvF>s!_mGs*zS+PfkV3=4v)f-JBp)ioK9lT ziQH|p+dVo{rfqxhQroV<@(4pu<0Up}%n#F@k1vjcbMO@ZgkL1!aEaz?KJ&S+N3E=F z$SuKkaU?g(3;v2REiTZQDRg=k{ZjU{vS(QsFZFZ%Avos&_-rvh^LSjjtDUpuxTj6o z=|5;~^{)2xZ&RLdzb4pWQNF~MU!UqDL$Ik1y@tO+a+PEgP3gI6eqvq|Ck(y8~bkWeGRxOYbQ5!jUS9@Yx6% zB2vZuaS+%eXXvWRz9*v?f;)>k4JiOY{-A7Pw_jYxXeXGgSsux_y{QYm3oA_{`pOV@Ks+KX8_O93FYV zUX2r1MsSMxM!|G>VGoFGQX||hRA&>56e-L*@6#=-d~^onHiT#F#N$ZS_MHCVK!)QH zzCSutrtl(XFK~sd!Ud?DaQDUoS->{%NEE3`YH%za@@WOg7UN9V!w~(p=w@LgkpU#; zF!>>h_t28LIK4pucnL=?qqsuDnKvVO6?JVC*HF9xLYbK(BKaoj-a=j7IdQxI_dUOs z;zFY=Zv^>gNc)u-TN0^Tc2K;J;sX$>j_w{f6Yw(F7>{DvMYTTmgwprGRi!ilAzEBz zpain5;}k#)Ax)K*&Zl$!AjFk*A%vT-GTH&5@fI_9S?4UzI5(NeS_WrV41-l!iIup) z^Dy#LUtE=bH}n=;VUy~9dCC5l3K*^A2I1nm-0A#t)=?fiS=!}I< zf5Fb$35%X2JbEH>WJmW6J125P==oVe$uaVIm@n$KO3hZ{oS@xWmWV=%%5?>xU(<`d6k+q2iis(2NbyNX&0EvGT2s zGev0^Y)s9s%_rKI+K4~V(1)GX$?790d7!*$W3eNMt-A!|ofJ*$aF<_9YKvJA!oVaF zgG(9rr6=SXH1DM#ybsM3ObE3}<*p~%qcDb?FcR%ad2R%`{x$pKr17*pSKR|}x#C)9 zQ<`W4^M(8f#V=6YKtX6C$}kXaN{6ya{t`!SqWBdGq5=zA%1YVMM)WJwi|oS@P_r!4 z7#3uEXDK=7Tq#2-@8I%JaI}?T$PvPJPu>0)ykq;}o?4fwr!JIjsrBM#r+qi&e-ko6 z>_Ot_6bZTML5P;nktzr25^@I6>atE$27+3m?^My9oBAQLAvMxHeGb#2+aNUFWcW3h z$;%+i`U;?P1uCX8tLh~{d7W{#!RvtW7UMeNzNyz?P7_gFU!XWMywfRQ4#iwz+p1(E z+m3BeI@>SK1sPnxB(lJz63@z@ONvH?^m2osxA9{@x>oq7=R&;z2N9)KAk zVHc>ETdXvx4>fdV392EmY-VebLNqa_5X&x8e@-BlU74e ze5hL>+_IV~1?h5oALEj=7RvH@$`rWI>8063mqmpB+IlL!QMmU+^J$6CJ6{ ZTPfGv#!!newE&%E-#XA-S+9mB`!ACARAvAG diff --git a/custom_components/hacs/hacsbase/__pycache__/data.cpython-38.pyc b/custom_components/hacs/hacsbase/__pycache__/data.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11981f50f34ed8a5df15edd127a06dbc355fcbff GIT binary patch literal 5419 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 M;4APqnz^#^KfoJ+_W%F@ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/hacsbase/__pycache__/exceptions.cpython-37.pyc b/custom_components/hacs/hacsbase/__pycache__/exceptions.cpython-37.pyc deleted file mode 100644 index b0789b2d457d6dfaaaf9d0c11965202c9ea524b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 546 zcmb7AJx{|h5Otii4OJ^uNNfxl>C#MW2vs0HCYEj;u!4P|m6|x%j#N6Zz@K63KVi?x zFJNWjE}`kd!b$gX7yItL=ZoR6&oI6|UhcQ}{iJ3W0TxqCdxD_YoZT?xDgMS32k+IN zbL@Q;U>^X--dCL&8+SK{m!)+&i>}rZ3b)j`ok-l!hKJP@(~b~;%{|=Bm51{j=lyDU zn@HOZZw9ln024hVb}1)!Zf=oLLwv?3AbQgwXx%hbkN$-Slskb`(>x7S2Q6VOyuSR`mMT1J O-twIMT4D_RQSbv3#(MVv diff --git a/custom_components/hacs/hacsbase/__pycache__/hacs.cpython-38.pyc b/custom_components/hacs/hacsbase/__pycache__/hacs.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81b589a49e28ec640cc52b690d66df396c2367f3 GIT binary patch literal 10766 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 diff --git a/custom_components/hacs/hacsbase/configuration.py b/custom_components/hacs/hacsbase/configuration.py index c9bb80e..825582b 100644 --- a/custom_components/hacs/hacsbase/configuration.py +++ b/custom_components/hacs/hacsbase/configuration.py @@ -1,73 +1,77 @@ -"""HACS Configuration.""" -import attr -from integrationhelper import Logger -from custom_components.hacs.hacsbase.exceptions import HacsException - - -@attr.s(auto_attribs=True) -class Configuration: - """Configuration class.""" - - # Main configuration: - appdaemon_path: str = "appdaemon/apps/" - appdaemon: bool = False - netdaemon_path: str = "netdaemon/apps/" - netdaemon: bool = False - config: dict = {} - config_entry: dict = {} - config_type: str = None - debug: bool = False - dev: bool = False - frontend_mode: str = "Grid" - frontend_compact: bool = False - options: dict = {} - onboarding_done: bool = False - plugin_path: str = "www/community/" - python_script_path: str = "python_scripts/" - python_script: bool = False - sidepanel_icon: str = "mdi:alpha-c-box" - sidepanel_title: str = "Community" - theme_path: str = "themes/" - theme: bool = False - token: str = None - - # Config options: - country: str = "ALL" - experimental: bool = False - release_limit: int = 5 - - def to_json(self): - """Return a dict representation of the configuration.""" - return self.__dict__ - - def print(self): - """Print the current configuration to the log.""" - logger = Logger("hacs.configuration") - config = self.to_json() - for key in config: - if key in ["config", "config_entry", "options", "token"]: - continue - logger.debug(f"{key}: {config[key]}") - - @staticmethod - def from_dict(configuration: dict, options: dict): - """Set attributes from dicts.""" - if isinstance(options, bool) or isinstance(configuration.get("options"), bool): - raise HacsException("Configuration is not valid.") - - if options is None: - options = {} - - if not configuration: - raise HacsException("Configuration is not valid.") - - config = Configuration() - - config.config = configuration - config.options = options - - for conf_type in [configuration, options]: - for key in conf_type: - setattr(config, key, conf_type[key]) - - return config +"""HACS Configuration.""" +import attr + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.logger import getLogger + +_LOGGER = getLogger() + + +@attr.s(auto_attribs=True) +class Configuration: + """Configuration class.""" + + # Main configuration: + appdaemon_path: str = "appdaemon/apps/" + appdaemon: bool = False + netdaemon_path: str = "netdaemon/apps/" + netdaemon: bool = False + config: dict = {} + config_entry: dict = {} + config_type: str = None + debug: bool = False + dev: bool = False + frontend_mode: str = "Grid" + frontend_compact: bool = False + frontend_repo: str = "" + frontend_repo_url: str = "" + options: dict = {} + onboarding_done: bool = False + plugin_path: str = "www/community/" + python_script_path: str = "python_scripts/" + python_script: bool = False + sidepanel_icon: str = "hacs:hacs" + sidepanel_title: str = "HACS" + theme_path: str = "themes/" + theme: bool = False + token: str = None + + # Config options: + country: str = "ALL" + experimental: bool = False + release_limit: int = 5 + + def to_json(self) -> dict: + """Return a dict representation of the configuration.""" + return self.__dict__ + + def print(self) -> None: + """Print the current configuration to the log.""" + config = self.to_json() + for key in config: + if key in ["config", "config_entry", "options", "token"]: + continue + _LOGGER.debug("%s: %s", key, config[key]) + + @staticmethod + def from_dict(configuration: dict, options: dict = None) -> None: + """Set attributes from dicts.""" + if isinstance(options, bool) or isinstance(configuration.get("options"), bool): + raise HacsException("Configuration is not valid.") + + if options is None: + options = {} + + if not configuration: + raise HacsException("Configuration is not valid.") + + config = Configuration() + + config.config = configuration + config.options = options + + for conf_type in [configuration, options]: + for key in conf_type: + setattr(config, key, conf_type[key]) + + return config diff --git a/custom_components/hacs/hacsbase/const.py b/custom_components/hacs/hacsbase/const.py deleted file mode 100644 index 1e72154..0000000 --- a/custom_components/hacs/hacsbase/const.py +++ /dev/null @@ -1,10 +0,0 @@ -"""Constants for HACS""" -# pylint: disable=unused-import -STORAGE_VERSION = "6" -STORENAME = "hacs" - -# Messages -NOT_SUPPORTED_HA_VERSION = "You have version '{}' of Home Assistant, but version '{}' of '{}' require version '{}' of Home Assistant, install and upgrades are disabled for this integration untill you upgrade Home Assistant." - - -NO_ELEMENTS = "No elements to show, open the store to install some awesome stuff." diff --git a/custom_components/hacs/hacsbase/data.py b/custom_components/hacs/hacsbase/data.py index 1464849..e1f62a1 100644 --- a/custom_components/hacs/hacsbase/data.py +++ b/custom_components/hacs/hacsbase/data.py @@ -1,152 +1,203 @@ -"""Data handler for HACS.""" -from integrationhelper import Logger -from ..const import VERSION -from ..repositories.repository import HacsRepository -from ..repositories.manifest import HacsManifest -from ..store import async_save_to_store, async_load_from_store - -from custom_components.hacs.globals import get_hacs, removed_repositories, get_removed -from custom_components.hacs.helpers.register_repository import register_repository - - -class HacsData: - """HacsData class.""" - - def __init__(self): - """Initialize.""" - self.logger = Logger("hacs.data") - self.hacs = get_hacs() - - async def async_write(self): - """Write content to the store files.""" - if self.hacs.system.status.background_task or self.hacs.system.disabled: - return - - self.logger.debug("Saving data") - - # Hacs - await async_save_to_store( - self.hacs.hass, - "hacs", - { - "view": self.hacs.configuration.frontend_mode, - "compact": self.hacs.configuration.frontend_compact, - "onboarding_done": self.hacs.configuration.onboarding_done, - }, - ) - - await async_save_to_store( - self.hacs.hass, "removed", [x.__dict__ for x in removed_repositories] - ) - - # Repositories - content = {} - for repository in self.hacs.repositories: - if repository.repository_manifest is not None: - repository_manifest = repository.repository_manifest.manifest - else: - repository_manifest = None - content[repository.information.uid] = { - "authors": repository.data.authors, - "category": repository.data.category, - "description": repository.data.description, - "downloads": repository.releases.downloads, - "full_name": repository.data.full_name, - "first_install": repository.status.first_install, - "hide": repository.status.hide, - "installed_commit": repository.versions.installed_commit, - "installed": repository.status.installed, - "last_commit": repository.versions.available_commit, - "last_release_tag": repository.versions.available, - "last_updated": repository.information.last_updated, - "name": repository.data.name, - "new": repository.status.new, - "repository_manifest": repository_manifest, - "selected_tag": repository.status.selected_tag, - "show_beta": repository.status.show_beta, - "stars": repository.data.stargazers_count, - "topics": repository.data.topics, - "version_installed": repository.versions.installed, - } - - await async_save_to_store(self.hacs.hass, "repositories", content) - self.hacs.hass.bus.async_fire("hacs/repository", {}) - self.hacs.hass.bus.fire("hacs/config", {}) - - async def restore(self): - """Restore saved data.""" - hacs = await async_load_from_store(self.hacs.hass, "hacs") - repositories = await async_load_from_store(self.hacs.hass, "repositories") - removed = await async_load_from_store(self.hacs.hass, "removed") - try: - if not hacs and not repositories: - # Assume new install - self.hacs.system.status.new = True - return True - self.logger.info("Restore started") - - # Hacs - self.hacs.configuration.frontend_mode = hacs.get("view", "Grid") - self.hacs.configuration.frontend_compact = hacs.get("compact", False) - self.hacs.configuration.onboarding_done = hacs.get("onboarding_done", False) - - for entry in removed: - removed_repo = get_removed(entry["repository"]) - removed_repo.update_data(entry) - - # Repositories - for entry in repositories: - repo = repositories[entry] - if not self.hacs.is_known(repo["full_name"]): - await register_repository( - repo["full_name"], repo["category"], False - ) - repository = self.hacs.get_by_name(repo["full_name"]) - if repository is None: - self.logger.error(f"Did not find {repo['full_name']}") - continue - - # Restore repository attributes - repository.information.uid = entry - await self.hacs.hass.async_add_executor_job( - restore_repository_data, repository, repo - ) - - self.logger.info("Restore done") - except Exception as exception: # pylint: disable=broad-except - self.logger.critical(f"[{exception}] Restore Failed!") - return False - return True - - -def restore_repository_data( - repository: type(HacsRepository), repository_data: dict -) -> None: - """Restore Repository Data""" - repository.data.authors = repository_data.get("authors", []) - repository.data.description = repository_data.get("description") - repository.releases.last_release_object_downloads = repository_data.get("downloads") - repository.information.last_updated = repository_data.get("last_updated") - repository.data.topics = repository_data.get("topics", []) - repository.data.stargazers_count = repository_data.get("stars", 0) - repository.releases.last_release = repository_data.get("last_release_tag") - repository.status.hide = repository_data.get("hide", False) - repository.status.installed = repository_data.get("installed", False) - repository.status.new = repository_data.get("new", True) - repository.status.selected_tag = repository_data.get("selected_tag") - repository.status.show_beta = repository_data.get("show_beta", False) - repository.versions.available = repository_data.get("last_release_tag") - repository.versions.available_commit = repository_data.get("last_commit") - repository.versions.installed = repository_data.get("version_installed") - repository.versions.installed_commit = repository_data.get("installed_commit") - - repository.repository_manifest = HacsManifest.from_dict( - repository_data.get("repository_manifest", {}) - ) - - if repository.status.installed: - repository.status.first_install = False - - if repository_data["full_name"] == "hacs/integration": - repository.versions.installed = VERSION - repository.status.installed = True +"""Data handler for HACS.""" +import os +from queueman import QueueManager + +from custom_components.hacs.const import VERSION +from custom_components.hacs.helpers.classes.manifest import HacsManifest +from custom_components.hacs.helpers.functions.logger import getLogger +from custom_components.hacs.helpers.functions.register_repository import ( + register_repository, +) +from custom_components.hacs.helpers.functions.store import ( + async_load_from_store, + async_save_to_store, + get_store_for_key, +) +from custom_components.hacs.share import get_hacs + + +class HacsData: + """HacsData class.""" + + def __init__(self): + """Initialize.""" + self.logger = getLogger() + self.hacs = get_hacs() + self.queue = QueueManager() + self.content = {} + + async def async_write(self): + """Write content to the store files.""" + if self.hacs.status.background_task or self.hacs.system.disabled: + return + + self.logger.debug("Saving data") + + # Hacs + await async_save_to_store( + self.hacs.hass, + "hacs", + { + "view": self.hacs.configuration.frontend_mode, + "compact": self.hacs.configuration.frontend_compact, + "onboarding_done": self.hacs.configuration.onboarding_done, + }, + ) + + # Repositories + self.content = {} + for repository in self.hacs.repositories or []: + self.queue.add(self.async_store_repository_data(repository)) + + if not self.queue.has_pending_tasks: + self.logger.debug("Nothing in the queue") + elif self.queue.running: + self.logger.debug("Queue is already running") + else: + await self.queue.execute() + await async_save_to_store(self.hacs.hass, "repositories", self.content) + self.hacs.hass.bus.async_fire("hacs/repository", {}) + self.hacs.hass.bus.fire("hacs/config", {}) + + async def async_store_repository_data(self, repository): + repository_manifest = repository.repository_manifest.manifest + data = { + "authors": repository.data.authors, + "category": repository.data.category, + "description": repository.data.description, + "domain": repository.data.domain, + "downloads": repository.data.downloads, + "full_name": repository.data.full_name, + "first_install": repository.status.first_install, + "installed_commit": repository.data.installed_commit, + "installed": repository.data.installed, + "last_commit": repository.data.last_commit, + "last_release_tag": repository.data.last_version, + "last_updated": repository.data.last_updated, + "name": repository.data.name, + "new": repository.data.new, + "repository_manifest": repository_manifest, + "selected_tag": repository.data.selected_tag, + "show_beta": repository.data.show_beta, + "stars": repository.data.stargazers_count, + "topics": repository.data.topics, + "version_installed": repository.data.installed_version, + } + if data: + if repository.data.installed and ( + repository.data.installed_commit or repository.data.installed_version + ): + await async_save_to_store( + self.hacs.hass, + f"hacs/{repository.data.id}.hacs", + repository.data.to_json(), + ) + self.content[str(repository.data.id)] = data + + async def restore(self): + """Restore saved data.""" + hacs = await async_load_from_store(self.hacs.hass, "hacs") + repositories = await async_load_from_store(self.hacs.hass, "repositories") + try: + if not hacs and not repositories: + # Assume new install + self.hacs.status.new = True + return True + self.logger.info("Restore started") + self.hacs.status.new = False + + # Hacs + self.hacs.configuration.frontend_mode = hacs.get("view", "Grid") + self.hacs.configuration.frontend_compact = hacs.get("compact", False) + self.hacs.configuration.onboarding_done = hacs.get("onboarding_done", False) + + # Repositories + stores = {} + for entry in repositories or []: + stores[entry] = get_store_for_key(self.hacs.hass, f"hacs/{entry}.hacs") + + stores_exist = {} + + def _populate_stores(): + for entry in repositories or []: + stores_exist[entry] = os.path.exists(stores[entry].path) + + await self.hacs.hass.async_add_executor_job(_populate_stores) + + # Repositories + for entry in repositories or []: + self.queue.add( + self.async_restore_repository( + entry, repositories[entry], stores[entry], stores_exist[entry] + ) + ) + + await self.queue.execute() + + self.logger.info("Restore done") + except (Exception, BaseException) as exception: # pylint: disable=broad-except + self.logger.critical(f"[{exception}] Restore Failed!") + return False + return True + + async def async_restore_repository( + self, entry, repository_data, store, store_exists + ): + if not self.hacs.is_known(entry): + await register_repository( + repository_data["full_name"], repository_data["category"], False + ) + repository = [ + x + for x in self.hacs.repositories + if str(x.data.id) == str(entry) + or x.data.full_name == repository_data["full_name"] + ] + if not repository: + self.logger.error(f"Did not find {repository_data['full_name']} ({entry})") + return + + repository = repository[0] + + # Restore repository attributes + repository.data.id = entry + repository.data.authors = repository_data.get("authors", []) + repository.data.description = repository_data.get("description") + repository.releases.last_release_object_downloads = repository_data.get( + "downloads" + ) + repository.data.last_updated = repository_data.get("last_updated") + repository.data.topics = repository_data.get("topics", []) + repository.data.domain = repository_data.get("domain", None) + repository.data.stargazers_count = repository_data.get("stars", 0) + repository.releases.last_release = repository_data.get("last_release_tag") + repository.data.hide = repository_data.get("hide", False) + repository.data.installed = repository_data.get("installed", False) + repository.data.new = repository_data.get("new", True) + repository.data.selected_tag = repository_data.get("selected_tag") + repository.data.show_beta = repository_data.get("show_beta", False) + repository.data.last_version = repository_data.get("last_release_tag") + repository.data.last_commit = repository_data.get("last_commit") + repository.data.installed_version = repository_data.get("version_installed") + repository.data.installed_commit = repository_data.get("installed_commit") + + repository.repository_manifest = HacsManifest.from_dict( + repository_data.get("repository_manifest", {}) + ) + + if repository.data.installed: + repository.status.first_install = False + + if repository_data["full_name"] == "hacs/integration": + repository.data.installed_version = VERSION + repository.data.installed = True + + restored = store_exists and await store.async_load() or {} + + if restored: + repository.data.update_data(restored) + if not repository.data.installed: + repository.logger.debug( + "Should be installed but is not... Fixing that!" + ) + repository.data.installed = True diff --git a/custom_components/hacs/hacsbase/hacs.py b/custom_components/hacs/hacsbase/hacs.py new file mode 100644 index 0000000..ba06d62 --- /dev/null +++ b/custom_components/hacs/hacsbase/hacs.py @@ -0,0 +1,361 @@ +"""Initialize the HACS base.""" +import json +from datetime import timedelta + +from aiogithubapi import AIOGitHubAPIException +from queueman import QueueManager +from queueman.exceptions import QueueManagerExecutionStillInProgress + +from custom_components.hacs.helpers import HacsHelpers + +from custom_components.hacs.helpers.functions.get_list_from_default import ( + async_get_list_from_default, +) +from custom_components.hacs.helpers.functions.register_repository import ( + register_repository, +) +from custom_components.hacs.helpers.functions.remaining_github_calls import ( + get_fetch_updates_for, +) +from custom_components.hacs.helpers.functions.store import ( + async_load_from_store, + async_save_to_store, +) +from custom_components.hacs.operational.setup_actions.categories import ( + async_setup_extra_stores, +) +from custom_components.hacs.share import ( + get_factory, + get_queue, + get_removed, + is_removed, + list_removed_repositories, +) + +from ..enums import HacsCategory, HacsStage +from ..base import HacsBase + + +class HacsStatus: + """HacsStatus.""" + + startup = True + new = False + background_task = False + reloading_data = False + upgrading_all = False + + +class HacsFrontend: + """HacsFrontend.""" + + version_running = None + version_available = None + version_expected = None + update_pending = False + + +class HacsCommon: + """Common for HACS.""" + + categories = [] + default = [] + installed = [] + skip = [] + + +class System: + """System info.""" + + status = HacsStatus() + config_path = None + ha_version = None + disabled = False + running = False + lovelace_mode = "storage" + + +class Hacs(HacsBase, HacsHelpers): + """The base class of HACS, nested throughout the project.""" + + repositories = [] + repo = None + data_repo = None + data = None + status = HacsStatus() + configuration = None + version = None + session = None + factory = get_factory() + queue = get_queue() + recuring_tasks = [] + common = HacsCommon() + + def get_by_id(self, repository_id): + """Get repository by ID.""" + try: + for repository in self.repositories: + if str(repository.data.id) == str(repository_id): + return repository + except (Exception, BaseException): # pylint: disable=broad-except + pass + return None + + def get_by_name(self, repository_full_name): + """Get repository by full_name.""" + try: + repository_full_name_lower = repository_full_name.lower() + for repository in self.repositories: + if repository.data.full_name_lower == repository_full_name_lower: + return repository + except (Exception, BaseException): # pylint: disable=broad-except + pass + return None + + def is_known(self, repository_id): + """Return a bool if the repository is known.""" + return str(repository_id) in [str(x.data.id) for x in self.repositories] + + @property + def sorted_by_name(self): + """Return a sorted(by name) list of repository objects.""" + return sorted(self.repositories, key=lambda x: x.display_name) + + @property + def sorted_by_repository_name(self): + """Return a sorted(by repository_name) list of repository objects.""" + return sorted(self.repositories, key=lambda x: x.data.full_name) + + async def register_repository(self, full_name, category, check=True): + """Register a repository.""" + await register_repository(full_name, category, check=check) + + async def startup_tasks(self, _event=None): + """Tasks that are started after startup.""" + await self.async_set_stage(HacsStage.STARTUP) + self.status.background_task = True + await async_setup_extra_stores() + self.hass.bus.async_fire("hacs/status", {}) + + await self.handle_critical_repositories_startup() + await self.handle_critical_repositories() + await self.async_load_default_repositories() + await self.clear_out_removed_repositories() + + self.recuring_tasks.append( + self.hass.helpers.event.async_track_time_interval( + self.recurring_tasks_installed, timedelta(minutes=30) + ) + ) + self.recuring_tasks.append( + self.hass.helpers.event.async_track_time_interval( + self.recurring_tasks_all, timedelta(minutes=800) + ) + ) + self.recuring_tasks.append( + self.hass.helpers.event.async_track_time_interval( + self.prosess_queue, timedelta(minutes=10) + ) + ) + + self.hass.bus.async_fire("hacs/reload", {"force": True}) + await self.recurring_tasks_installed() + + await self.prosess_queue() + + self.status.startup = False + self.status.background_task = False + self.hass.bus.async_fire("hacs/status", {}) + await self.async_set_stage(HacsStage.RUNNING) + + async def handle_critical_repositories_startup(self): + """Handled critical repositories during startup.""" + alert = False + critical = await async_load_from_store(self.hass, "critical") + if not critical: + return + for repo in critical: + if not repo["acknowledged"]: + alert = True + if alert: + self.log.critical("URGENT!: Check the HACS panel!") + self.hass.components.persistent_notification.create( + title="URGENT!", message="**Check the HACS panel!**" + ) + + async def handle_critical_repositories(self): + """Handled critical repositories during runtime.""" + # Get critical repositories + critical_queue = QueueManager() + instored = [] + critical = [] + was_installed = False + + try: + critical = await self.data_repo.get_contents("critical") + critical = json.loads(critical.content) + except AIOGitHubAPIException: + pass + + if not critical: + self.log.debug("No critical repositories") + return + + stored_critical = await async_load_from_store(self.hass, "critical") + + for stored in stored_critical or []: + instored.append(stored["repository"]) + + stored_critical = [] + + for repository in critical: + removed_repo = get_removed(repository["repository"]) + removed_repo.removal_type = "critical" + repo = self.get_by_name(repository["repository"]) + + stored = { + "repository": repository["repository"], + "reason": repository["reason"], + "link": repository["link"], + "acknowledged": True, + } + if repository["repository"] not in instored: + if repo is not None and repo.installed: + self.log.critical( + "Removing repository %s, it is marked as critical", + repository["repository"], + ) + was_installed = True + stored["acknowledged"] = False + # Remove from HACS + critical_queue.add(repository.uninstall()) + repo.remove() + + stored_critical.append(stored) + removed_repo.update_data(stored) + + # Uninstall + await critical_queue.execute() + + # Save to FS + await async_save_to_store(self.hass, "critical", stored_critical) + + # Restart HASS + if was_installed: + self.log.critical("Resarting Home Assistant") + self.hass.async_create_task(self.hass.async_stop(100)) + + async def prosess_queue(self, _notarealarg=None): + """Recurring tasks for installed repositories.""" + if not self.queue.has_pending_tasks: + self.log.debug("Nothing in the queue") + return + if self.queue.running: + self.log.debug("Queue is already running") + return + + can_update = await get_fetch_updates_for(self.github) + if can_update == 0: + self.log.info("HACS is ratelimited, repository updates will resume later.") + else: + self.status.background_task = True + self.hass.bus.async_fire("hacs/status", {}) + try: + await self.queue.execute(can_update) + except QueueManagerExecutionStillInProgress: + pass + self.status.background_task = False + self.hass.bus.async_fire("hacs/status", {}) + + async def recurring_tasks_installed(self, _notarealarg=None): + """Recurring tasks for installed repositories.""" + self.log.debug("Starting recurring background task for installed repositories") + self.status.background_task = True + self.hass.bus.async_fire("hacs/status", {}) + + for repository in self.repositories: + if ( + repository.data.installed + and repository.data.category in self.common.categories + ): + self.queue.add(self.factory.safe_update(repository)) + + await self.handle_critical_repositories() + self.status.background_task = False + self.hass.bus.async_fire("hacs/status", {}) + await self.data.async_write() + self.log.debug("Recurring background task for installed repositories done") + + async def recurring_tasks_all(self, _notarealarg=None): + """Recurring tasks for all repositories.""" + self.log.debug("Starting recurring background task for all repositories") + await async_setup_extra_stores() + self.status.background_task = True + self.hass.bus.async_fire("hacs/status", {}) + + for repository in self.repositories: + if repository.data.category in self.common.categories: + self.queue.add(self.factory.safe_common_update(repository)) + + await self.async_load_default_repositories() + await self.clear_out_removed_repositories() + self.status.background_task = False + await self.data.async_write() + self.hass.bus.async_fire("hacs/status", {}) + self.hass.bus.async_fire("hacs/repository", {"action": "reload"}) + self.log.debug("Recurring background task for all repositories done") + + async def clear_out_removed_repositories(self): + """Clear out blaclisted repositories.""" + need_to_save = False + for removed in list_removed_repositories(): + repository = self.get_by_name(removed.repository) + if repository is not None: + if repository.data.installed and removed.removal_type != "critical": + self.log.warning( + f"You have {repository.data.full_name} installed with HACS " + + "this repository has been removed, please consider removing it. " + + f"Removal reason ({removed.removal_type})" + ) + else: + need_to_save = True + repository.remove() + + if need_to_save: + await self.data.async_write() + + async def async_load_default_repositories(self): + """Load known repositories.""" + self.log.info("Loading known repositories") + + for item in await async_get_list_from_default(HacsCategory.REMOVED): + removed = get_removed(item["repository"]) + removed.reason = item.get("reason") + removed.link = item.get("link") + removed.removal_type = item.get("removal_type") + + for category in self.common.categories or []: + self.queue.add(self.async_get_category_repositories(HacsCategory(category))) + + await self.prosess_queue() + + async def async_get_category_repositories(self, category: HacsCategory): + """Get repositories from category.""" + repositories = await async_get_list_from_default(category) + for repo in repositories: + if is_removed(repo): + continue + repository = self.get_by_name(repo) + if repository is not None: + if str(repository.data.id) not in self.common.default: + self.common.default.append(str(repository.data.id)) + else: + continue + continue + self.queue.add(self.factory.safe_register(repo, category)) + + async def async_set_stage(self, stage: str) -> None: + """Set the stage of HACS.""" + self.stage = HacsStage(stage) + self.log.info("Stage changed: %s", self.stage) + self.hass.bus.async_fire("hacs/stage", {"stage": self.stage}) diff --git a/custom_components/hacs/hacsbase/task_factory.py b/custom_components/hacs/hacsbase/task_factory.py deleted file mode 100644 index 8b32aa9..0000000 --- a/custom_components/hacs/hacsbase/task_factory.py +++ /dev/null @@ -1,75 +0,0 @@ -# pylint: disable=missing-docstring,invalid-name -import logging -import time -from datetime import timedelta -import asyncio -from aiogithubapi import AIOGitHubException - -from custom_components.hacs.hacsbase.exceptions import HacsException -from custom_components.hacs.helpers.register_repository import register_repository - - -max_concurrent_tasks = asyncio.Semaphore(15) -sleeper = 5 - -logger = logging.getLogger("hacs.factory") - - -class HacsTaskFactory: - def __init__(self): - self.tasks = [] - self.running = False - - async def execute(self): - if not self.tasks: - logger.debug("No tasks to execute") - return - if self.running: - logger.debug("Allready executing tasks") - return - try: - self.running = True - logger.info("Processing %s tasks", len(self.tasks)) - start = time.time() - await asyncio.gather(*self.tasks) - logger.info( - "Task processing of %s tasks completed in %s seconds", - len(self.tasks), - timedelta(seconds=round(time.time() - start)).seconds, - ) - self.tasks = [] - self.running = False - except RuntimeError: - logger.warning("RuntimeError, Clearing current tasks") - self.tasks = [] - self.running = False - - async def safe_common_update(self, repository): - async with max_concurrent_tasks: - try: - await repository.common_update() - except (AIOGitHubException, HacsException) as exception: - logger.error("%s - %s", repository.data.full_name, exception) - - # Due to GitHub ratelimits we need to sleep a bit - await asyncio.sleep(sleeper) - - async def safe_update(self, repository): - async with max_concurrent_tasks: - try: - await repository.update_repository() - except (AIOGitHubException, HacsException) as exception: - logger.error("%s - %s", repository.data.full_name, exception) - - # Due to GitHub ratelimits we need to sleep a bit - await asyncio.sleep(sleeper) - - async def safe_register(self, repo, category): - async with max_concurrent_tasks: - try: - await register_repository(repo, category) - except (AIOGitHubException, HacsException) as exception: - logger.error("%s - %s", repo, exception) - - # Due to GitHub ratelimits we need to sleep a bit - await asyncio.sleep(sleeper) diff --git a/custom_components/hacs/handler/__init__.py b/custom_components/hacs/handler/__init__.py deleted file mode 100644 index ab064c7..0000000 --- a/custom_components/hacs/handler/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Initialize handlers.""" diff --git a/custom_components/hacs/handler/__pycache__/__init__.cpython-37.pyc b/custom_components/hacs/handler/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 50f46496dd03d0e523851f879407f56406657aa4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmZ?b<>g`kf{*9x;$(pIV-N=h7=a82ATH(r5-AK(3@MDk44O<;BA$7fC7FpinN_I@ z8Hsr*IjKd(dVZRWx7g$3Q}UDJ<5w~iF#)xJiC;$g$@zI{nd$n;rNt%rx$(*Qxdr)o rsd**E`WcDIAOdWHetdi;(AfBRy@JYH95%W6DWy57b|5Ew24V&P?qw~Z diff --git a/custom_components/hacs/handler/__pycache__/download.cpython-37.pyc b/custom_components/hacs/handler/__pycache__/download.cpython-37.pyc deleted file mode 100644 index e2b77c7285dab58bf6687ae91e98706d72ae00eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2115 zcmZWq&yO256t+D-lT5PN>~>iSlu%AVG*Jr&s47&I0*izywLMUBAexMw>|`h7U_0#Y zFw;Y~*QymqDx{=0{thnOxch{}X^&icf#*q5fpFw!zvt&?`_1>g+^W~B2-;u2Km4{2 zvpOZk)g;pPOCP(Z({_Szo_JBiN6G&160_#J*iUvh=@nIy{IkG+HR#S70}9} zZq~t{oZ(0KYuIxa-9ryhZb34t+0|+r+9o6IQJgUOemhKQTQD^jXI3MeJC{NR9pyDaxbEUn_#6AqA z6b6*qlT1qZlG=jrsNL1M+7>LN!Zr@>h>K~cI*zvHBGIrllG;s5P-s^$IZq(6VBgJ| zRKFnc25fqHtp|A-#p7Nymx@mZ5ueU@3VD(}vmZoC6DE3_hjeGBdS`XnjcH)=y#>K! z8w7G(?8DEtd>oJ_vB@rOSykL51lxGe7H@%1WRhT71G;%#iMJmCg$gKAB8@D7UjIUp zRRG2cpiSW@l4E6^5qX`uVD-T270xq!+Avtl0TsEOJ3t@%LxUsl0R42SaC3J7C@c{N z7Qmpp@Rl$Fn!LMfKZ7adPbv$8H7x!BY}aUIiRFuORy8&SC(zm%dD<;J6)cPvyoRW| zBu`s~uj-3N?&bcHY$BVOt24}u)(vZ(VR2AKY%KA5zD2i|m^NQW1@L0AMYlm~fmY3{ z&k^0uE5IqMsFk;C=C$=cve>3Od2PctV>>svh)J5*(-vF!2|RSO36A0O_KxQ*Irh;AAZkPwCZN9|1w9yQs#k!LA=Jt znKM_TD<5e$9;aL|O^%0mI+nJlKoIS-6o>+z+GRp;Av3Exk(u3%q|`(VK^1-ed| z%$)9NI89#S-ud6qGQYR7_k^id+5SB?<>je7p@!kp)fL;>+n@3|m{MTpv4|CG(@=$m zjlj<h@-2qj8kA!3T(8b650=AUX~DT^BGIEQwGsalyC{};ZobPP#wYx zrExQlqVhNfi#^Wb8Bp_ZuHr=3B0f7c9(*{_j+`Yi(3YLVQbB|`9dScScmmUvS7lmU zFjsQQMwm8n6;?8r#;2+2)&;tiB3Hc@qR8K5hbk;YX_L(P(>^fPLQz z@GcH;3%9L3+*tpbxItQAt>P9Dw;)ERrM9HtZ zeaMm-q+(SuhhcQYMWt%@YxhjB{{#V1gC054?ABKr6%dZZ2I(W5F+w5e< u#mDA!Qe#hYkFxBB03Ys7$J2X{d_5TS5Uf8xwx51F2>r6jeh~=HV5=7Z90iDD zPBOeC0l_wQGG|Ex3NhvG0}5R3U7|^6eR^IgT`b31EXyp^VyvpNETo?PhB;`0P4`3l z1!C`A5ywJC_Gfmvuq~x?*y;hG#wkiM$2Yz_-Nh-{keqVB)m%F@y2l%*c2Wvo-P8l@ z)TD{KO}`J=gyBT=ky+b9n|z2#;8V@AZI)~2jjq%d1r z6Qy<3c(S2wJfNm4B`FkxqyO|`8G}k0&M>{!eU+cgiH3mRVANL7$2<~EvJ=g@$!#zUrF&<)h0zDUSOC82|5iw@GG|AI& tzO!kRz(s-=R@Ak~N+BC*&8dd%BN)LX(|x%tc$JA)7&2x3qXZZ@{{Z&}?Ee4& diff --git a/custom_components/hacs/handler/download.py b/custom_components/hacs/handler/download.py deleted file mode 100644 index 489c99d..0000000 --- a/custom_components/hacs/handler/download.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Download.""" -import os -import gzip -import shutil - -import aiofiles -import async_timeout -from integrationhelper import Logger -import backoff -from ..hacsbase.exceptions import HacsException - -from custom_components.hacs.globals import get_hacs - - -@backoff.on_exception(backoff.expo, Exception, max_tries=5) -async def async_download_file(url): - """ - Download files, and return the content. - """ - hacs = get_hacs() - logger = Logger("hacs.download.downloader") - if url is None: - return - - # There is a bug somewhere... TODO: Find that bug.... - if "tags/" in url: - url = url.replace("tags/", "") - - logger.debug(f"Downloading {url}") - - result = None - - with async_timeout.timeout(60, loop=hacs.hass.loop): - request = await hacs.session.get(url) - - # Make sure that we got a valid result - if request.status == 200: - result = await request.read() - else: - raise HacsException( - "Got status code {} when trying to download {}".format( - request.status, url - ) - ) - - return result - - -async def async_save_file(location, content): - """Save files.""" - logger = Logger("hacs.download.save") - logger.debug(f"Saving {location}") - mode = "w" - encoding = "utf-8" - errors = "ignore" - - if not isinstance(content, str): - mode = "wb" - encoding = None - errors = None - - try: - async with aiofiles.open( - location, mode=mode, encoding=encoding, errors=errors - ) as outfile: - await outfile.write(content) - outfile.close() - - # Create gz for .js files - if os.path.isfile(location): - if location.endswith(".js") or location.endswith(".css"): - with open(location, "rb") as f_in: - with gzip.open(location + ".gz", "wb") as f_out: - shutil.copyfileobj(f_in, f_out) - - # Remove with 2.0 - if "themes" in location and location.endswith(".yaml"): - filename = location.split("/")[-1] - base = location.split("/themes/")[0] - combined = f"{base}/themes/{filename}" - if os.path.exists(combined): - logger.info(f"Removing old theme file {combined}") - os.remove(combined) - - except Exception as error: # pylint: disable=broad-except - msg = "Could not write data to {} - {}".format(location, error) - logger.error(msg) - return False - - return os.path.exists(location) diff --git a/custom_components/hacs/helpers/__init__.py b/custom_components/hacs/helpers/__init__.py new file mode 100644 index 0000000..8c5755c --- /dev/null +++ b/custom_components/hacs/helpers/__init__.py @@ -0,0 +1,17 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +from custom_components.hacs.helpers.methods import ( + HacsHelperMethods, + RepositoryHelperMethods, +) +from custom_components.hacs.helpers.properties import RepositoryHelperProperties + + +class RepositoryHelpers( + RepositoryHelperMethods, + RepositoryHelperProperties, +): + """Helper class for repositories""" + + +class HacsHelpers(HacsHelperMethods): + """Helper class for HACS""" diff --git a/custom_components/hacs/helpers/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/helpers/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac59aa04c2926340670286561134f3298f40a6e2 GIT binary patch literal 719 zcmb7BJ4*vW5Z>L}Jd6owA%Y;J60XUCwTO?xG)6JlY%_%0)g0X2p1YSqTP^(~`7!>1 zYpty8EyUTo#6(kw13S!vnfbnNuREOqbKUJF(iKT4fhbA6`TE0X!|P8lsQ-V zSW8=MU#U#nDA(rxN5iMZ&hOTx(Kwk$%7$*?vLTHM@&*Qq2r7wSk_t~U1mG2$zjxV( z-RfR9j7QdXZ?x%}ITXAwnDU|tkd6`sP_zINEhn*y+W>cyQ9SE30Hh89ro^n{>C%1d z2cb@HqT3*x*jy(N>Lk;tN^={GQGsAwN?-$kD2;M}UUqMs=xg{aL)YYG{%@M=3xVy! z)(XM?@#%12>T{e6#HQ_P9du5=3IZ+jf!`2&D>QnJMsG%=msEdg9CG8K6aF6)+4oNx j?uH!s^4t=nQcj1fXdYDJAG!n9l{R?WYf(noCvSWKX9%?_ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/__pycache__/download.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/download.cpython-37.pyc deleted file mode 100644 index d36671205fc0cacc0222adbc1229257e81fccc9b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4121 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 diff --git a/custom_components/hacs/helpers/__pycache__/get_defaults.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/get_defaults.cpython-37.pyc deleted file mode 100644 index 4f7ec21af8a53f51ba1170c78460b8db76127242..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1399 zcmZXUJ#X7a7{~8;#}~=6A-k@f7D1!Ii;7wTGGr=>G-weADBPigQvr>@@=g+MlE>Vg zfJmS+)EW9AJji1|M?XOKTRU~^+NsY&%7Tp?z=!v)=lTDC)QjESHi7o=>_=Nn8 zgS*C|^Bf-Y27(|XBB<~(I->AqnU}K>%lU{?oadRJ2cw|w`&pPrqlgmOlI)tww%ifH z4HKb=KJZbe9=C2h(H1)}-WA?Yq~BSdoXM<|+VrgIO{MJ#If>_)?P*ymlUk)yX$Jp5 zCgi;Dxz@L@etenQv-z*?VB7uvT_Q`Hs-jO_@PnF8rH0-f+&13cvgk7xOjA39lTi{| zIaPY$0xj)a7m0`WLyO=+Vg7{2{0?DCR-T1Iujz^jPcXrSf5TS1q7}ai^+^RqES^?m z4daS_MsDdVB7!rr^4~qG{O`yw-BnO|Yp)6}*)65y?ehzAL5i<9vHYs7f1_j-QnK8u z*xIi`+;IWtMX(Crhp)-cr0`x7{EdFN{NkmwW-E`<(}xl{K8q8x>?CtzReqYpTA1bj z-ALu7Dr8~J^2to)@-#LkH8w8nX`=FcUZi$$YG*Q+26Za4`7|xuPFjFYI!0-oUoW5c z1C4yRT~rs6XcTrqT$ZvBt~Hrw*|>;vsTpqbrPfOKBOSmWCASX4g1;MJJmIIwKZ0S^Bb0Na2O02r5BF!(y$g24oP00sjv`0}I%#PLgmo@Xi+#)X^d+EU$x-Tlag zMgl`M80tzE`Vro=kKq8rCm2v$eTV_=Yr+xq-0{bTV3y_yEE+t2=g0x=(r)7euJ!j^ zI35d?jK}(O7`slKs^+VX%T(iM+;F#l58Z}}2HP(`B4>b-3^wEp(qf`?UK8KQ!tJ7z rjv1>R7inT^95m)~uco@Xy0GZ$T#0!mzoD?hbRYm(P(z1y*wOL-*tdPX diff --git a/custom_components/hacs/helpers/__pycache__/information.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/information.cpython-37.pyc deleted file mode 100644 index af7ad8b9735666bc177cda48807d2915ab30339d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5163 zcmcgwO>Y~=8Q$4%QY1yovSP=HyiJ-oOl>L+QWOP*KyhS8X=^1-fF@<(5Npm#BDLJ5 zXIF{1SnAN^2c*3^oMS6|xaon5X-CJuTEiTT)fW@qMo z-p|=v^?Fso??3-I`QWdY73E*_F#T!hY@tM-q2fwM;f$+3>oByMuMV}2Hq<*hBf9Pz zL$hPbzTsO#yJMqoa_bYNQ{gtRpsn&MUqZXYYrKxO#vR_|7d~N~I$z-zL22++ehKX| zU*k`pZSu?f3fc?&Nq!aW3V(_}jrJm6=hx7#a`j!M^@HTOcf9yW1di}VVbqU9F?RaF zUMPlc+z$h%zvsBYxcxQQPA}8GL=DV8`pHHw4EFl_8@;0_4u{=dI2?t67sS!V0r1;6@cfY{qKz5pv`6Et zLeE`V%qARHP${a-s`%SXFIv$6wFI6(ub})LB^sfM0sY5};EtLcK#y+$c6O>t__Z_j zO~AgROw?56>Y*)Op*g8K&`%Anas5n1YjE>Sov5*ys#ps`(v{eX?LGCdDh5oUKfU$7 z6ky9ro_qrWiVr*oOvhl-3HPMDfT=KXq}@c{j6U2?Dx16?x%YjKC&uOmQL398Q&OuU z8Otu5KS)t~@?P_7+in(+2At9Z;TO!wWMCvO@PmQZ{TD5mux}XzfHt|CMN+g5?*noJc_45YokOmD1fnoEu!D~nounjfF4Tb?%-j(mNN&zldX~9p$H{=TVM~N0)kgl zeTF^#L| zDcN7irAqYD@ytne9dV%W_zh?=3!3EX2iidY98Th)O74i<%O^C;*oK2Tw8X90d{$`6D%P=64eM0$bzn;t zwJdf6RBVURR7=AKLFr@{7J zk`V=oeXw+|N+1uE->VPcW~4?VTvw!hkKFhGAxmw)l(FLlWq(xN^!rgv5$aXa--uQ> zmE=b58p~sL%d>3yVbArWSKH?=Cd<2_jLyzpcocBw`XhFIH(7r@Jo32{gfZ;Xew=i+uvGK{DvDmF+GMM6UMe$L6a6N82F}c6HTiF7O%?I% zXP8^67UgrK0I#FSbb_p4fXH;HiZ!?_ST0k7Ul&vr)EkIkId!X`YM|bV^@93hLDfNh zKQ;;~B7j`i0M$pLAy?og?8Hj0zEPwT)2zygLnn3xSZ-(L$UoYLk}SDwv~a{?owI#5(Sj;h;fmS5hPXr{hS-Z$q@y0U~JGE15 zVB(!G(ZjjA6;AtB2TR@B+scD4-%}o(9KS7R?_jne{*65 zE*uCjruZtgPwTvL#tP?-HB%^Hx#m~2CW1vBYS;KqwUcQq<(e|~;&uS9l)$Dx^eFed znP}}nlvwSfA*W9D=6yIp-$P%JK|Nv{rL6vc-z_M$YN_HmG~#)x<`qU<2kj3iQ;+?^ zg3d`pfs_7*xBmLS`0chDx%Yb(OJr7_QAW~C1m!+?(tLw@7ioqGU+l#ZBFfZgq(2oK zG~*?jVMHUpA0v+^cXqyZX9bthsE@S2OGlwwX4F~!(DnPdwv)GyGJ85ECH&~MA;PaB8W0~CK{1v z+CnEk)A1{$;`eUQB~D)Lq=65NGbU=1V#0LuzJ)jJZskF(xJ_%{spg5%9=k(-w^hv< zAq~u`2*4taKEmx_QQ|f}599;#hDClz{1VkH-_^S3bbPbkIp!Bne7Xc9Cs z2g3I*7()Edbhc73M43C&_bM0-p9ki|BHqS%7UUs*MYP9@LcBxc3+A*9+LtKblmd;k z)H~uDwU;uh+vQ=e+Z8{?b5?WvVSZtBNB#Wfp@H7H{*5mlZTjMYiaerdPd_Ee`+D}` zV`ulG`?%$_%WG(qXKD29L*BsU6;oc+ghMpSVq{d5RxQm)s*xbM31Vf2aS4I^0AOzp nLw@9YxOD;o2bB&#(IhXT*>!u_uG*Dq#a_1?_D$Qgmu&NY#h=@q diff --git a/custom_components/hacs/helpers/__pycache__/install.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/install.cpython-37.pyc deleted file mode 100644 index 98744e53482f37eed992c1378a98f5ce7bbcee02..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3194 zcmai0L5~}^6&`Y=(MT(4*PFGyjZ;@~8Yc=IZPFM)3lv72Yyju71sbDC1vG=%AtjA= zG^3ECwO5dPsDqwz@S(S)fc8?L|DedBmtG6>&TD`GIX1_f`aRA_E5r^eVaUhF_sGY` z@4d%&TdjtJ-+%w}$4@5DInLKKSp7JNckt;1jd1#oV1g&C&uq{89KD;klS;qBXs(h} zCtlyPbTz3>{JuY__v?1-C5_2ee+zU?_@6obrl^Ys`j*%dP4sQi5^eO`f`90Ax96P? zQk{oM(izEQDphBYsg9D!S_};j7!qp+sfuA;oyWn3qf3+jb*2fKX zYNfVB1#+i()9SJXTei-zL;5>A+gAV1x5qonOBn5rcgD|s;Rx)4)!SIjJ!hETd-hy@ zd3ojB5#CkjF^B!-x{#&4zq2}5^>-KAu-sj^#qM^8{U=Plus(}xN6zvm3+J4z{E)Wu z=gIEX7$%U@bDv}Gx}5|1PU_J68%|V6vvP*^{@D|*-hh@j?x1~fd}G`=sX_Xif+uh%<;c!0 zUn=-ym+(Q?g9h54k6)%XxVb(($GW=$C;+#S`j!tWe`WLZ`KvS{yaU7a`K|Y|39#0Q z3n+(#$&SwDRClsLCm+cRM4$DJ=6COdb1Ic4F$mntCbDx^>llHM=ABq~(k$;p*<^~9 zjuY8=5a%N-gNagWUd_XyJ}~uE=ECCqdv-9tc0kb)Ac|-y06h?~k`dzQ>|nk_a|ba3 zV5qEt_q$zlX*LyME`xQzof-c$Ok!dhPb!ruZMLE?4a%b_7olQyOEZEzTTPe>bVAb> zauCjvJUCWi8jXyrWGV3s678cw9CkW*p3y?*0`)2U2xxY9k^?1ZOb zoP@YTP>Xtzj**p4dUTJG{bQwcA+c*LV?JwepLy(g&e;{#=04xCa{FwTw|N8fE`t@I)^StT>AP>3wWh2oM`ZmM?J-<7xDemj)MZw2ct(%n*RC@MS< z9K^kaPB2Ka2kK?w?3a5y1tzmiz(=61_E0xT9^rv_@wHT^F+#^QLVcD-0U|ar^^5Aw zRF5_#z!uFfh`NR5H~91oG!Ab9UQG+CCcxKVdjJdpkm!8^56Q2=LoXU~!F%{L*^)bt z9jc8~wU#cuYfz`1ab;W;l`q|M4#-q-N#6X8yT$F~Sx3#%&V!LmZ3f?rM@rGFknGL3 zZDzd)%lKEwX{J^xbX~(^f!tu1wI@C5dWsBGchDI37^$)J%mDeixB>Eamluv9`+w-2 z*TEa&(-a~O0@8;k6mi!+pa7z6tPj2fQ9>D2Row&4$M6H_cM7_g1f%VIzRs}f5{;W0DL%@j2v8vlS8fyE2Xra*Ytx?2 zj+0oA3K2XepNYN}l&1Lfbu@&&CflLJW&Z_D<0h*xMY7!;Qwsu-MM0phV63j8G1s4p zlpcYlH%zkQFwqtT=CvOYBFSTz+>@(!uD0d3fAjw-l!bt*Vo_gT`7sUq=GMJcIkctF srePaS7Ektg8}PGXN)c-OUr#bIOXM9wn%+ZGDcwU3YP$aW{=Q%TA6XE3FaQ7m diff --git a/custom_components/hacs/helpers/__pycache__/misc.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/misc.cpython-37.pyc deleted file mode 100644 index 0c9b130961a2845f00e6482b749f47df83783c75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 980 zcmYjQzi$&U6t>TImmjnUHKA%HggmgE1POCgAp{a)p#wz>891(IU*g8GBil#SDq?{@ zqeCiC`}6bbr(Xo2-(mCLVgN7UvCqLV zG)4@wNa8W(5hE9v#Vq+Aj|q?97qj$&uoi1WBw^7J$~xz}Z@FA>L#tYqPH1IEbS`Xp z1#IXr!#*|K)kc*#1i-=?YQBcYeg?NhC+Gw-JnNYLH9iWl=!CFn7MT&?*o+tn>ny$A zGqR61S;=L};uU7eKDunNl(kk7tnFrPuOJJ~IC|flfx0QDs+e=Cs|Fjs&{jBY&S<3# ztu3cQHUCsbE5{WZ9Qu0#(8Ls>IUk4D*LZmK7i^86tuggD&pPAYEmuA-l&H9MCiLlJ zR=C2q%K})SlJDQ)yGiw(s+KYj>^`vzDV%Qsqby3;JK-dUQFd=@{6J<0$k&5msZ}K= z!?Lzc&+}5x7g}-U>~LC?;2Lr4Fi0|3ocZ2_yZn}>DVP9}4Qz;P;dZozQ{2T}vV+^$ z^dXuNPZU4(L`!BO2$aDE3+s)R2e5IVoWtKD(+7kDvU>~%rIr}9d=l=A17$DcC0gMP z!Ye$BK__zlZlhB{kF}OmRMhI)l%R^Ga00kxZd!mqg@HDn(o-QNEu_`-m{VtJ$g8Me z(7h-NIe7CSOMF*Yp{y$u43dB({(%;nnk z0Qtw8mvYG~mruoH%1sW}O63Ny+k+h2d}~o3$1uPcn@14KQs2sRrpr7x`++eeHpBIC eXthbYQ>&@k6l@>NHLE3m5xyInf&*G|zxNMuq!I-H diff --git a/custom_components/hacs/helpers/__pycache__/network.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/network.cpython-37.pyc deleted file mode 100644 index 79b69f206b5fa577b0cc69570b839ee8596d4b25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 431 zcmY*Vu};G<5RH>G4X6b%!_YMhiP#ZBup$-^Lk6N?yNT7rj?ONq9hmqT{-hgwRwhQ) zPTaMsDo(n0&(^zlr@I-C4?T?EulMH_)^B?3B0zGDY0nXamwSQHk|~U?bhuU{i)JU0)C))?_cm*@>xV1`3Rm5G2(r~fEG-mepnR3 y@S^A=Yxqn$AX$GxW)+l98NHnablXkye$6c#QbT;%8ZmF=6&=9RypWC9DEI@RMQ+Ie diff --git a/custom_components/hacs/helpers/__pycache__/register_repository.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/register_repository.cpython-37.pyc deleted file mode 100644 index 98476d0b9f2e4b6362f403527a4583d0ce16147b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1455 zcmZ`(OK&4Z5bmBw&)BgYC)tGEMM5L-TCb3O*$W5|3koDC+MqaMcYRKa?e$QPjDb<^jnIt9fJD~7f^&@$YW;)rIg+A4%#*FYs1)};) z=Z`Xz8U=cZF0^y}BQ9`Z%q;$OMhg5AQS(Y_6!;!kpW~%jXg_0qe~}jE%DO~{h>`;Y zp7p6ctxfCvj9P^`t+{oWH>M5#dlhljgmY`Ud5PD$ZSfo2ZLPMct>V+#gmnD0*S(e^LKB`^&d_Z4Opof6=4>veo`sI=m9=Vbrno8$iB>G8>P z_lu*){r-MGZ!0Q$L7cHM_Y}o>tv?IWG>FGe{*Ds}Cr&cxkMF%Ops#d^<9lGD+U3cY z`R2FkwlsG}33qba8F@j-XgA-jdaUy8_(_z8EMs}|O2K&%WD`f?ZoXLpxoN}TLlYs{ z;AM+e<{d+70hPq_6;N5fQI^e{y&KTZn_TtW;B9{{vLtd<*d%6gCb}w0cdZ;S!3k(e z0xA*sGz28iAu{5In(XpYEZ~(yl8DU9=0fT*dm-(zB2WZL80QHWfGbT|Or-&p@MLW? z4?{QhA|@@cLiAC(YqkuEHQEEhGWu-oc zNqF2?Qad#be(M=a}=YQntX_tRWNF%smG_Z|Zn)(`;U=6o$9V9m1hV>4tZP;z& z2cV&88r&1y()s(~<4Fgfc52dcU7Gl=D;r*rjG;~DLoW@ga=mp6bw%trOom=4O7`W$ u*C?s{hMr*Ebx}k`c1gQJP?qSG={=B1`{O8~^N@Y2o|90GVc-V*+T=gTWT<2S diff --git a/custom_components/hacs/helpers/__pycache__/validate_repository.cpython-37.pyc b/custom_components/hacs/helpers/__pycache__/validate_repository.cpython-37.pyc deleted file mode 100644 index 816e3af48936ed7f9917fb2f768571fbbfdf18f9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2498 zcmZ`*&2Jk;6rb5$uh*YRo%AE&BTgv=hsvo?4;<18Xo2(+sus~=sVr;n*j{Hp+?jD2 zTk9T3j~v1oAvs4R{tNDKpA!-%E=XK^;=S=lQmWe8+4*?y&3nIj?>GBtsgx)1`}dD$ z-##x9@)r)~KLa`+!y~?f#wQKpQ{M<_WHbzleKRy8t6@c%Mn;dV(2lZ=tnOz*C(1Q) zx^IX1sL&`x#YQnIHA*m-^_}OWQTB6w9^OlS;Q^@?N7r{**k`;dld7LoTS*iparM9p z1K*PYblVB9a@J2oAQK)iQU4qKA~mWipWOZOi$LxUnx7rESRaLJhH^Sgx?Qg&R6Yn6eq?2^Y!^lTL?mmGN0~&{35syzZb6;4&8) zPi?#$u^V}D&}Krqtt6H#mVjOU6p&vCKL=61xVa6>?Vz*W8VH$0ARP6R7{tXkg1_C> zhbp$`9-YCo-XE%^nIrRMNJ1hUXo%sMMOrrb3iKB!=U{*tt5N&^Gq-{0BJ6 z^~$0R@=KHD)I@3MpX*g7E54ymv5=Za^n3actALKpXXYdF4T+6MD3|H2@?RJ7T3Z%; zEicdOb^D0&-xm_r)S_>p#o%7Qd2EP}Kyz`T^0jB^Q>er8Xk}K1(+a6#h1B;(>-Xmb zGz5}jq8cZ%$_@h|>!XWj)&f!Wc&i%#<Wq~`9bX93e9|NvQUS(vm&the1M#{AZ zVW{I$=T4c&O`1K{lB#gVX_fb+0C`+7a@^`}^3IP1s5X?_K6RO-FGy%LW z^Ie2PSus15$_gaxcpj}evpYwqoOj>_q1OzVGC6B2+Y^FGp-f<7m7BvZxQ(75JE|-| z0lC9C(xkHVos`2?qphZ(^*!0G<@mA|K_ccG*v+x|4av7=wsa``Ll|+qwjh^grv8^g zybP;r@Q5Zf#IUIW|8;m*=qk-qAZxmAI?!t3h8EW?YSU$qaj0Wdj0(t=Va5iHim^(| zMjqBKfaC>~fcYZ51pOlX^?rM`WtDYZKWVwHDtbZE31oNB^!kB5W%c$ew@w{{t#`tt z2{}AXI6OqP^%_YOZ^BJpUmzoN?li8xPGNeX)n}gy`e0sfeIX2AFOg1ho&WmmbYkiI kO~-l$Z@gJK)~#}OqQoDB>=u4s2s{D{uvyj!t!AC#e^*MttN;K2 diff --git a/custom_components/hacs/helpers/classes/__init__.py b/custom_components/hacs/helpers/classes/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/custom_components/hacs/helpers/classes/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/helpers/classes/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4bc010c7f2132192950cf05d501cf96feb7f9a72 GIT binary patch literal 149 zcmWIL<>g`k0{@<~@gVv!h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vCKRG`yEi+v| zxwN<>KQ}%(Ker%1FEy{ESU)2%8Azn&6r>gv>nG&M4u=4F<|$LkeT-r}&y Q%}*)KNwou+`5A~A06JkK?f?J) literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/classes/__pycache__/exceptions.cpython-38.pyc b/custom_components/hacs/helpers/classes/__pycache__/exceptions.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7f8b11f992ceff90afd238e8899ae5a6d69fa14 GIT binary patch literal 548 zcmb7Ay-ve05Oy5Xrd2CdNNfxl>C(JFg(@L_rZQA4T`Xg}v{DiWpCgs7F!CO#57QUO z%3CmTPM`&eg_G`lpKX2jeRtRIcL~Pr>yE$S`R>8bU)$vgDNn}b7dswTVmTdr7j=i zM4EiDdPrm~l1q~nCfB)xWQik_rA~1YBr+8M8WMdlQ9M~M+ph&EmMEz4zrCF_-U=<9 zRtJx#SH{NR%EcmfOW|T+b*$ITuhG={i3cA|7x}x6mxB4N$>Zse*%qEvXU|`2tv~<8 RQbprti|5`Vc#kX`uups{d`JKQ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/classes/__pycache__/frontend_view.cpython-38.pyc b/custom_components/hacs/helpers/classes/__pycache__/frontend_view.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c36ce91a9fc783ea77bb1524a887247f6e0325d GIT binary patch literal 1484 zcma)6&5qkP5GE;ElD+n37hN>i7ATl1pAd*PikUMGu{!ZPZ0Bp~2B`hStn{^NoJ)^*RLB#qaOLKRzLU z;bDJ)jogQAGzdvJkyNsRmW(rsx>tCm&wZ!+MXL;W;Ph4zmJyGf9u)1e!#hq7i-WSu zyOc}`@2Ox-_~DSqNVZ=R*;fAA<9B379=s&{NOrGCzqdRYJ$QH(&#axts*>|MudGSl zLpG#O8}CM4!{c~Vm+FBrxv^+xMg+28%hOR>$wIkM@h|E+-oDc$V<`I= zfOGE_TA4Z4-uD{csAAHD+l$TN|Figm=pKan`^N!bHOZ%gY+-C&imWc-1qxc`Dj3XE zF<06QGOQMg*?}!y00znYw(%hLrC9M(@jSy4-#1v<9$H85dC-@ueu@2M}D^j^^`@V^FDyrKGA8?5M>LQh)QlNFW0 zGp5gMxQ3IBR$y@*bKZWqY!8)rdd>glS&mV7tKlix~_t z&|)*h$$!*I+#Td}^C7PioQ!14t7NjMGMfWslfq5D#_sTMK49bKU-0qRG2A4(kAf@k zXA2v{)LUQ^u6v#M-%^sQ#SA?{QzTc4i2MQ;10>XEw0j+`6`T~8EUit#O_S9GCsWaU5!ayrQpT8kLkB8rk{AxT-2wdO)@+TWbg#3lU@daz- z3wX>^06|J3s0bR`24z5zhfP?9@EJ8x+besN$nbSv^hEzx`VA2S5q}^e-UoYH4n+8p z%!b<^lIOgu7t+{l$ve^1-EF288PBv_DN|deH+hmQtzDVxqFD0E#g{0+5{8FkTjwXrXNjK77guijO1Woy;_2rDUHTgAAwy1p?L>W{C7 zQ~m*~l9Uuq8oYw@!P$Z`l0DfM;hc!59LN}W&-0<@ec(Zvc+Fr=YEq753YyrjkNx`4 z>rcEsnUh;mPUKYf9uv@we0=&4r~Z3q{(EE3&pki!{KE5z&v)tbP3J^j$yA*FM#Y&p z{}6%Kk3;}oU$_)~IM}^CKaR4h;l|`IT-5Pae(ffe>a6T6t2?HZvTn4kS4_)BawA>5 zs2hnw7q|D_a}oOQ&X={2OvnXaH(1(p8!uH0nG7_Dch+63 zq(+WEC~7S$Y`YuQ$}(S<8*AAKsi6QaS~;T{$F!nHO$&kd;dus+xdzxlzuv@pYM~!{ z`ZKk3Pu|lV*-;kk$d}}&i}!&IcXS8QE_h7#Ati6K9R;Vt?Z+>r&F;oyZKcT;TD6&| zD+?BFFQ3;?In3$d?3v(}&mxUA=pF)==LSYv%rOgnT#gTVhI$vik(=L%z69w%*9G{! zsBepEZLDfprP`J1zz*X_zF5izEh@Yl(iDeG`3h{p;=s1behR6~2>=OW8V7y2V|qrn zSI3>sALO%-T+A4@kujGr)+(`Xkf)5jUGwICXTTV|ZH#HO*M-JvH(`u-or2TB`($8i zfLqaxQjLy*>LG$RA;QA+2;&rCj1VATp?ZQaML0z`18^w}g{`Yr+NBcu5w4?2M{tE9 z8jaaZ29N|%kkaFON~ec72&h-jV*LqdUEgk2&_7>>i?BuR$$yN`ah~&gxgmL1PypU_dnYTjC%0hd0~g;RpSwWI z!l=gib=8br(sC`5sm5M(LDis8`UMn%C%vPg0y1bO|;}2kpGU8po{E8 z+L>&JV2&e8h}^WN;S9q5kU`#`^eP=g+clm)d_TKX|CKZq%MSOHiFqbgv7K% zVT=9yXxCNQ4yxG{6A5a@oKKXP)fgw7U(Jeo^+n?^;yeR~?{Z!=jauO|oa+u^KxK4@ zF2YLxT&WuF1)$Uqa2LUB0L95@M5|F4>|k&!W(i$CY$s^HWxfkx+Ny+;_MpU3)9(MT TnTrlr@SmaN?X-Lvc`^M93RwbW literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/classes/__pycache__/repository.cpython-38.pyc b/custom_components/hacs/helpers/classes/__pycache__/repository.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d15ad33e8abbfb47b532f70041597333216691b8 GIT binary patch literal 12467 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 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/classes/__pycache__/validate.cpython-38.pyc b/custom_components/hacs/helpers/classes/__pycache__/validate.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0de8e064c214c7264af92bfd97764c0128c44ed4 GIT binary patch literal 534 zcmY*Vu};G<5Vf5&4HZR*FYp=(H9JBG2?@3$QDDeo8QX1xk~r82s1+L>`4K*b9|$9# zz{bS6O{;Lyd%ip8y*uY_I2<5|>-SIg1?RUby5T`_4zNc+S4bj>SAaK?AW0@D@&xq% z#ZEz?tx$p`N{GaXH$#c9dXh+Qh2C%yNFVgQCcNiaDtV=rd)sUzaL3Z+4i^A>0Q8C; zkr_xlX;)Yt->9lK1%1#uqv@1Zb48ywuBk5Q3%8V0TMMDA9bMi=xCxXo+E~bZtFq|_ zGCmmpp2R{I({vV#+EzMeLg%F}R8iS@&IORlN@Z*;GH$K1aXUnf%0<&}2Lx_SN5~7X zkC%tLSG6CEqka=IR`6Ug)(jZSwX8Fj?=$vP^Q^t;GbXiQ%n str: + """Return a string representation of the repository.""" + return f"<{self.data.category.title()} {self.data.full_name}>" + + @property + def display_name(self): + """Return display name.""" + return get_repository_name(self) + + @property + def display_status(self): + """Return display_status.""" + if self.data.new: + status = "new" + elif self.pending_restart: + status = "pending-restart" + elif self.pending_upgrade: + status = "pending-upgrade" + elif self.data.installed: + status = "installed" + else: + status = "default" + return status + + @property + def display_status_description(self): + """Return display_status_description.""" + description = { + "default": "Not installed.", + "pending-restart": "Restart pending.", + "pending-upgrade": "Upgrade pending.", + "installed": "No action required.", + "new": "This is a newly added repository.", + } + return description[self.display_status] + + @property + def display_installed_version(self): + """Return display_authors""" + if self.data.installed_version is not None: + installed = self.data.installed_version + else: + if self.data.installed_commit is not None: + installed = self.data.installed_commit + else: + installed = "" + return installed + + @property + def display_available_version(self): + """Return display_authors""" + if self.data.last_version is not None: + available = self.data.last_version + else: + if self.data.last_commit is not None: + available = self.data.last_commit + else: + available = "" + return available + + @property + def display_version_or_commit(self): + """Does the repositoriy use releases or commits?""" + if self.data.releases: + version_or_commit = "version" + else: + version_or_commit = "commit" + return version_or_commit + + @property + def main_action(self): + """Return the main action.""" + actions = { + "new": "INSTALL", + "default": "INSTALL", + "installed": "REINSTALL", + "pending-restart": "REINSTALL", + "pending-upgrade": "UPGRADE", + } + return actions[self.display_status] + + async def common_validate(self, ignore_issues=False): + """Common validation steps of the repository.""" + await common_validate(self, ignore_issues) + + async def common_registration(self): + """Common registration steps of the repository.""" + # Attach repository + if self.repository_object is None: + self.repository_object = await get_repository( + self.hacs.session, self.hacs.configuration.token, self.data.full_name + ) + self.data.update_data(self.repository_object.attributes) + + # Set topics + self.data.topics = self.data.topics + + # Set stargazers_count + self.data.stargazers_count = self.data.stargazers_count + + # Set description + self.data.description = self.data.description + + if self.hacs.system.action: + if self.data.description is None or len(self.data.description) == 0: + raise HacsException("::error:: Missing repository description") + + async def common_update(self, ignore_issues=False): + """Common information update steps of the repository.""" + self.logger.debug("%s Getting repository information", self) + + # Attach repository + await common_update_data(self, ignore_issues) + + # Update last updaeted + self.data.last_updated = self.repository_object.attributes.get("pushed_at", 0) + + # Update last available commit + await self.repository_object.set_last_commit() + self.data.last_commit = self.repository_object.last_commit + + # Get the content of hacs.json + await self.get_repository_manifest_content() + + # Update "info.md" + self.information.additional_info = await get_info_md_content(self) + + async def download_zip_files(self, validate): + """Download ZIP archive from repository release.""" + download_queue = QueueManager() + try: + contents = False + + for release in self.releases.objects: + self.logger.info( + "%s ref: %s --- tag: %s.", self, self.ref, release.tag_name + ) + if release.tag_name == self.ref.split("/")[1]: + contents = release.assets + + if not contents: + return validate + + for content in contents or []: + download_queue.add(self.async_download_zip_file(content, validate)) + + await download_queue.execute() + except (Exception, BaseException): + validate.errors.append("Download was not completed") + + return validate + + async def async_download_zip_file(self, content, validate): + """Download ZIP archive from repository release.""" + try: + filecontent = await async_download_file(content.download_url) + + if filecontent is None: + validate.errors.append(f"[{content.name}] was not downloaded") + return + + result = await async_save_file( + f"{tempfile.gettempdir()}/{self.data.filename}", filecontent + ) + with zipfile.ZipFile( + f"{tempfile.gettempdir()}/{self.data.filename}", "r" + ) as zip_file: + zip_file.extractall(self.content.path.local) + + if result: + self.logger.info("%s Download of %s completed", self, content.name) + return + validate.errors.append(f"[{content.name}] was not downloaded") + except (Exception, BaseException): + validate.errors.append("Download was not completed") + + return validate + + async def download_content(self, validate, _directory_path, _local_directory, _ref): + """Download the content of a directory.""" + from custom_components.hacs.helpers.functions.download import download_content + + validate = await download_content(self) + return validate + + async def get_repository_manifest_content(self): + """Get the content of the hacs.json file.""" + if not "hacs.json" in [x.filename for x in self.tree]: + if self.hacs.system.action: + raise HacsException( + "::error:: No hacs.json file in the root of the repository." + ) + return + if self.hacs.system.action: + self.logger.info("%s Found hacs.json", self) + + self.ref = version_to_install(self) + + try: + manifest = await self.repository_object.get_contents("hacs.json", self.ref) + self.repository_manifest = HacsManifest.from_dict( + json.loads(manifest.content) + ) + self.data.update_data(json.loads(manifest.content)) + except (AIOGitHubAPIException, Exception) as exception: # Gotta Catch 'Em All + if self.hacs.system.action: + raise HacsException( + f"::error:: hacs.json file is not valid ({exception})." + ) from None + if self.hacs.system.action: + self.logger.info("%s hacs.json is valid", self) + + def remove(self): + """Run remove tasks.""" + self.logger.info("%s Starting removal", self) + + if self.data.id in self.hacs.common.installed: + self.hacs.common.installed.remove(self.data.id) + for repository in self.hacs.repositories: + if repository.data.id == self.data.id: + self.hacs.repositories.remove(repository) + + async def uninstall(self): + """Run uninstall tasks.""" + self.logger.info("%s Uninstalling", self) + if not await self.remove_local_directory(): + raise HacsException("Could not uninstall") + self.data.installed = False + if self.data.category == "integration": + if self.data.config_flow: + await self.reload_custom_components() + else: + self.pending_restart = True + elif self.data.category == "theme": + try: + await self.hacs.hass.services.async_call( + "frontend", "reload_themes", {} + ) + except (Exception, BaseException): # pylint: disable=broad-except + pass + if self.data.full_name in self.hacs.common.installed: + self.hacs.common.installed.remove(self.data.full_name) + + await async_remove_store(self.hacs.hass, f"hacs/{self.data.id}.hacs") + + self.data.installed_version = None + self.data.installed_commit = None + self.hacs.hass.bus.async_fire( + "hacs/repository", + {"id": 1337, "action": "uninstall", "repository": self.data.full_name}, + ) + + async def remove_local_directory(self): + """Check the local directory.""" + import shutil + from asyncio import sleep + + try: + if self.data.category == "python_script": + local_path = f"{self.content.path.local}/{self.data.name}.py" + elif self.data.category == "theme": + if os.path.exists( + f"{self.hacs.core.config_path}/{self.hacs.configuration.theme_path}/{self.data.name}.yaml" + ): + os.remove( + f"{self.hacs.core.config_path}/{self.hacs.configuration.theme_path}/{self.data.name}.yaml" + ) + local_path = self.content.path.local + elif self.data.category == "integration": + if not self.data.domain: + self.logger.error("%s Missing domain", self) + return False + local_path = self.content.path.local + else: + local_path = self.content.path.local + + if os.path.exists(local_path): + if not is_safe_to_remove(local_path): + self.logger.error( + "%s Path %s is blocked from removal", self, local_path + ) + return False + self.logger.debug("%s Removing %s", self, local_path) + + if self.data.category in ["python_script"]: + os.remove(local_path) + else: + shutil.rmtree(local_path) + + while os.path.exists(local_path): + await sleep(1) + else: + self.logger.debug( + "%s Presumed local content path %s does not exist", self, local_path + ) + + except (Exception, BaseException) as exception: + self.logger.debug( + "%s Removing %s failed with %s", self, local_path, exception + ) + return False + return True diff --git a/custom_components/hacs/repositories/repositorydata.py b/custom_components/hacs/helpers/classes/repositorydata.py similarity index 52% rename from custom_components/hacs/repositories/repositorydata.py rename to custom_components/hacs/helpers/classes/repositorydata.py index 60b6775..6741bc6 100644 --- a/custom_components/hacs/repositories/repositorydata.py +++ b/custom_components/hacs/helpers/classes/repositorydata.py @@ -1,6 +1,7 @@ """Repository data.""" from datetime import datetime from typing import List + import attr @@ -8,32 +9,52 @@ import attr class RepositoryData: """RepositoryData class.""" - id: int = 0 - full_name: str = "" - pushed_at: str = "" - category: str = "" archived: bool = False - description: str = "" - manifest_name: str = None - topics: List[str] = [] - fork: bool = False - domain: str = "" - default_branch: str = None - stargazers_count: int = 0 - last_commit: str = "" - file_name: str = "" - content_in_root: bool = False - zip_release: bool = False - filename: str = "" - render_readme: bool = False - hide_default_branch: bool = False - domains: List[str] = [] - country: List[str] = [] authors: List[str] = [] - homeassistant: str = None # Minimum Home Assistant version + category: str = "" + content_in_root: bool = False + country: List[str] = [] + config_flow: bool = False + default_branch: str = None + description: str = "" + domain: str = "" + domains: List[str] = [] + downloads: int = 0 + file_name: str = "" + filename: str = "" + first_install: bool = False + fork: bool = False + full_name: str = "" hacs: str = None # Minimum HACS version - persistent_directory: str = None + hide: bool = False + hide_default_branch: bool = False + homeassistant: str = None # Minimum Home Assistant version + id: int = 0 iot_class: str = None + installed: bool = False + installed_commit: str = None + installed_version: str = None + open_issues: int = 0 + last_commit: str = None + last_version: str = None + last_updated: str = 0 + manifest_name: str = None + new: bool = True + persistent_directory: str = None + pushed_at: str = "" + releases: bool = False + render_readme: bool = False + published_tags: List[str] = [] + selected_tag: str = None + show_beta: bool = False + stargazers_count: int = 0 + topics: List[str] = [] + zip_release: bool = False + + @property + def stars(self): + """Return the stargazers count.""" + return self.stargazers_count or 0 @property def name(self): @@ -44,19 +65,33 @@ class RepositoryData: def to_json(self): """Export to json.""" - return self.__dict__ + return attr.asdict(self) @staticmethod def create_from_dict(source: dict): """Set attributes from dicts.""" data = RepositoryData() for key in source: + print(key) if key in data.__dict__: if key == "pushed_at": - setattr( - data, key, datetime.strptime(source[key], "%Y-%m-%dT%H:%M:%SZ") - ) - elif key == "county": + if source[key] == "": + continue + if "Z" in source[key]: + setattr( + data, + key, + datetime.strptime(source[key], "%Y-%m-%dT%H:%M:%SZ"), + ) + else: + setattr( + data, + key, + datetime.strptime(source[key], "%Y-%m-%dT%H:%M:%S"), + ) + elif key == "id": + setattr(data, key, str(source[key])) + elif key == "country": if isinstance(source[key], str): setattr(data, key, [source[key]]) else: @@ -70,10 +105,21 @@ class RepositoryData: for key in data: if key in self.__dict__: if key == "pushed_at": - setattr( - self, key, datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%SZ") - ) - elif key == "county": + if data[key] == "": + continue + if "Z" in data[key]: + setattr( + self, + key, + datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%SZ"), + ) + else: + setattr( + self, key, datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%S") + ) + elif key == "id": + setattr(self, key, str(data[key])) + elif key == "country": if isinstance(data[key], str): setattr(self, key, [data[key]]) else: diff --git a/custom_components/hacs/helpers/classes/validate.py b/custom_components/hacs/helpers/classes/validate.py new file mode 100644 index 0000000..716da9c --- /dev/null +++ b/custom_components/hacs/helpers/classes/validate.py @@ -0,0 +1,11 @@ +class Validate: + """Validate.""" + + errors = [] + + @property + def success(self): + """Return bool if the validation was a success.""" + if self.errors: + return False + return True diff --git a/custom_components/hacs/helpers/functions/__init__.py b/custom_components/hacs/helpers/functions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/custom_components/hacs/helpers/functions/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41b5882447495d795c3b162ced807ecbf4d3be0b GIT binary patch literal 151 zcmWIL<>g`k0{@<~@gVv!h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6v8KRG`yEi+v| zxwN<>KQ}%(Ker%1FEy{ESU)2%8Azn&6r>gv>!+3GC6{F8=N0S6$7kkcmc+;F6;$5h Su*uC&Da}c>1DX06h#3Hma3tFR literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/configuration_schema.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/configuration_schema.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83944b4f331e2a92ef6fb1cedd18659b24b5ffcd GIT binary patch literal 1890 zcmah~O>f&a7#8(q+3{!Mw#(A4Ua<``P&hje#W2@V7h|q%IL@#U7z9P66LqrWQdE%! z?q%z7`v*NF$Nrf80bY0Nb%!2y>0Z*x@=+9{!N=r7`W5-Sk6!OrDkTlt==VRpcaEn0 zB`24g36pQ3r@sOa4Qm7uJwjNAZzD41CN_~qOl%Pg+r-8NQos&zaFG;oiIngfS;J*g z#uZY*RZ_(@Qp5F7n<3hu4N`xllLlFTY2o$%xDDW%hnLdXOFB z3ZoJAQ|d+GJmk1cj~A4Ma~g9$!kbs4E_{CEB}_(d>%xX(z>S2>D9>0NAbFL%$*~2! z2R;1^V5y~L4yg#y3yq^EItS*|;My4^=MSBmAp)t4llv1DtL3-SqW}bIt)6kN8VLj5 znkB3y746`y;}5sjNE8U2`B}uwEcmc#O^8$!p0P!n|vlVAw;Vi{jIagKtkm69z zl}>BCeumVFOerKeUuh`LRGfK*(-o&*;S9wY7u@MOclgGcIvtFQt8rKYn>Z)i*KKm{ ziEDnzW$Zi>Mt3lHBOQ5ClqvaIf-t3xgvMk+X*Wf&J09y5za^%z>ioBrmQYGBaFGw`!Uny%sfcI%SHD% zh_W<%M#b8=JKgn06Bz{j+?2nQZeGE<{LQ9vV{XKiHn|@^hvoDKfZUG=Jo$h9tyuxj zqKoca8n5^gtV4gx+7KBIrR*%9a^4IW$bzlx`~?iS_M0Xv1Rz?X9qs4(3w?=}`X-ob zsXx%3)|VPs0G-_HCG&$YrY(L%KalmduHL8B!wpajUPGmeuveGAu21u_KJe1Z_~ap+ zDph|ux{YkzK~npGdeZ;Y$Ev%0hT?Idz^(mr-d~8FwDc6Ou*r8(EN38Q#<# Sy=<81(&v0;m_~E0dFNk>1m|Y} literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/constrains.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/constrains.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5addd8d747d3765dceb63fad8ba5ed5d60e04f48 GIT binary patch literal 1319 zcmZ`&-A)rh6rS1LZnr-uAR$~BveBz1x-IvH&=eFBDTEe{P0VDoGwm+!Po0^fG;oEN zzJW1;OCQD;m|GJQU%?wavn>TQY%(+FXLir|zVq$j+*}#Kv-kNs`Dr2aLpwJg1Dp+5 zq6-^EJw!1zV%)=Unz50XJu|U-7S?eqE+lr(&h0{6Oq`wrw%seyB6U8YUYV9?8O{o= z&?=l&YIIO-ZoIPHY<1j@^tl`j-5^Va;B zR#wApWnJ5VX#ey`Ud-;W0M>1#oj46~G_Qq*UD zH#|}Efo_Ui2k&bn`MeDqTUaly-CM9t2-vW4*!y5;*k=HF6TRIy!_qi1FRW91Q8-0M z_EGT?U&2SE1OBbpnF0CIMfudY#29H!bkO_ql&;kYT~Xh?&4|lBMnZZ(TZI>(G}rP| zMkbUs^kt~*JTH{p&v@cXWd#{$sxqO8=qp^eugo|bC@0{Ni~>Kdnaa|HDJRYXUo)l5 zL=3K(<|SQAgVtjf_h3W7kb{@;60X9T=Wem2g`qplS!#fB#S)+fhNN|Fh$mndE^LaW z^#z3T)znN-6GUTy?hE5<7C@HnQ~_=z++#nEs5f4Ela1VzF$%hxrN@ulj6?JT6`~_z$Q33z} literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/download.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/download.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0cce5aa7a17a91aa0e409ab0fcbc19c9698dbbb6 GIT binary patch literal 5669 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 bYk2Va!qlpT8Y`W!=B<~kqt*#)!DRmh#4Dp; literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/filters.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/filters.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b2190e8f39c7319baa05f65e69bace4310b99d6 GIT binary patch literal 1296 zcmZuwPm2>V6i+hgbUNF*D@sB1FoFp6&?0X|s)>B&A5Dcl+r%VrQkmh2k`MvQN3|l=-bx&*A zs@iH&?ZBCi3mf1Y+Aw4a@J zlBv?gC{BvexX7W9>I`}bh;%qkrRr=5>n!W3_IPIRE_K-C&KAp5Ph}p%{II&DiNgG02q#6wEF zp7c=!xLT1l)bZKK9TW`^wid5JcaSf|E_#}MC6A4f^^rI(HV@`St1~5II1cm_sY~FtB zC@euBGMOg2kh3VuBQUUeqC!Roz;gSb#ci!W9S-AhPY)y#W$T%WH=7&+fEMCntE=s{ zw((o;HexH5aEYMrSFmg%<1F8%?LfT3O-qEDfD3-|7OWh!sHE^;|G2Hju_%%_cHQOh fbAcr`TzyAL@N86w=`ekcDHO8GIr$sj33vYipGZX@ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/get_list_from_default.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/get_list_from_default.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1539ff06f7b28ca329177a77eaabc4d8b687fcc GIT binary patch literal 1266 zcma)6PjAyO6t|s!ZI^5v8eAAtCB&hZRv@@dh=I^H32g|3Hgbv5#BI|hab-KwTBTgZ zk&nP(y}{RjGhe`0PJ9I95A*C2G6V+j}$}Q{IxSHK4K;(|%Z_vc>6`i9|A>utXa-ka-~mB8vGQa8E`vof+hFdE3G-k z=m?`j4Er**pTZbIm0EILoQ3rzd60V3#=~*0k&Ix z&a!15QXsrDbu~ZT1vv-P8@o)l?uZsmX-0DiR~QGqeT_Rn3)e&`M^GUkDawmj-cJSv z4|R@|CD{qhm8)5Y6RGd3)rIMx$HP1tDsv<>G-Y8dl(T?wU83C5#*LH>ltnpbT-BH9 zRo>%J(BG;R(_S$EFwNUGujz}X4n(<^<5i~C__{u>(_gjp5%Epf`M%%1aK9TBLb428 z&c-a)PIoI#hE?^7ZokMQ9iQmxz%*-t<}p|V))`OK%}`A8DERBS2XBcM2t;gb5`qaf zv5m=(hP8p4WCL%)*diN*Zvm#grko&%Sri0(4RmG8$vDXeoM^=j6E;ZXu;_*3q$17V z{KNZ>_P#^&A`={b2T0w$0ALByMSUYug!%ifN2$p6=P| zUMFtNMhIJ+5RU_=$kDFEA(nFHz!4;F96%sNb>)Kp1q;RezUrRYnZXuZm{C_%e^p(N z-}kQXSF2?Wzqfw==jPStH0>YsGWs*{at2TGI}}`NXk6zk)Ehc#BV=8pVRX%gsT17{ zt*+g$Roe=kuG?@`+YU?Ja-)p4!`=H@V~Uq}8FhtE@e1lHukvZs)BGsk!{_enjTyd| z&x10{_wfbPd-#5S0QDR{$Pc03%MbG-sOR~Y_;aZD@#ndRdV#;dUqrp1zrDmo#qe7?P#zg<|VgGS;N;8|oit1e6 zN;?c9zZ=wRR>Ky7&$~fZ%IkWUH|*;d&tAB8F)QUYNW)|XX|FD} zHj^~&Hd}GG7e_&qCW{2!;#v^)0+B4PY(_2OL9#f)w|eicEYW+Drkx^0=TK!4(#qAxo#ZtS^FdKUp%rmvrN!=B;UKT&m!A_kRUh0%PQ=*?% zxN}ecss2MPVmH-%H8(qS8B9#q0uR8X0K$t`6i

aIn$k%v?(DtY)Pp-cJ16VZbwM zX^|-9rNtrX$f;9-5V1IQ%3F?iWx`vDHzQuFDnR6(UCuP)+nu15$_fc>v2r2}5v)k3 zc$E%@hkh#%7PcWskY#D81JWdZNjtflKnJAlD|QJJqiTkz(ihWIxKt49^I{e)xtA7I zeC+bU;s?-?96~|dH1TKp0=Q|Ttmt##?X)fqqqjwz9Qo7xDESoTt3PW(}W zk|XHF0*FUJeYs`{+FNc12x$eG*biC`i!GfbND{IvRvNDdQDc6DAuF3<*d!g6_G&v_ z+q|tnl%-%`jwVr@CKPuks9~L?f9>1L-wM1Egc*kmqnnxHL!t z>zgBoG(R#Y4*C4k4=I8$@WEVlP^D7?%E)1@oTKHQ2#Q`gC6u2E+ue35-QhPLHa!W# zQP7s~3_+O7K_Gey&+tb=NZ_8wlh7Bbc2CaN!<(;aI}EVh z>D)S~ZRxtU@r{jzJ`7&X&R&O{DFd}4l!fBn9av~`qabuuPuGvyD?yUh1-U+15y8f0 zTLfLQ>y0WIissXBb&vZFA^wZm^5a?$jmCkV(a27p!^4l<7xwWUrvt*>;sjE-B4*$Rnra?J4+SSoSR# zs^}y#v+%22apw386vsi7@CDYLCHtYi*rTW=8U^~CqHCS~_!?L= z4`A=7u=j)OWsG>g*!dxh+^cpzUtqE8na1|d9SGLG%p0*sZNv|&B9Cx~@{v`CO%j4^ zdU5Q1jERF-^#<{sBCAyZbE{13>S(`a8|IB%Odjg8q}&w=*Fz>*{SB#qI3Yo@ycD*R zlmfidK21m>Vrxvxj+0`JY<8&p^`$Uw`C)Pzjlrkc4B{CI_wrE2d-bvY>T>qn`FJzr zUKFQL9G?&MNvVVP-ediiEVp7F#2-YkRnq2h*Kb2Llz$zl#!47}a7k8q+}b2drGU%c z*bSt{fb!aj*KNalN2}i07qYq*cVYY!?AVXe?ELNo@4yp|b$aUoB_b2s#mrIcg>keR z2=9&`wt2SiTCr5{VGm(iz}1(TMIXOfJ0!@x$(h1V0L!L`<5VfLG?m693FyXYAYQ^i z6@3Wer`Sscd0TOkimy{a0g9}igZtVI?&hDy9_jd-=~^rjaZY_Qz(++b;yBH9feK|< z4pNKaIayLnRl9-(0)M6Z-Qey;idJj6|Cd7sLUs2gWg|+<+2e=(tnm!5Kp>l40~cavusWm*YWk3I*^wdI?_+^LfYR! zBD~I^(G>B+QAR%~s0`HOsZmhR6jTG$bBHhVv9A?W6V#ijRZ!;(ss(C@TtbZn&FQ$E zlK3rJuy`DFtX}z8djQL)vI?R)pjJE6otXihyRZwT$A>QuoOY2JdTH#zsRF>dw0q&^ zD%4?Wgv$s?*Wj>a$?x^J58;VqwRacnj+#kJw0mh*8su_brdX;n>E+sNE{tvqS@0?n z?ZWS3StS?EVy%P3I+`J;j;^fIf!(frAN$A0@9ygGc*octCq>T=<5eh?NisJN5jDLB{?Yn)aW8;g@ z(88XYB~EK1oo!YSCH{5eGkis3zk{)s_$THs?MHgc;mG?g2_-M{DL8uEcEHWs;`6?{ zUF9XX#@xMP#y%8bIAed~^Nl{QLWrjY`5~19Q!1(gY+OIU%#!@w2`Du^kr^=gnN#2F za%z-M+(y0|2575L^s&A;#*hEi!-;Rw(%+(DTq?wA(0-36cW1CK*|7zS{ur(sDoB3<-$9hvoVJ& zucVEOf+ks8b&1lLQEo*EGr7P-x`RH^qE#%lO1}2Qfg9-m4W8r-3JL^Wx^7`*!__Os zd~Q(xWjb^PLrFWO?391fg^YSI?ugEwxCSNhf@T}z2pL&==)?+Bx@LLE6z`XF@O^Ym zvVjc#ALtX}sHO{gADhaDR$;$^B2N~hUG7}jj6Kt@*lcAVdu_`P^-C+pI z;Z+K%5-SynC|{z~Mai~eyC60R0%8O@1){oEbCaI5Yf Vx9pb6C3gz{WA2=L!Zpz{{|y3G={EoX literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/is_safe_to_remove.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/is_safe_to_remove.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cdbb709c9f5a4c7e3502cc0e5547719848bc3ca2 GIT binary patch literal 808 zcmZ`$&5qMB5VjpB&34=JCm{|Tat()u2S5l3fj9xu!X+0gYr9RlNgUY@(xTl{Ir0X} zUN|7}FuZ`ToOp$v7^g+5R63C-<8Q{E&)ThSJ*PC zOk#?K^a(8+^-0eEKnIj!N8dvp{_#>;Ru=$Zf*w8mHDBGTVQaE1g0 zX+rH^vBBBr2N~~;RSn~+=~OFQYqHlL(y^vcp#A$Chh7FrHUy1-cN=($q$aM|#lJvV~}E`HW1 R(bw|1AC8H@Q9L1&;4fB+-D3a% literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/logger.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/logger.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f63be79361adad65e8459bbc1fbe08ce7c8eec82 GIT binary patch literal 633 zcmYjN&5qMB5VqYUO`8;iv=ZWgl*>w+OLMPQh-|33DpFf8TmGnhJ+&2f3=iqt+utp3O zh+#%rT;L&+g0K;D+EM2e4n`b*bGh>!7aqqqW9~Ee6fuvJo5=@H6>X%7&eQB_eg{jS{B8soV-vnfvzO^= zN%Qpe(gf5}UJVt+Y;|#czCNRAc71V~@1HIf$0sen<*g_?9_z&-IzH*R)@2QuabGOY z)~lEp4<>bGZ`^?kA1<{8##6lCDN%t!|INOwXW-h()Fap(@1MLKxKSA$c8MCH`%+YV z9%E&B5PS$fkh0SMStOMdo2E{x;bvONZYu>By-wbj6##FyTdl%!;Fr06gMjpM>OyOtFIrCVxHe EA1>>oV*mgE literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/misc.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/misc.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..de4b450149e76a9d423cdec43c85952e13b6b4bc GIT binary patch literal 1480 zcmZ8h-EJc_6drpf$s|pe)izovKP!WFwVj0|Ttc*>AXEerw5Z)hA}u?(V<(x`GmdNz z-A3VR_Y&Tq7s(|L!wdM9tG+@nD94ki4eP|mpFL-eeZKE_p0BSrJZO7={2iZHJntVj zS#7Z9eF);jYH{9Czh+(#Vf!Q7Q7Wl6*eBecQC-=YQV$2m86nSD&nd=_O zQk`7FNxa<%*}70goX}*%C%aqQJ@|K?3{x{Ijys9WI??n+v~~DZyHDGbPurijfBE>R zdw6u#0k9n{{Es@|A8k)zcKD)v)OiwZM-L%*RdG|OJV9=^Wpg-y{xx*{E10Qw;++5$ zr*+j@-~+dYPke??vFZU1RFC;?JgqL~%-{5uUX@wZ(Zwp8-mK=xnqwHlat!C-71bxb z{qh{dj5x_@#)%x1Sn;vcsgdfO45T83<|NI_Cj%vO!*kZzwQDJ$p`wmG8@Si6Q1|j0 z+7)VFA!1P+)%zQ_vEq#8>40maobWba)KD8H6r#XR{nkpn8&_K&6hg!*xH;`cg3EgF}K{m}PVLM)RjIF`%$Pudr$XLJsM77;=hE5orF<6|aE`m<3aBj+O|` z(P;pB`ja1)dQx&Mr6B2mXj!NP)YAk`0p)H+6ojM-lKh06r9u!Yv?Rxz7*&AZfTRpq zN)swN-#m*dwvp;I*M{amxDV3VyPAWJ;1U1Em3G?Kep-TRB0209VRavJQExlr)L57e zK=#4%rC9KRiAU*h#8nLCm&XdQc>q4T{Al{0;$%#faTT8tN}lh3-`Tx% zdjShrLAhnrBqO`NzZ388{J3}U-Lt*@pKWERWHGiCA%R}V+-0CXbmooB-{XrtS8EGy zQx-#BTi~xlFdo!a;F5j>IBMEj95b23u_7>6?}Bj`0=`%vuU0O3TXUtI3a(uCYSS54 th&FH~P?A>$TRBd7uu_fiX(m}A_!lls?Ot=7hp34I1chC>vk{_J{Xcwujwt{D literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/path_exsist.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/path_exsist.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d59bfad43b2d9c91856bc47757141792e39c234a GIT binary patch literal 571 zcmZWly-ve05VoB(4YWmKVL-e8b4D2?5wRT4)&M{1=q!N?;pz}V+t z<^{4c@d`}br4SLI#&a8^^W=`*UFXRzb{)w_EbzZ@lidcq_GijQX`BGoo>8_kMS_^WMm6DGp+g^o zWeff)*6a=MZ1`>9)Ivk;ma$E1Pc7$Q@s#m!a>I;|>H8u~bTOUJ!lVML^C;1IsS8zD z2nje$RaPnk;j}6eJJ$t-bSzR!z+=O5Rrgwbmv{&aj2lwv4_SJ5(cjVG7=vYxNcVxS zk+OA=w75Z`uGxqgnhRV*E47=(0QFG}t0IYFDG^&GmDMI%=t%<;h~`VChvUt!wqcL9 z54U?Qk~9Sbz_`!N0CV7(4yO9#p9VwHU#PB7Hd|<; z#M<;gkV>VhqGvz~z0q?|J@?v6)zhfAa^YV=@sLL>M(j=U67ScXzGKy;>H2P-K>|0GM+nb@?FEk6VwzwTS z{bI8S>j_@urTes5;!cCq$}i_PMJG^+kgn%SF^E-=#B!(p0&TqlI z+S;YNz8EAy9MzbvT=jf)B+zsDb|Cyj@FOtfmGV(|jv;m#8S5#@1i zkjA0K-yoUYKgEj=GQVhYyf5YZ8zfpcNix?f0nQ~#cHt~X^*2#Ko{20^Ikg~L<%zXN zxp|M>AfG@*yVkeXO>&Dw>?TR(Gwi29KiR|TH@Ov6zawqRr}*@J2V$T39D1{Br#5JR zOl>{`-2!P3_kZDPe<7D*{n2HRoaJV(m}xQMFIfiXAyvPE4*EFuJKKF{{~WLK*-T%_ zaqv@*jd91$U|%sNH-U?D5>A?orjaa*L6nG&^pI?$Vxt!f20_$uNAJ6Va-%o_vflQ> zfS;hO>wDmW++DvsTKEdJnY-JLrTc+$+g=a~ULP$iEeR=OxwPc|$7tjn8Jz})6Iws+ z4?+RV9xZ?(zcD#D_gFP`!U1!RD&uhOogmqEHLYuU!N(d8KL!g6#w-kV9;Obk^lNvQ zCM2cE^gUQX*RoZQN#OBM>>KFSGg33Op%ST0DALm`;BX~b{eq@pzQ}R{!oIw+ z=*Lkz=q&m}mBjrPf{r5*C2BG2_O=KILaN2~F!HhGYEh0~h1PMX`d~+wmP2?f(0%1& z1Z~4)6GqOE(MNA{Sq@|03)M>f*iT~Bqp2+Kad2JE-Se(Ob+(rwkyWs6R*nr@Q0lu9 z^J*J#^y5gI3iu(hk@M`wuJchAB(GhSOK?Em{51lOIG>J;6 zZ2YVS0^&>2k8cZYZhH!lYyq574z(RfQ7g#z(8oC>v$p(g;rFy5MH_v5rHkV583xdI zp*ocvvYf$;NF_b{mZLx@stpriHY-zyPBI*2YEwq1VUK@Rl*5>&C7f9mBvsTOFveb= zvL_E5R(-{68!W0unX&_u+W9aX3N{eUer3Kk9eEKl)!6m*+Jr8&T0Hh!EnW74xC3`# zxaAFktWEmj8;q=Dk@Ybn>wXAr5~_Ywv&xP)J8gaLf1K5}Cq>2)`60l_S%HA^M&Xsn cS6R25<$lbEp;*DIsqlBlq-8d3mRZ^S7oXT8q5uE@ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/remaining_github_calls.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/remaining_github_calls.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c16380caf5ce5e88795ac12a43a4f5a145267d26 GIT binary patch literal 988 zcmZ`&&2G~`5Z)graiX-KszpUyEN;0tQV*OELWrsbRjovX3c0LY@7l3qd#&BIfXYEg z<;X*%+;|Soyue<$&{yb*S*I;Uh?Qp6`#1B=H=kdxtql;2qc1<$CyLNdx41nBES`et z9dH~?5yzb5c#2`BIVtFrVkGDpntI&hozH06;a%Q?xy#7}jr_}>zk}%+xYLaW zEzvo80r2+G0w1IDk%tU6Bq8&G+Q(=?Fgo2dbm=wZ9Ak8h-lDG_#}jz5ARoyaSeM`% zyzFqe!STGWxal@f?tOIT?olYmQx3( z<8?g5?df0CyX($c>Yu|0(1#6bfFdBD0QrQ|1#Ji?b6-7l8fZv^w^2jqSnY6c0&Q86 z>%9l)9H{-0_zo#nb}T~OR1oTLpZnHB2sBq+D=RJv{xzSGPDe@oMXgpxxTh(SY1IPp&4Y_ zf}<^A5v dU~PRZI{$GYd7XhK)>c7xSdE*VV5;O99^Lz9Bdml6Qx7}_Fa6J6+&uGsF z_}hYa2SxBXKI%3K24i4^(Tt2qt?8K7+8H~wcE>JiC-dh1*eAgK3m7+;%e-eW4j4Uw zpHFDd6ZNs6Yr7&gZhp%JeMRq2>EjmiKOs=H2^5Fuf55I_ zhuRy*`h5?-xorOgdsMZ5Xg@-n(`^U(wgY&A2SxiNe!>USG~+5Pnh4T-Hn^b-Sry(= zPj4O>Kb_@5azj^>2SdlWbHTU?c%BG`zQ*TL3aJVwJX3`mCQ2D9CkSVS@%1U6b5(fZ zc|6ZvlhLa;IEr9&X&*_v2T)u;=5tZU6&V}UTXjO1M)AQHVu{9F=)p>+nh#j4hXQq{^G*>##6K))(r3vCx)D;?6EO>6bn#y=dCKT2s zlyMiaKE+Cvb+g4WwMxh;kGNvG*-lq{7N^9Y?5BF7+VbQ&ip zc(&kDji$>yu}P?r!g~-d&dpA&&hsR)FtVY#NT=?g0NNq0owT*p7E!3@n;xa4PY#_9 z*(V*cM+VMS(%a4s=~0Ugsr(puhD|)Oh>0YM4cu?4mIL&pEU?hufUP|8fqq q`adJ>e@5CoYx=qY?}(2qXK)Lz?K0z^*&C;NC|nZQNdwxUzVi7wjwQb)x%zfbdv2(JCl?oE9^2o z=}|v{2<_3&;n^=RS5JP0o_xt{>$WwJdh@)I%I|lHr{62zGLi1$o#DK@NHCGwRiNoi|{w^N7#D-r%!*4)%yQyQnokUhG<> z*`A;SCb`h2{RjM_7B1)Bbho$GWTM~{)P)agJKfFgt+@Mcd*}7~TH=75jR1r?%vG4? zDJ&cp_@J(v1(p#f1^$MHAEI%Qcpo*1^bS{0&lmYI=E;tc$?Lfrru+95A%-b)#2;RWs^R4uMv%Ul26#qQUN z{32W#U(aL~U&9d&v0i|)#1TCkPCh5T49XetV_c9ET+w#5?Ww+87gA_fCVV7Wo+cGj ztEPAw_EFH(QaE&l(@!&DM#hT#3QuR*3Z{65E^e2}-KfYpNLgPRRxWa&X=X#?N*{t^hlmkP(^4q27Q6v(yaKFymAznkl&_ zG~E?+Xav`;a?TUjtgveD2(o&f!po$VrVT4j_rcMmMt!PoddD+mBaXRB;<#M?e`D<% zC4DcIcdxWc+jlE8lCf3Kxx)8F;KQcfA5+j=<7r<}(TiO1VJ2R=MmJ4ZLeeD7Ac~@y GsP-3LdPHvk literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/template.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/template.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d6c6662b58bcf48e32669d2db1f08d193cc4ea34 GIT binary patch literal 927 zcmZWoL2uJA6t*4LNlUs34WSKjsJQi#N{Hh$A*Qj*v`HIiqZccSy^t(TBHNv|s^u~c z9Dv^d-1ss40be;G{=rT>cWE~XNB;8O%gcM;_u{5Ts*Bjjo*vhA zrA+h(s-Zr%!JFm@*zHuBy(&$mN?g~)YJuT6Zo}4J0j4-dITrZ5t=4btoXki`1>od` z@uuh+&%CLZQ+V3S8K6HUb32&lw*foC`@&q-F*-sNnFl63Bj+9U38NXPnLIY#Z!1&! z9b+^_3>~9)=m$9h#WVVWo`6@96YxMKojg61rI2c=Qung5msF*ZrHS@m_nB>Hr8aR< zNMToMNY2WXk81%Fv7K5;B?}pAX+sT0Vx-_EPVK`BsdQGAJidstB0eqTU1(XaziNZ# zb#2#*SR3BBygJRHm|`$82iE$yfFnBrupd^HgdH(?Xj|aX0 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/validate_repository.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/validate_repository.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c4dbdb1e71aa96b83820f7643b7cbff4e3684c19 GIT binary patch literal 2799 zcma)8&5s*J7O(0rw|i`lGZ{iMJ4*~p0t1pKXhA~SC?bLl6SN8_0Rfg!tKC($yQe># zs&Ysy8?7=N_OOT9%L;Mw9Qhx(apsQd$e#9JKw80j)wVN9#DYCl^Xk>BSFe8W%O5qH z0fEo^|NK6@vPsB)aB=!$!{RL%lEK7Ck1)!tjOJF)qDb4BojX0p&`##&UeC*Gy;|<~ ze8Y3IdLHxwLwngq-sm;*X0HkBCi7YSOWJF(77N%0P@AlAhwN=muN?4f%tc3)9aeVY zGSADRb1%vg7O4ctK`A3!jPkJ2 zJcwfXh+*r-kgHJej7O47-KthgjSsYsYb7{`qg(g5kf0@0WtbFFMOn6IRT3jms)Iy= zqP)Dv*^SsjLzW+;9T?xikZ-~?Br}qdkiu*&YzF^N07K=>$c!FYHwm*22nCsidqP$# zW&tAh^p&@t0zoQ1mYs6YQ6v5cjR)Q7vzKJ&?GcX;lVW&!Nsqi;trd=24LhxKv?W7K#@N;0V_4c%YlfuqXu&6DcQL zenZ3-y#1$l_d$A)4EN)SRAmll^Kn_gX}OOu?vISi<^Et&#OO4+zcLljT{)&ZexNs( z7OuA22(J$lv3$El+qT#S`Y}&ngyS%t_&4}Pks~sK)mD zK0P3FX9k?GhTMfebJF?=rQ~B$^c`1Mw9lbC;6oo1meQHf09Vt;Hs@z^m?w z4gRF^AHZv;XBJz`Mh$9X<{nW-A6g%iH{hnZ`-S@n`IHpaCm11adRBZ3r@mdEa;j6z zIie4#+BP!Y5fwjz)Q@W^cPRzBAJS>6o`D@>dwR~i?%DOb?3sN`#e2*{P2qV~WB!+9 zX1_y@FgDMJ7g*hJfKD$gxo)_l{-Ad8M7ncCjiej?5zu(y5gMSipb*25-9e>${i^4V zseGs|&deQQ$LKi^@;~5>>4jrD{%T0xA)j5HdowS69=M%D3--T;)eH05tcJ9)poX`n z^~Imq#tEJKGvD0w;xWDZHBha^CDs7!;r!w<)R@+Zg#G_+!4g3qfeHqp- zA6s%qZLm$m4RbWyLoV2|J+A|`x7eE1(^nAPW9#l^c=7F1FSf+-G6K3)1c0_NoO&V# zfcy9V=XXXS&)d_p%RH~ruY>8=y)|_qqc6$n&h2#~KwadrQlv@mXt5XEY_!nbCh)cbz*` zN<(dQnTQ7J8%tZ=E1KRnnO;q2;j9j>;uV0ZssILhQT zAXdkI-8d{N#<-v|?s3C>6%E58%DKQtJYko(0KDrkB$j87TI2>aq4M)0DJQWRK2V=ShC=`h0jkDhQrz1-Ji+P+nG21y3KKwz<9yehC32^S_^gWiIzLpQ71 zHxZ_PX;+npywHxRuJyqr%Z#o1%%e&dmi?5+N^hJ3NC%NpBI!>QmjXQ{&{f)lo}?xc zZc~v^ld)(Z*ERKBVA&HJMl2eSdBL>5iVac6J%m8l<*58T>~kpg&1F*zvm{TH_SZeK zR#^niay;pSgGQW%2!nPCeqU)PQP4#iSp8 zmDDzL7hPY$FGUkKYk&lVC>Izq+CgM>9cKf!t}q)%YP1&!Yyjenk;6_RUd8qExUTcn zPXJVCdcF8F?!JM${z~7I;n9NKT^RBUn26<3hg$G;Aa1>%1KXopbesCL4g40}wHq+| zcH7KGj(XJlU*LKmvxPLAId5&#mgUm`zFQy(T+q<~-4^6S<7daQ=sA%0fgb>M4(2w{ z0&TgqM{nR?Ti3#nm2nvAMwFDpM2#l>Xq=c>(62q!Si1;EciC9GF}McevAf1ZR^jvd zZ=72<pxmI_Zo_WdLE{%$oB|2} literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/functions/__pycache__/version_to_install.cpython-38.pyc b/custom_components/hacs/helpers/functions/__pycache__/version_to_install.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf50c8b25d5c375b47ca5f27a2c3e1ed344e6d2f GIT binary patch literal 613 zcmZWmu};G<5Vez(mMX0fAAlk;b!k=xgerj$EHEIJES77#ja$c#YzI`t$b=Xf`2jwL zAIQpIFkvSts>DS;zjwapobT>Oqakp1`}T!iLjb-#*|ortnOo+-ISMjBAq`$Y25EQ) zCec%JEse!OBsmi$)5${VM6*&EZk6WDObK+tt$FNkJa@}{IQI-zU76r+;1ebbP@q z(*>6-dCWPcMHDXeHR0AGPtbj>2D%RGy0f literal 0 HcmV?d00001 diff --git a/custom_components/hacs/configuration_schema.py b/custom_components/hacs/helpers/functions/configuration_schema.py similarity index 53% rename from custom_components/hacs/configuration_schema.py rename to custom_components/hacs/helpers/functions/configuration_schema.py index 704ea4f..dfe632b 100644 --- a/custom_components/hacs/configuration_schema.py +++ b/custom_components/hacs/helpers/functions/configuration_schema.py @@ -1,16 +1,17 @@ """HACS Configuration Schemas.""" # pylint: disable=dangerous-default-value import voluptuous as vol -from .const import LOCALE + +from custom_components.hacs.const import LOCALE # Configuration: TOKEN = "token" SIDEPANEL_TITLE = "sidepanel_title" SIDEPANEL_ICON = "sidepanel_icon" +FRONTEND_REPO = "frontend_repo" +FRONTEND_REPO_URL = "frontend_repo_url" APPDAEMON = "appdaemon" NETDAEMON = "netdaemon" -PYTHON_SCRIPT = "python_script" -THEME = "theme" # Options: COUNTRY = "country" @@ -18,47 +19,47 @@ DEBUG = "debug" RELEASE_LIMIT = "release_limit" EXPERIMENTAL = "experimental" +# Config group +PATH_OR_URL = "frontend_repo_path_or_url" -def hacs_base_config_schema(config: dict = {}, config_flow: bool = False) -> dict: + +def hacs_base_config_schema(config: dict = {}) -> dict: """Return a shcema configuration dict for HACS.""" if not config: config = { TOKEN: "xxxxxxxxxxxxxxxxxxxxxxxxxxx", - SIDEPANEL_ICON: "mdi:alpha-c-box", - SIDEPANEL_TITLE: "HACS", - APPDAEMON: False, - NETDAEMON: False, - PYTHON_SCRIPT: False, - THEME: False, - } - if config_flow: - return { - vol.Required(TOKEN, default=config.get(TOKEN)): str, - vol.Optional(SIDEPANEL_TITLE, default=config.get(SIDEPANEL_TITLE)): str, - vol.Optional(SIDEPANEL_ICON, default=config.get(SIDEPANEL_ICON)): str, - vol.Optional(APPDAEMON, default=config.get(APPDAEMON)): bool, - vol.Optional(NETDAEMON, default=config.get(NETDAEMON)): bool, } return { vol.Required(TOKEN, default=config.get(TOKEN)): str, - vol.Optional(SIDEPANEL_TITLE, default=config.get(SIDEPANEL_TITLE)): str, - vol.Optional(SIDEPANEL_ICON, default=config.get(SIDEPANEL_ICON)): str, - vol.Optional(APPDAEMON, default=config.get(APPDAEMON)): bool, - vol.Optional(NETDAEMON, default=config.get(NETDAEMON)): bool, - vol.Optional(PYTHON_SCRIPT, default=config.get(PYTHON_SCRIPT)): bool, - vol.Optional(THEME, default=config.get(THEME)): bool, } def hacs_config_option_schema(options: dict = {}) -> dict: """Return a shcema for HACS configuration options.""" if not options: - options = {COUNTRY: "ALL", DEBUG: False, RELEASE_LIMIT: 5, EXPERIMENTAL: False} + options = { + APPDAEMON: False, + COUNTRY: "ALL", + DEBUG: False, + EXPERIMENTAL: False, + NETDAEMON: False, + RELEASE_LIMIT: 5, + SIDEPANEL_ICON: "hacs:hacs", + SIDEPANEL_TITLE: "HACS", + FRONTEND_REPO: "", + FRONTEND_REPO_URL: "", + } return { - vol.Optional(COUNTRY, default=options.get(COUNTRY)): vol.In(LOCALE), + vol.Optional(SIDEPANEL_TITLE, default=options.get(SIDEPANEL_TITLE)): str, + vol.Optional(SIDEPANEL_ICON, default=options.get(SIDEPANEL_ICON)): str, vol.Optional(RELEASE_LIMIT, default=options.get(RELEASE_LIMIT)): int, - vol.Optional(EXPERIMENTAL, default=options.get(EXPERIMENTAL)): bool, + vol.Optional(COUNTRY, default=options.get(COUNTRY)): vol.In(LOCALE), + vol.Optional(APPDAEMON, default=options.get(APPDAEMON)): bool, + vol.Optional(NETDAEMON, default=options.get(NETDAEMON)): bool, vol.Optional(DEBUG, default=options.get(DEBUG)): bool, + vol.Optional(EXPERIMENTAL, default=options.get(EXPERIMENTAL)): bool, + vol.Exclusive(FRONTEND_REPO, PATH_OR_URL): str, + vol.Exclusive(FRONTEND_REPO_URL, PATH_OR_URL): str, } diff --git a/custom_components/hacs/helpers/functions/constrains.py b/custom_components/hacs/helpers/functions/constrains.py new file mode 100644 index 0000000..ebef8ab --- /dev/null +++ b/custom_components/hacs/helpers/functions/constrains.py @@ -0,0 +1,43 @@ +"""HACS Startup constrains.""" +# pylint: disable=bad-continuation +import os + +from custom_components.hacs.const import ( + CUSTOM_UPDATER_LOCATIONS, + CUSTOM_UPDATER_WARNING, +) +from custom_components.hacs.helpers.functions.misc import version_left_higher_then_right +from custom_components.hacs.share import get_hacs + +MINIMUM_HA_VERSION = "0.110.0" + + +def check_constrains(): + """Check HACS constrains.""" + if not constrain_custom_updater(): + return False + if not constrain_version(): + return False + return True + + +def constrain_custom_updater(): + """Check if custom_updater exist.""" + hacs = get_hacs() + for location in CUSTOM_UPDATER_LOCATIONS: + if os.path.exists(location.format(hacs.core.config_path)): + msg = CUSTOM_UPDATER_WARNING.format(location.format(hacs.core.config_path)) + hacs.log.critical(msg) + return False + return True + + +def constrain_version(): + """Check if the version is valid.""" + hacs = get_hacs() + if not version_left_higher_then_right(hacs.system.ha_version, MINIMUM_HA_VERSION): + hacs.log.critical( + f"You need HA version {MINIMUM_HA_VERSION} or newer to use this integration." + ) + return False + return True diff --git a/custom_components/hacs/helpers/download.py b/custom_components/hacs/helpers/functions/download.py similarity index 51% rename from custom_components/hacs/helpers/download.py rename to custom_components/hacs/helpers/functions/download.py index 20414db..18851bf 100644 --- a/custom_components/hacs/helpers/download.py +++ b/custom_components/hacs/helpers/functions/download.py @@ -1,10 +1,22 @@ """Helpers to download repository content.""" +import os import pathlib import tempfile import zipfile -from custom_components.hacs.hacsbase.exceptions import HacsException -from custom_components.hacs.handler.download import async_download_file, async_save_file -from custom_components.hacs.helpers.filters import filter_content_return_one_of_type + +import async_timeout +import backoff +from queueman import QueueManager, concurrent + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.filters import ( + filter_content_return_one_of_type, +) +from custom_components.hacs.helpers.functions.logger import getLogger +from custom_components.hacs.helpers.functions.save import async_save_file +from custom_components.hacs.share import get_hacs + +_LOGGER = getLogger() class FileInformation: @@ -14,6 +26,36 @@ class FileInformation: self.name = name +@backoff.on_exception(backoff.expo, Exception, max_tries=5) +async def async_download_file(url): + """Download files, and return the content.""" + hacs = get_hacs() + if url is None: + return + + if "tags/" in url: + url = url.replace("tags/", "") + + _LOGGER.debug("Downloading %s", url) + + result = None + + with async_timeout.timeout(60, loop=hacs.hass.loop): + request = await hacs.session.get(url) + + # Make sure that we got a valid result + if request.status == 200: + result = await request.read() + else: + raise HacsException( + "Got status code {} when trying to download {}".format( + request.status, url + ) + ) + + return result + + def should_try_releases(repository): """Return a boolean indicating whether to download releases or not.""" if repository.data.zip_release: @@ -24,7 +66,7 @@ def should_try_releases(repository): return False if repository.data.category not in ["plugin", "theme"]: return False - if not repository.releases.releases: + if not repository.data.releases: return False return True @@ -94,9 +136,10 @@ def gather_files_to_download(repository): return files -async def download_zip(repository, validate): +async def download_zip_files(repository, validate): """Download ZIP archive from repository release.""" contents = [] + queue = QueueManager() try: for release in repository.releases.objects: repository.logger.info( @@ -108,33 +151,48 @@ async def download_zip(repository, validate): if not contents: return validate - for content in contents: - filecontent = await async_download_file(content.download_url) + for content in contents or []: + queue.add(async_download_zip_file(repository, content, validate)) - if filecontent is None: - validate.errors.append(f"[{content.name}] was not downloaded.") - continue + await queue.execute() + except (Exception, BaseException) as exception: # pylint: disable=broad-except + validate.errors.append(f"Download was not completed [{exception}]") - result = await async_save_file( - f"{tempfile.gettempdir()}/{repository.data.filename}", filecontent - ) - with zipfile.ZipFile( - f"{tempfile.gettempdir()}/{repository.data.filename}", "r" - ) as zip_file: - zip_file.extractall(repository.content.path.local) + return validate - if result: - repository.logger.info(f"download of {content.name} complete") - continue + +async def async_download_zip_file(repository, content, validate): + """Download ZIP archive from repository release.""" + try: + filecontent = await async_download_file(content.download_url) + + if filecontent is None: validate.errors.append(f"[{content.name}] was not downloaded.") - except Exception as exception: # pylint: disable=broad-except - validate.errors.append(f"Download was not complete [{exception}]") + return + + result = await async_save_file( + f"{tempfile.gettempdir()}/{repository.data.filename}", filecontent + ) + with zipfile.ZipFile( + f"{tempfile.gettempdir()}/{repository.data.filename}", "r" + ) as zip_file: + zip_file.extractall(repository.content.path.local) + + os.remove(f"{tempfile.gettempdir()}/{repository.data.filename}") + + if result: + repository.logger.info(f"Download of {content.name} completed") + return + validate.errors.append(f"[{content.name}] was not downloaded.") + except (Exception, BaseException) as exception: # pylint: disable=broad-except + validate.errors.append(f"Download was not completed [{exception}]") return validate async def download_content(repository): """Download the content of a directory.""" + queue = QueueManager() contents = gather_files_to_download(repository) repository.logger.debug(repository.data.filename) if not contents: @@ -144,38 +202,45 @@ async def download_content(repository): if repository.data.content_in_root and repository.data.filename: if content.name != repository.data.filename: continue - repository.logger.debug(f"Downloading {content.name}") + queue.add(dowload_repository_content(repository, content)) + await queue.execute() + return repository.validate - filecontent = await async_download_file(content.download_url) - if filecontent is None: - repository.validate.errors.append(f"[{content.name}] was not downloaded.") - continue +@concurrent(10) +async def dowload_repository_content(repository, content): + """Download content.""" + repository.logger.debug(f"Downloading {content.name}") - # Save the content of the file. - if repository.content.single or content.path is None: - local_directory = repository.content.path.local + filecontent = await async_download_file(content.download_url) - else: - _content_path = content.path - if not repository.data.content_in_root: - _content_path = _content_path.replace( - f"{repository.content.path.remote}", "" - ) - - local_directory = f"{repository.content.path.local}/{_content_path}" - local_directory = local_directory.split("/") - del local_directory[-1] - local_directory = "/".join(local_directory) - - # Check local directory - pathlib.Path(local_directory).mkdir(parents=True, exist_ok=True) - - local_file_path = (f"{local_directory}/{content.name}").replace("//", "/") - - result = await async_save_file(local_file_path, filecontent) - if result: - repository.logger.info(f"download of {content.name} complete") - continue + if filecontent is None: repository.validate.errors.append(f"[{content.name}] was not downloaded.") + return + # Save the content of the file. + if repository.content.single or content.path is None: + local_directory = repository.content.path.local + + else: + _content_path = content.path + if not repository.data.content_in_root: + _content_path = _content_path.replace( + f"{repository.content.path.remote}", "" + ) + + local_directory = f"{repository.content.path.local}/{_content_path}" + local_directory = local_directory.split("/") + del local_directory[-1] + local_directory = "/".join(local_directory) + + # Check local directory + pathlib.Path(local_directory).mkdir(parents=True, exist_ok=True) + + local_file_path = (f"{local_directory}/{content.name}").replace("//", "/") + + result = await async_save_file(local_file_path, filecontent) + if result: + repository.logger.info(f"Download of {content.name} completed") + return + repository.validate.errors.append(f"[{content.name}] was not downloaded.") diff --git a/custom_components/hacs/helpers/filters.py b/custom_components/hacs/helpers/functions/filters.py similarity index 100% rename from custom_components/hacs/helpers/filters.py rename to custom_components/hacs/helpers/functions/filters.py diff --git a/custom_components/hacs/helpers/functions/get_list_from_default.py b/custom_components/hacs/helpers/functions/get_list_from_default.py new file mode 100644 index 0000000..b2ed428 --- /dev/null +++ b/custom_components/hacs/helpers/functions/get_list_from_default.py @@ -0,0 +1,35 @@ +"""Helper to get default repositories.""" +import json +from typing import List + +from aiogithubapi import AIOGitHubAPIException + +from custom_components.hacs.enums import HacsCategory +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.information import get_repository +from custom_components.hacs.share import get_hacs + + +async def async_get_list_from_default(default: HacsCategory) -> List: + """Get repositories from default list.""" + hacs = get_hacs() + repositories = [] + + try: + repo = await get_repository( + hacs.session, + hacs.configuration.token, + "hacs/default", + ) + content = await repo.get_contents(default, repo.default_branch) + repositories = json.loads(content.content) + + except (AIOGitHubAPIException, HacsException) as exception: + hacs.log.error(exception) + + except (Exception, BaseException) as exception: + hacs.log.error(exception) + + hacs.log.debug("Got %s elements for %s", len(repositories), default) + + return repositories diff --git a/custom_components/hacs/helpers/information.py b/custom_components/hacs/helpers/functions/information.py similarity index 71% rename from custom_components/hacs/helpers/information.py rename to custom_components/hacs/helpers/functions/information.py index 2fde31f..7fb21d3 100644 --- a/custom_components/hacs/helpers/information.py +++ b/custom_components/hacs/helpers/functions/information.py @@ -1,8 +1,11 @@ """Return repository information if any.""" import json -from aiogithubapi import AIOGitHubException, AIOGitHub -from custom_components.hacs.handler.template import render_template -from custom_components.hacs.hacsbase.exceptions import HacsException + +from aiogithubapi import AIOGitHubAPIException, GitHub + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.template import render_template +from custom_components.hacs.share import get_hacs def info_file(repository): @@ -29,17 +32,19 @@ async def get_info_md_content(repository): return "" info = info.content.replace(" bool: + """Helper to check if path is safe to remove.""" + hacs = get_hacs() + paths = [ + Path(f"{hacs.core.config_path}/{hacs.configuration.appdaemon_path}"), + Path(f"{hacs.core.config_path}/{hacs.configuration.netdaemon_path}"), + Path(f"{hacs.core.config_path}/{hacs.configuration.plugin_path}"), + Path(f"{hacs.core.config_path}/{hacs.configuration.python_script_path}"), + Path(f"{hacs.core.config_path}/{hacs.configuration.theme_path}"), + Path(f"{hacs.core.config_path}/custom_components/"), + ] + if Path(path) in paths: + return False + return True diff --git a/custom_components/hacs/helpers/functions/logger.py b/custom_components/hacs/helpers/functions/logger.py new file mode 100644 index 0000000..900bc91 --- /dev/null +++ b/custom_components/hacs/helpers/functions/logger.py @@ -0,0 +1,19 @@ +"""Custom logger for HACS.""" +# pylint: disable=invalid-name +import logging +import os + +from ...const import PACKAGE_NAME + +_HACSLogger: logging.Logger = logging.getLogger(PACKAGE_NAME) + +if "GITHUB_ACTION" in os.environ: + logging.basicConfig( + format="::%(levelname)s:: %(message)s", + level="DEBUG", + ) + + +def getLogger(_name: str = None) -> logging.Logger: + """Return a Logger instance.""" + return _HACSLogger diff --git a/custom_components/hacs/helpers/misc.py b/custom_components/hacs/helpers/functions/misc.py similarity index 70% rename from custom_components/hacs/helpers/misc.py rename to custom_components/hacs/helpers/functions/misc.py index aeb19ed..83bd56c 100644 --- a/custom_components/hacs/helpers/misc.py +++ b/custom_components/hacs/helpers/functions/misc.py @@ -1,5 +1,11 @@ """Helper functions: misc""" +import re import semantic_version +from functools import lru_cache + +RE_REPOSITORY = re.compile( + r"(?:(?:.*github.com.)|^)([A-Za-z0-9-]+\/[\w.-]+?)(?:(?:\.git)?|(?:[^\w.-].*)?)$" +) def get_repository_name(repository) -> str: @@ -21,6 +27,7 @@ def get_repository_name(repository) -> str: ) +@lru_cache(maxsize=1024) def version_left_higher_then_right(new: str, old: str) -> bool: """Return a bool if source is newer than target, will also be true if identical.""" if not isinstance(new, str) or not isinstance(old, str): @@ -28,3 +35,11 @@ def version_left_higher_then_right(new: str, old: str) -> bool: if new == old: return True return semantic_version.Version.coerce(new) > semantic_version.Version.coerce(old) + + +def extract_repository_from_url(url: str) -> str or None: + """Extract the owner/repo part form a URL.""" + match = re.match(RE_REPOSITORY, url) + if not match: + return None + return match.group(1).lower() diff --git a/custom_components/hacs/helpers/functions/path_exsist.py b/custom_components/hacs/helpers/functions/path_exsist.py new file mode 100644 index 0000000..5e941d6 --- /dev/null +++ b/custom_components/hacs/helpers/functions/path_exsist.py @@ -0,0 +1,13 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +import os + +from custom_components.hacs.share import get_hacs + + +def path_exsist(path) -> bool: + return os.path.exists(path) + + +async def async_path_exsist(path) -> bool: + hass = get_hacs().hass + return await hass.async_add_executor_job(path_exsist, path) diff --git a/custom_components/hacs/helpers/functions/register_repository.py b/custom_components/hacs/helpers/functions/register_repository.py new file mode 100644 index 0000000..2fad499 --- /dev/null +++ b/custom_components/hacs/helpers/functions/register_repository.py @@ -0,0 +1,70 @@ +"""Register a repository.""" +from aiogithubapi import AIOGitHubAPIException + +from custom_components.hacs.helpers.classes.exceptions import ( + HacsException, + HacsExpectedException, +) +from custom_components.hacs.share import get_hacs + +from ...repositories import RERPOSITORY_CLASSES + + +# @concurrent(15, 5) +async def register_repository(full_name, category, check=True, ref=None): + """Register a repository.""" + hacs = get_hacs() + + if full_name in hacs.common.skip: + if full_name != "hacs/integration": + raise HacsExpectedException(f"Skipping {full_name}") + + if category not in RERPOSITORY_CLASSES: + raise HacsException(f"{category} is not a valid repository category.") + + repository = RERPOSITORY_CLASSES[category](full_name) + if check: + try: + await repository.async_registration(ref) + if hacs.status.new: + repository.data.new = False + if repository.validate.errors: + hacs.common.skip.append(repository.data.full_name) + if not hacs.status.startup: + hacs.log.error("Validation for %s failed.", full_name) + if hacs.system.action: + raise HacsException(f"::error:: Validation for {full_name} failed.") + return repository.validate.errors + if hacs.system.action: + repository.logger.info("%s Validation completed", repository) + else: + repository.logger.info("%s Registration completed", repository) + except AIOGitHubAPIException as exception: + hacs.common.skip.append(repository.data.full_name) + raise HacsException( + f"Validation for {full_name} failed with {exception}." + ) from None + + exists = ( + False + if str(repository.data.id) == "0" + else [x for x in hacs.repositories if str(x.data.id) == str(repository.data.id)] + ) + + if exists: + if exists[0] in hacs.repositories: + hacs.repositories.remove(exists[0]) + + else: + if hacs.hass is not None and ( + (check and repository.data.new) or hacs.status.new + ): + hacs.hass.bus.async_fire( + "hacs/repository", + { + "action": "registration", + "repository": repository.data.full_name, + "repository_id": repository.data.id, + }, + ) + hacs.repositories.append(repository) diff --git a/custom_components/hacs/helpers/functions/remaining_github_calls.py b/custom_components/hacs/helpers/functions/remaining_github_calls.py new file mode 100644 index 0000000..27a0afe --- /dev/null +++ b/custom_components/hacs/helpers/functions/remaining_github_calls.py @@ -0,0 +1,32 @@ +"""Helper to calculate the remaining calls to github.""" +import math + +from custom_components.hacs.helpers.functions.logger import getLogger + +_LOGGER = getLogger() + + +async def remaining(github): + """Helper to calculate the remaining calls to github.""" + try: + ratelimits = await github.get_rate_limit() + except (BaseException, Exception) as exception: # pylint: disable=broad-except + _LOGGER.error(exception) + return None + if ratelimits.get("remaining") is not None: + return int(ratelimits["remaining"]) + return 0 + + +async def get_fetch_updates_for(github): + """Helper to calculate the number of repositories we can fetch data for.""" + margin = 1000 + limit = await remaining(github) + pr_repo = 15 + + if limit is None: + return None + + if limit - margin <= pr_repo: + return 0 + return math.floor((limit - margin) / pr_repo) diff --git a/custom_components/hacs/helpers/functions/save.py b/custom_components/hacs/helpers/functions/save.py new file mode 100644 index 0000000..05a6e03 --- /dev/null +++ b/custom_components/hacs/helpers/functions/save.py @@ -0,0 +1,52 @@ +"""Download.""" +import gzip +import os +import shutil + +import aiofiles + +from custom_components.hacs.helpers.functions.logger import getLogger + +_LOGGER = getLogger() + + +async def async_save_file(location, content): + """Save files.""" + _LOGGER.debug("Saving %s", location) + mode = "w" + encoding = "utf-8" + errors = "ignore" + + if not isinstance(content, str): + mode = "wb" + encoding = None + errors = None + + try: + async with aiofiles.open( + location, mode=mode, encoding=encoding, errors=errors + ) as outfile: + await outfile.write(content) + outfile.close() + + # Create gz for .js files + if os.path.isfile(location): + if location.endswith(".js") or location.endswith(".css"): + with open(location, "rb") as f_in: + with gzip.open(location + ".gz", "wb") as f_out: + shutil.copyfileobj(f_in, f_out) + + # Remove with 2.0 + if "themes" in location and location.endswith(".yaml"): + filename = location.split("/")[-1] + base = location.split("/themes/")[0] + combined = f"{base}/themes/{filename}" + if os.path.exists(combined): + _LOGGER.info("Removing old theme file %s", combined) + os.remove(combined) + + except (Exception, BaseException) as error: # pylint: disable=broad-except + _LOGGER.error("Could not write data to %s - %s", location, error) + return False + + return os.path.exists(location) diff --git a/custom_components/hacs/helpers/functions/store.py b/custom_components/hacs/helpers/functions/store.py new file mode 100644 index 0000000..6ecdfa2 --- /dev/null +++ b/custom_components/hacs/helpers/functions/store.py @@ -0,0 +1,34 @@ +"""Storage handers.""" +# pylint: disable=import-outside-toplevel +from homeassistant.helpers.json import JSONEncoder + +from custom_components.hacs.const import VERSION_STORAGE + + +def get_store_for_key(hass, key): + """Create a Store object for the key.""" + key = key if "/" in key else f"hacs.{key}" + from homeassistant.helpers.storage import Store + + return Store(hass, VERSION_STORAGE, key, encoder=JSONEncoder) + + +async def async_load_from_store(hass, key): + """Load the retained data from store and return de-serialized data.""" + store = get_store_for_key(hass, key) + restored = await store.async_load() + if restored is None: + return {} + return restored + + +async def async_save_to_store(hass, key, data): + """Generate dynamic data to store and save it to the filesystem.""" + await get_store_for_key(hass, key).async_save(data) + + +async def async_remove_store(hass, key): + """Remove a store element that should no longer be used""" + if "/" not in key: + return + await get_store_for_key(hass, key).async_remove() diff --git a/custom_components/hacs/handler/template.py b/custom_components/hacs/helpers/functions/template.py similarity index 72% rename from custom_components/hacs/handler/template.py rename to custom_components/hacs/helpers/functions/template.py index ca77e7a..4f59840 100644 --- a/custom_components/hacs/handler/template.py +++ b/custom_components/hacs/helpers/functions/template.py @@ -1,29 +1,32 @@ -"""Custom template support.""" -# pylint: disable=broad-except -from jinja2 import Template -from integrationhelper import Logger - - -def render_template(content, context): - """Render templates in content.""" - # Fix None issues - if context.releases.last_release_object is not None: - prerelease = context.releases.last_release_object.prerelease - else: - prerelease = False - - # Render the template - try: - render = Template(content) - render = render.render( - installed=context.status.installed, - pending_update=context.pending_upgrade, - prerelease=prerelease, - selected_tag=context.status.selected_tag, - version_available=context.releases.last_release, - version_installed=context.display_installed_version, - ) - return render - except Exception as exception: - Logger("hacs.template").debug(exception) - return content +"""Custom template support.""" +# pylint: disable=broad-except +from jinja2 import Template + +from custom_components.hacs.helpers.functions.logger import getLogger + +_LOGGER = getLogger() + + +def render_template(content, context): + """Render templates in content.""" + # Fix None issues + if context.releases.last_release_object is not None: + prerelease = context.releases.last_release_object.prerelease + else: + prerelease = False + + # Render the template + try: + render = Template(content) + render = render.render( + installed=context.data.installed, + pending_update=context.pending_upgrade, + prerelease=prerelease, + selected_tag=context.data.selected_tag, + version_available=context.releases.last_release, + version_installed=context.display_installed_version, + ) + return render + except (Exception, BaseException) as exception: + _LOGGER.debug(exception) + return content diff --git a/custom_components/hacs/helpers/functions/validate_repository.py b/custom_components/hacs/helpers/functions/validate_repository.py new file mode 100644 index 0000000..57e4381 --- /dev/null +++ b/custom_components/hacs/helpers/functions/validate_repository.py @@ -0,0 +1,98 @@ +"""Helper to do common validation for repositories.""" +from aiogithubapi import AIOGitHubAPIException + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.information import ( + get_releases, + get_repository, + get_tree, +) +from custom_components.hacs.helpers.functions.version_to_install import ( + version_to_install, +) +from custom_components.hacs.share import get_hacs, is_removed + + +async def common_validate(repository, ignore_issues=False): + """Common validation steps of the repository.""" + repository.validate.errors = [] + + # Make sure the repository exist. + repository.logger.debug("%s Checking repository.", repository) + await common_update_data(repository, ignore_issues) + + # Step 6: Get the content of hacs.json + await repository.get_repository_manifest_content() + + +async def common_update_data(repository, ignore_issues=False): + """Common update data.""" + hacs = get_hacs() + releases = [] + try: + repository_object = await get_repository( + hacs.session, hacs.configuration.token, repository.data.full_name + ) + repository.repository_object = repository_object + repository.data.update_data(repository_object.attributes) + except (AIOGitHubAPIException, HacsException) as exception: + if not hacs.status.startup: + repository.logger.error("%s %s", repository, exception) + if not ignore_issues: + repository.validate.errors.append("Repository does not exist.") + raise HacsException(exception) from None + + # Make sure the repository is not archived. + if repository.data.archived and not ignore_issues: + repository.validate.errors.append("Repository is archived.") + raise HacsException("Repository is archived.") + + # Make sure the repository is not in the blacklist. + if is_removed(repository.data.full_name) and not ignore_issues: + repository.validate.errors.append("Repository is in the blacklist.") + raise HacsException("Repository is in the blacklist.") + + # Get releases. + try: + releases = await get_releases( + repository.repository_object, + repository.data.show_beta, + hacs.configuration.release_limit, + ) + if releases: + repository.data.releases = True + repository.releases.objects = [x for x in releases if not x.draft] + repository.data.published_tags = [ + x.tag_name for x in repository.releases.objects + ] + repository.data.last_version = next(iter(repository.data.published_tags)) + + except (AIOGitHubAPIException, HacsException): + repository.data.releases = False + + if not repository.force_branch: + repository.ref = version_to_install(repository) + if repository.data.releases: + for release in repository.releases.objects or []: + if release.tag_name == repository.ref: + assets = release.assets + if assets: + downloads = next(iter(assets)).attributes.get("download_count") + repository.data.downloads = downloads + + repository.logger.debug( + "%s Running checks against %s", repository, repository.ref.replace("tags/", "") + ) + + try: + repository.tree = await get_tree(repository.repository_object, repository.ref) + if not repository.tree: + raise HacsException("No files in tree") + repository.treefiles = [] + for treefile in repository.tree: + repository.treefiles.append(treefile.full_path) + except (AIOGitHubAPIException, HacsException) as exception: + if not hacs.status.startup: + repository.logger.error("%s %s", repository, exception) + if not ignore_issues: + raise HacsException(exception) from None diff --git a/custom_components/hacs/helpers/functions/version_to_install.py b/custom_components/hacs/helpers/functions/version_to_install.py new file mode 100644 index 0000000..75dd4ba --- /dev/null +++ b/custom_components/hacs/helpers/functions/version_to_install.py @@ -0,0 +1,20 @@ +"""Install helper for repositories.""" + + +def version_to_install(repository): + """Determine which version to isntall.""" + if repository.data.last_version is not None: + if repository.data.selected_tag is not None: + if repository.data.selected_tag == repository.data.last_version: + repository.data.selected_tag = None + return repository.data.last_version + return repository.data.selected_tag + return repository.data.last_version + if repository.data.selected_tag is not None: + if repository.data.selected_tag == repository.data.default_branch: + return repository.data.default_branch + if repository.data.selected_tag in repository.data.published_tags: + return repository.data.selected_tag + if repository.data.default_branch is None: + return "main" + return repository.data.default_branch diff --git a/custom_components/hacs/helpers/get_defaults.py b/custom_components/hacs/helpers/get_defaults.py deleted file mode 100644 index 744540d..0000000 --- a/custom_components/hacs/helpers/get_defaults.py +++ /dev/null @@ -1,44 +0,0 @@ -"""Helpers to get default repositories.""" -import json -from aiogithubapi import AIOGitHub, AIOGitHubException -from integrationhelper import Logger -from custom_components.hacs.helpers.information import get_repository - - -async def get_default_repos_orgs(github: type(AIOGitHub), category: str) -> dict: - """Gets default org repositories.""" - repositories = [] - logger = Logger("hacs") - orgs = { - "plugin": "custom-cards", - "integration": "custom-components", - "theme": "home-assistant-community-themes", - } - if category not in orgs: - return repositories - - try: - repos = await github.get_org_repos(orgs[category]) - for repo in repos: - repositories.append(repo.full_name) - - except AIOGitHubException as exception: - logger.error(exception) - - return repositories - - -async def get_default_repos_lists(session, token, default: str) -> dict: - """Gets repositories from default list.""" - repositories = [] - logger = Logger("hacs") - - try: - repo = await get_repository(session, token, "hacs/default") - content = await repo.get_contents(default) - repositories = json.loads(content.content) - - except AIOGitHubException as exception: - logger.error(exception) - - return repositories diff --git a/custom_components/hacs/helpers/install.py b/custom_components/hacs/helpers/install.py deleted file mode 100644 index 7eab265..0000000 --- a/custom_components/hacs/helpers/install.py +++ /dev/null @@ -1,128 +0,0 @@ -"""Install helper for repositories.""" -import os -import tempfile -from custom_components.hacs.globals import get_hacs -from custom_components.hacs.hacsbase.exceptions import HacsException -from custom_components.hacs.hacsbase.backup import Backup, BackupNetDaemon -from custom_components.hacs.helpers.download import download_content - - -async def install_repository(repository): - """Common installation steps of the repository.""" - persistent_directory = None - await repository.update_repository() - repository.validate.errors = [] - - if not repository.can_install: - raise HacsException( - "The version of Home Assistant is not compatible with this version" - ) - - version = version_to_install(repository) - if version == repository.data.default_branch: - repository.ref = version - else: - repository.ref = f"tags/{version}" - - if repository.status.installed and repository.data.category == "netdaemon": - persistent_directory = BackupNetDaemon(repository) - persistent_directory.create() - - elif repository.data.persistent_directory: - if os.path.exists( - f"{repository.content.path.local}/{repository.data.persistent_directory}" - ): - persistent_directory = Backup( - f"{repository.content.path.local}/{repository.data.persistent_directory}", - tempfile.gettempdir() + "/hacs_persistent_directory/", - ) - persistent_directory.create() - - if repository.status.installed and not repository.content.single: - backup = Backup(repository.content.path.local) - backup.create() - - if repository.data.zip_release and version != repository.data.default_branch: - await repository.download_zip(repository) - else: - await download_content(repository) - - if repository.validate.errors: - for error in repository.validate.errors: - repository.logger.error(error) - if repository.status.installed and not repository.content.single: - backup.restore() - - if repository.status.installed and not repository.content.single: - backup.cleanup() - - if persistent_directory is not None: - persistent_directory.restore() - persistent_directory.cleanup() - - if repository.validate.success: - if repository.data.full_name not in repository.hacs.common.installed: - if repository.data.full_name == "hacs/integration": - repository.hacs.common.installed.append(repository.data.full_name) - repository.status.installed = True - repository.versions.installed_commit = repository.versions.available_commit - - if version == repository.data.default_branch: - repository.versions.installed = None - else: - repository.versions.installed = version - - await reload_after_install(repository) - installation_complete(repository) - - -async def reload_after_install(repository): - """Reload action after installation success.""" - if repository.data.category == "integration": - if repository.config_flow: - if repository.data.full_name != "hacs/integration": - await repository.reload_custom_components() - repository.pending_restart = True - - elif repository.data.category == "theme": - try: - await repository.hacs.hass.services.async_call( - "frontend", "reload_themes", {} - ) - except Exception: # pylint: disable=broad-except - pass - elif repository.data.category == "netdaemon": - try: - await repository.hacs.hass.services.async_call( - "hassio", "addon_restart", {"addon": "e466aeb3_netdaemon"} - ) - except Exception: # pylint: disable=broad-except - pass - - -def installation_complete(repository): - """Action to run when the installation is complete.""" - hacs = get_hacs() - hacs.hass.bus.async_fire( - "hacs/repository", - {"id": 1337, "action": "install", "repository": repository.data.full_name}, - ) - - -def version_to_install(repository): - """Determine which version to isntall.""" - if repository.versions.available is not None: - if repository.status.selected_tag is not None: - if repository.status.selected_tag == repository.versions.available: - repository.status.selected_tag = None - return repository.versions.available - return repository.status.selected_tag - return repository.versions.available - if repository.status.selected_tag is not None: - if repository.status.selected_tag == repository.data.default_branch: - return repository.data.default_branch - if repository.status.selected_tag in repository.releases.published_tags: - return repository.status.selected_tag - if repository.data.default_branch is None: - return "master" - return repository.data.default_branch diff --git a/custom_components/hacs/helpers/methods/__init__.py b/custom_components/hacs/helpers/methods/__init__.py new file mode 100644 index 0000000..26d9422 --- /dev/null +++ b/custom_components/hacs/helpers/methods/__init__.py @@ -0,0 +1,30 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +from custom_components.hacs.helpers.methods.installation import ( + RepositoryMethodInstall, + RepositoryMethodPostInstall, + RepositoryMethodPreInstall, +) +from custom_components.hacs.helpers.methods.registration import ( + RepositoryMethodPostRegistration, + RepositoryMethodPreRegistration, + RepositoryMethodRegistration, +) +from custom_components.hacs.helpers.methods.reinstall_if_needed import ( + RepositoryMethodReinstallIfNeeded, +) + + +class RepositoryHelperMethods( + RepositoryMethodReinstallIfNeeded, + RepositoryMethodInstall, + RepositoryMethodPostInstall, + RepositoryMethodPreInstall, + RepositoryMethodPreRegistration, + RepositoryMethodRegistration, + RepositoryMethodPostRegistration, +): + """Collection of repository methods that are nested to all repositories.""" + + +class HacsHelperMethods: + """Helper class for HACS methods""" diff --git a/custom_components/hacs/helpers/methods/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/helpers/methods/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..586b3b797f60f4f4b8f322908b4cf922228f99d8 GIT binary patch literal 1078 zcmb7DJ#W-77`F3qNsgjVa8i>16zeqQH8h;9HCQppuz7d_OJypn^u;@{3&oFXV+$dgC%1tQ%Ghi0@fN6|$6QUa zg}SXLa8?CVW86I7#;cC_@q7lrftOz3dc!U`blp5~0T9I; z(STzb^1vCg;lvcg5_IQ>`TXWc2?3=iPSl*}zlIUnI%bGn6qXbkNDamUCsq+>;myhl zOg_7cnv}7+kib|oWK1f)5`H{l?0i*-9!VJEs$}d6>HCgo8-5>5N>$IRvq`x!R!LSW zxl}dO)=U;f=|W4`OnO2S#;UrqjOEJVS=~N5o%$-?@mAHp4>gJIPd+}a8 MiibE@|9(ZIKQpQ{pa1{> literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/methods/__pycache__/installation.cpython-38.pyc b/custom_components/hacs/helpers/methods/__pycache__/installation.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0845fdd7da683bc7714b13cf75adf8ba0e419ed1 GIT binary patch literal 3840 zcmb7HPmkNi73YwYL}|HN?b`7=wwuUFx3PjmO46W3(ZaBkG<6P9j3n)WKtXUhBdxR) zsmxGuEUYiJk&}V?o)pmL=I_v3&;0^(Y)<_OLDBU0MwC|T)jBAF8O?id=KY^HPk-Fp ztXcRzdHm1tuMNxko`BWQ1#k;PJ;uZ>&Jrt_|FVDqwiA2g1P&vdlenW&P%>*bDUT{a z#jHz-H>w6zvo0sK(MGT_@&lh)f3SFkdyg#c3Fn~`Yyz(G8sM6N>wq`757;;G7T@Ic zM^@0_>@%yi^>1iobz4k3x8HjoKbv==SpEL1Sd8;QmIBe>*}XK$A|A$Bnu|0CZs$uO z706+pg@aV(QIfQ5U4Ac$4<}>2HUI63{DVl0vNR?UtDghl7KXZkNm%59z~+qG-!krS z_mLYo1ytf?K(0}>qOW`^#+e%AnY{n8$PY4pSBj73;=50oE=!S5WEwMYU3|2r@ed3| zVc^zh)>GDfib=alBt7W^Yo;G=c3@&}(C@?(m1iRuJQ`;yOjex(c%^e7k}(YLjEr#A zDNK*ZQ|q zkFAej*d16}P;8=*sV%2Fed*Ilnhw%_6N)ugb#AI$jMcOWH?F}q$^1m*g0~!9PO^Sq zNbL^NUM7*_mfWOi={!lM>Yeb6B|E^YGA4`ptj?xyKF1AbPq#|i3&S)Ti7?c)FdSuk zlF-@@!!IULGN;THx&Rp~3K`W%E9qgXW0(Z+Sluz07?@KZx%d`_x`OGvw1i7bdiPly z7Fj5(IC_5UJ_HraG zC7vZ|e6b0TYJjLQga8;%#BIXo#pH279@mi7n zh47XuFOps_&~%9=3dh>G?t*X`!`v{BNrKOh>i=16u0E^nU3j4IYIl8{&lDQDu~dEC zZP_g@%Zh7Fyk1$@gsSn&=cutL8_%9UlYkX4G{xgC7UvfaZiD88F6Pp(Jgm$-D#yG; z*kP5Izjco7Wr?XQON`f@zPw09)aQInE%|V|Wh&@NVhw&%Lf%ytL3gx0;JO^eR7-X3 zWZ(2U`F#z`m2|GjWhTpOuu!`^$|LQh;+}R%F3n15XMdt}ZSM5mKnh*$O_Ib^mvt#E zDT((m6xF+V`SMte*{uFw*PV8r7nwD;rd4`2u}N1WWNmg|1@$_{N!23Eso27W77Y{I zck#|}_JA!53@aW4HnfL$P!BzMBX_y|ko|@ADGN$r8FNM51lA#J?ok1HU>{K)REM=g zTL!caH&UOwNA@??-wb4^zI<$cH+c#B9_Mv)mL0L#)-fKU^iPJ`kpIfJhxTj-kkR8C zi<8vqumNt)F$4X=dRl&Q_QElGn_cUn4XX;i=B)pON#C5h^n@w@gafp9j-0s$X6G(; z{%UEDV<2mG`N%q^LTfyf*7FyO!n^PovL8FY1_!J@u>QRBfbPVV+0`SLvi=GCVs0Jj zLA%(H^!OT*uaWIWGSR+U(!K-jXNHDn`>+by-(k^wH!CP4?WHHSe9N4F8FO=Zedr%n zu=_uA9QO|G0y4DOD|5WLi&a{O6z93SqP#;P`b%eao=LV?4tv03zkQkQ*mb*xy$yz)v>OodsR6DNj`?;S9E`-) zwtNTTXAy Y$vKMpGB^}&^x!NJ@d{jE#%;P`7ZppkJob1z^UXK&W@%}Gz=)vdlDo1u(4|VsL6N5v-c|$mIKRQv7_=aJx}$2pwnsD^0UL8(2LhU7&-nWI0xOYJmXz zIB?Hk+U&GoGa(1$3l;P$FmXb@I1|`CgPc8uBx2?wr+d7;Q3rRUHP9^~0rNB|rPyqZ zwQOyibFP*I(93lx6A>}vXGO0km1%rPR6fXiW_4cw)KN`v$S@^q6tQ5MucJmFLe3W zONBjPmd9&whSN3(;w(D>4FGrFS*9*qW+6N7T#EjDq9Y3I>_(mn@jJ|>TE^u%F8Ec= z1=}iYJzaqdd|-P@Kv#T)Cvz2r`P@OZOZr)B*5Oxx#BL43xrTPZl2tHWLUvF7DiYIKBC2v!O s@x-_1nr<7LZo49y!tP}ImWA3{_}5n^*g9*D+i2ESX6S@$#ap@c2iKjr>i_@% literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/methods/__pycache__/reinstall_if_needed.cpython-38.pyc b/custom_components/hacs/helpers/methods/__pycache__/reinstall_if_needed.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54952f91a3499c55fe78ca4081b2211db8a50467 GIT binary patch literal 807 zcmah{y^ho{5VrHP8%}%tNHidd(A`Q$($Im>0pjP1TckVDq_KRqvzwJ;2iuX@6Wvkr z2ozBA9MrskTPj|GigET1MFPY~Gnuh7^UZvo*Sotr2;|j=Z{$vd(Dx9GC4t}o)b4}f zh+~ObTwxsiNJed3#TdOnJm$%J#1nzf<0|3l37SuSKn9v)A3uKb6h1eo-6%#n>Q+R& zwT*Qw{4kanf&)-{8;n2|=BSD|=Fvwy03=n+F_cdI-DA;d+c<4DN5ZW%KNgL$j!Jo0 zFNENNF9334gzJrB7|LBr@B_|Q&|#=|XpPp`?70ZMc%7`%b9@Nny+n2oP9~qG9Dg1L z2IOdQcJHXMwo%KxHoDEFW>n_SPxf=W(!J#QsmRS`FM=2I%qN^W>aSe%C%6gle#*3R zLOGuVMtnLH@)N0-OJRH}jL`;0NUV_c7i6wN&d+BhB-PEbWW9B|B}})SR#3#2E6TtK z*$HFIcA&(TzZ)S-g kub6AJvc+iW2KI+yBf!9}38+4Mxb5>%K(L{QSu~4(0dURR)c^nh literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/methods/installation.py b/custom_components/hacs/helpers/methods/installation.py new file mode 100644 index 0000000..81354dd --- /dev/null +++ b/custom_components/hacs/helpers/methods/installation.py @@ -0,0 +1,113 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +import os +import tempfile +from abc import ABC + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.download import download_content +from custom_components.hacs.helpers.functions.version_to_install import ( + version_to_install, +) +from custom_components.hacs.operational.backup import Backup, BackupNetDaemon + + +class RepositoryMethodPreInstall(ABC): + async def async_pre_install(self) -> None: + pass + + async def _async_pre_install(self) -> None: + self.logger.info("Running pre installation steps") + await self.async_pre_install() + self.logger.info("Pre installation steps completed") + + +class RepositoryMethodInstall(ABC): + async def async_install(self) -> None: + await self._async_pre_install() + self.logger.info("Running installation steps") + await async_install_repository(self) + self.logger.info("Installation steps completed") + await self._async_post_install() + + +class RepositoryMethodPostInstall(ABC): + async def async_post_installation(self) -> None: + pass + + async def _async_post_install(self) -> None: + self.logger.info("Running post installation steps") + await self.async_post_installation() + self.data.new = False + self.hacs.hass.bus.async_fire( + "hacs/repository", + {"id": 1337, "action": "install", "repository": self.data.full_name}, + ) + self.logger.info("Post installation steps completed") + + +async def async_install_repository(repository): + """Common installation steps of the repository.""" + persistent_directory = None + await repository.update_repository() + if repository.content.path.local is None: + raise HacsException("repository.content.path.local is None") + repository.validate.errors = [] + + if not repository.can_install: + raise HacsException( + "The version of Home Assistant is not compatible with this version" + ) + + version = version_to_install(repository) + if version == repository.data.default_branch: + repository.ref = version + else: + repository.ref = f"tags/{version}" + + if repository.data.installed and repository.data.category == "netdaemon": + persistent_directory = BackupNetDaemon(repository) + persistent_directory.create() + + elif repository.data.persistent_directory: + if os.path.exists( + f"{repository.content.path.local}/{repository.data.persistent_directory}" + ): + persistent_directory = Backup( + f"{repository.content.path.local}/{repository.data.persistent_directory}", + tempfile.gettempdir() + "/hacs_persistent_directory/", + ) + persistent_directory.create() + + if repository.data.installed and not repository.content.single: + backup = Backup(repository.content.path.local) + backup.create() + + if repository.data.zip_release and version != repository.data.default_branch: + await repository.download_zip_files(repository) + else: + await download_content(repository) + + if repository.validate.errors: + for error in repository.validate.errors: + repository.logger.error(error) + if repository.data.installed and not repository.content.single: + backup.restore() + + if repository.data.installed and not repository.content.single: + backup.cleanup() + + if persistent_directory is not None: + persistent_directory.restore() + persistent_directory.cleanup() + + if repository.validate.success: + if repository.data.full_name not in repository.hacs.common.installed: + if repository.data.full_name == "hacs/integration": + repository.hacs.common.installed.append(repository.data.full_name) + repository.data.installed = True + repository.data.installed_commit = repository.data.last_commit + + if version == repository.data.default_branch: + repository.data.installed_version = None + else: + repository.data.installed_version = version diff --git a/custom_components/hacs/helpers/methods/registration.py b/custom_components/hacs/helpers/methods/registration.py new file mode 100644 index 0000000..d6f2288 --- /dev/null +++ b/custom_components/hacs/helpers/methods/registration.py @@ -0,0 +1,43 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member, attribute-defined-outside-init +from abc import ABC + +from custom_components.hacs.validate import async_run_repository_checks + + +class RepositoryMethodPreRegistration(ABC): + async def async_pre_registration(self): + pass + + +class RepositoryMethodRegistration(ABC): + async def registration(self, ref=None) -> None: + self.logger.warning( + "'registration' is deprecated, use 'async_registration' instead" + ) + await self.async_registration(ref) + + async def async_registration(self, ref=None) -> None: + # Run local pre registration steps. + await self.async_pre_registration() + + if ref is not None: + self.data.selected_tag = ref + self.ref = ref + self.force_branch = True + + if not await self.validate_repository(): + return False + + # Run common registration steps. + await self.common_registration() + + # Set correct local path + self.content.path.local = self.localpath + + # Run local post registration steps. + await self.async_post_registration() + + +class RepositoryMethodPostRegistration(ABC): + async def async_post_registration(self): + await async_run_repository_checks(self) diff --git a/custom_components/hacs/helpers/methods/reinstall_if_needed.py b/custom_components/hacs/helpers/methods/reinstall_if_needed.py new file mode 100644 index 0000000..a83353b --- /dev/null +++ b/custom_components/hacs/helpers/methods/reinstall_if_needed.py @@ -0,0 +1,12 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +from abc import ABC + +from custom_components.hacs.helpers.functions.path_exsist import async_path_exsist + + +class RepositoryMethodReinstallIfNeeded(ABC): + async def async_reinstall_if_needed(self) -> None: + if self.data.installed: + if not await async_path_exsist(self.content.path.local): + self.logger.error("Missing from local FS, should be reinstalled.") + # await self.async_install() diff --git a/custom_components/hacs/helpers/network.py b/custom_components/hacs/helpers/network.py deleted file mode 100644 index acc0a1f..0000000 --- a/custom_components/hacs/helpers/network.py +++ /dev/null @@ -1,8 +0,0 @@ -"""Verify network.""" -from socket import gaierror -from integrationhelper import Logger - - -def internet_connectivity_check(host="api.github.com"): - """Verify network connectivity.""" - return True diff --git a/custom_components/hacs/helpers/properties/__init__.py b/custom_components/hacs/helpers/properties/__init__.py new file mode 100644 index 0000000..4267caf --- /dev/null +++ b/custom_components/hacs/helpers/properties/__init__.py @@ -0,0 +1,16 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +from custom_components.hacs.helpers.properties.can_be_installed import ( + RepositoryPropertyCanBeInstalled, +) +from custom_components.hacs.helpers.properties.custom import RepositoryPropertyCustom +from custom_components.hacs.helpers.properties.pending_update import ( + RepositoryPropertyPendingUpdate, +) + + +class RepositoryHelperProperties( + RepositoryPropertyPendingUpdate, + RepositoryPropertyCustom, + RepositoryPropertyCanBeInstalled, +): + pass diff --git a/custom_components/hacs/helpers/properties/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/helpers/properties/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85d0ba23015d9eb5be676ba340e7b7aacd8e5d7d GIT binary patch literal 663 zcmb7Cu};G<5Ve!KEhS|}EU;!tm=Hn*RWJZcsX$%4SRpngl4FNB89KwrkMJ@4KvpJx z0SSq-Q>m5802le`PUm~~a_8A_I6z=GukY;0L+HZ>>(+%}49AQBIO15M98-*~?@2HB zsqcJWc5*@qMwf_pIC(*w2qJuep9$^q?i~typTK}Z+#KABLYu78 zND#A>lnFBl{ie^DN^-$iGhi&&yq4Av7<;M{*=F8QxdprqzE0y*tA}hJr%o+PbzW#C zRAu7FBn5CiOkA`-4qal#GL=<~Ma7~dK(uX@!YWXoxOfuw=*b3MWa*+MU9_T$(nPU) z0i4TWmX?Yh{)50NFMV5f{P(h=T>-1z4$2OcJNc{Ow4Qx#O@GwR^2~}eI{<+n;D8LU HXTR?s#`(X1 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/helpers/properties/__pycache__/can_be_installed.cpython-38.pyc b/custom_components/hacs/helpers/properties/__pycache__/can_be_installed.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91812af48d5c2d05d397e98a4e75655c0ef11660 GIT binary patch literal 974 zcma)5%We}f6t&0BbXrm<>_OQEg#`Aff>wao1Vmvo8_OBrbSCwTgY6(q#V%d)3H(4m zh6P)1S@8>O;5rE?YLUQ_?;JnRx#xP`?eFgqY_C3j5nmHRzQw_I<2ZPN-3>4(qNpY{ z%_)sI(Mip6M#&4JnBo^iaiB|e~)voTt^vN1&vjY4zi`Q0;^``*;p zo`zFdw7r;%^wp%&m2dmhMme?{Lri@gZXpUcBI72XJ2>I+tzxmx2md8?b8w8M$%JTR z4MK!HA!?%*I^wPn?-x>UR8nu5ayp9C$NJ~MbM=hqYl;svY&ij*yf5>=8KY-DrByh!%E zMqYqxr@lg`9;G1g(gW^vcevB{-5r0-W=9C*>fxC`VT69iU|$#n=b$bDBay@!)i}o( zT_8y$y+@KNyrVgh>;|QyKTw5I9O&mSiz52_LLoQ@bthmH%CST_QB-0{?$HkCOj7s< z3n$mAHLmi;ZoXO5D(g3k&UsT8!0=U~Pe2!-ZVtwyJG91YvcvGDYqlQUj_o^%Z-)F> zl3QXw#CX8jo?a{8*(NEqSSD4O_?1fRD|Ax1r08MiU$<{oqHtN&c(t^`SEl)%9t7&V z4NM9z0;yzRu_lZQQ`e>m2U3-y(>@%Qoz}b&wSv7cr*!#<>=gL^{g@S|DXV2Rz;Nhn zO`{s`vL0@wGz{;uc348C+)L%W-2hI{Z5*fXPe|WlhNqz0_y619eCWAMM`6NyN}Puy z&TAt(t-zmh{;Lx@l7(pU0=%)ufYNi`GqaeBCmlr!@tsUC!F}d3rGbi{g^l;=Gkbk; SbQ_z)IO=fmC6&mSJ(L- z*ceR50AJ7*(jAVc&cj!px~Ttzqh(v2Hhzu%(%ZzXIu4dZop5goGSqtyfDM7W*m4n> zoPLhl_Ap^NO>=FSl-7sdm~C%D!EILyDH0<%HEdyzxR|q*G<2p|o?P1gZXr#IEK7|& zluVmV5I^UsE+wnB=^@LFP&zI0hbeZ$QpsjgY^UzA^`Yqe9;qV`;MaJR6!|<|jFOc$ zMdossMJ{rqN0%%C5EA6oqq31X72q~X(^W0?u&nISV=8d+J_A5rfPEa|F7CtU!`B5{ z5C3s{!^i3VxI%Z0^%c58AUuLMf!8mVqBMhfp$1YcSW*q#QJ6_*sZ323VbzD#s_Vd= zyn$oz?D0drg<@Ph1yZ|rg2;OG|Gn0GC-6E$N^_P8N^OVItl%qpyXb2b(^k3MF3i0Is^!@hx bool: + if self.data.homeassistant is not None: + if self.data.releases: + if not version_left_higher_then_right( + self.hacs.system.ha_version, self.data.homeassistant + ): + return False + return True + + @property + def can_install(self): + """kept for legacy compatibility""" + return self.can_be_installed diff --git a/custom_components/hacs/helpers/properties/custom.py b/custom_components/hacs/helpers/properties/custom.py new file mode 100644 index 0000000..3cb0486 --- /dev/null +++ b/custom_components/hacs/helpers/properties/custom.py @@ -0,0 +1,13 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +from abc import ABC + + +class RepositoryPropertyCustom(ABC): + @property + def custom(self): + """Return flag if the repository is custom.""" + if str(self.data.id) in self.hacs.common.default: + return False + if self.data.full_name == "hacs/integration": + return False + return True diff --git a/custom_components/hacs/helpers/properties/pending_update.py b/custom_components/hacs/helpers/properties/pending_update.py new file mode 100644 index 0000000..083930e --- /dev/null +++ b/custom_components/hacs/helpers/properties/pending_update.py @@ -0,0 +1,23 @@ +# pylint: disable=missing-class-docstring,missing-module-docstring,missing-function-docstring,no-member +from abc import ABC + + +class RepositoryPropertyPendingUpdate(ABC): + @property + def pending_update(self) -> bool: + if not self.can_install: + return False + if self.data.installed: + if self.data.selected_tag is not None: + if self.data.selected_tag == self.data.default_branch: + if self.data.installed_commit != self.data.last_commit: + return True + return False + if self.display_installed_version != self.display_available_version: + return True + return False + + @property + def pending_upgrade(self) -> bool: + """kept for legacy compatibility""" + return self.pending_update diff --git a/custom_components/hacs/helpers/register_repository.py b/custom_components/hacs/helpers/register_repository.py deleted file mode 100644 index d7c556f..0000000 --- a/custom_components/hacs/helpers/register_repository.py +++ /dev/null @@ -1,49 +0,0 @@ -"""Register a repository.""" -from aiogithubapi import AIOGitHubException -from custom_components.hacs.globals import get_hacs -from custom_components.hacs.hacsbase.exceptions import ( - HacsException, - HacsExpectedException, -) - - -async def register_repository(full_name, category, check=True): - """Register a repository.""" - hacs = get_hacs() - from custom_components.hacs.repositories import ( - RERPOSITORY_CLASSES, - ) # To hanle import error - - if full_name in hacs.common.skip: - if full_name != "hacs/integration": - raise HacsExpectedException(f"Skipping {full_name}") - - if category not in RERPOSITORY_CLASSES: - raise HacsException(f"{category} is not a valid repository category.") - - repository = RERPOSITORY_CLASSES[category](full_name) - if check: - try: - await repository.registration() - if hacs.system.status.new: - repository.status.new = False - if repository.validate.errors: - hacs.common.skip.append(repository.data.full_name) - if not hacs.system.status.startup: - hacs.logger.error(f"Validation for {full_name} failed.") - return repository.validate.errors - repository.logger.info("Registration complete") - except AIOGitHubException as exception: - hacs.common.skip.append(repository.data.full_name) - raise HacsException(f"Validation for {full_name} failed with {exception}.") - - hacs.hass.bus.async_fire( - "hacs/repository", - { - "id": 1337, - "action": "registration", - "repository": repository.data.full_name, - "repository_id": repository.information.uid, - }, - ) - hacs.repositories.append(repository) diff --git a/custom_components/hacs/helpers/validate_repository.py b/custom_components/hacs/helpers/validate_repository.py deleted file mode 100644 index f20adbf..0000000 --- a/custom_components/hacs/helpers/validate_repository.py +++ /dev/null @@ -1,90 +0,0 @@ -"""Helper to do common validation for repositories.""" -from aiogithubapi import AIOGitHubException -from custom_components.hacs.globals import get_hacs, is_removed -from custom_components.hacs.hacsbase.exceptions import HacsException -from custom_components.hacs.helpers.install import version_to_install -from custom_components.hacs.helpers.information import ( - get_repository, - get_tree, - get_releases, -) - - -async def common_validate(repository): - """Common validation steps of the repository.""" - repository.validate.errors = [] - - # Make sure the repository exist. - repository.logger.debug("Checking repository.") - await common_update_data(repository) - - # Step 6: Get the content of hacs.json - await repository.get_repository_manifest_content() - - -async def common_update_data(repository): - """Common update data.""" - hacs = get_hacs() - try: - repository_object = await get_repository( - hacs.session, hacs.configuration.token, repository.data.full_name - ) - repository.repository_object = repository_object - repository.data.update_data(repository_object.attributes) - except (AIOGitHubException, HacsException) as exception: - if not hacs.system.status.startup: - repository.logger.error(exception) - repository.validate.errors.append("Repository does not exist.") - raise HacsException(exception) - - # Make sure the repository is not archived. - if repository.data.archived: - repository.validate.errors.append("Repository is archived.") - raise HacsException("Repository is archived.") - - # Make sure the repository is not in the blacklist. - if is_removed(repository.data.full_name): - repository.validate.errors.append("Repository is in the blacklist.") - raise HacsException("Repository is in the blacklist.") - - # Get releases. - try: - releases = await get_releases( - repository.repository_object, - repository.status.show_beta, - hacs.configuration.release_limit, - ) - if releases: - repository.releases.releases = True - repository.releases.objects = releases - repository.releases.published_tags = [ - x.tag_name for x in releases if not x.draft - ] - repository.versions.available = next(iter(releases)).tag_name - for release in releases: - if release.tag_name == repository.ref: - assets = release.assets - if assets: - downloads = next(iter(assets)).attributes.get("download_count") - repository.releases.downloads = downloads - - except (AIOGitHubException, HacsException): - repository.releases.releases = False - - repository.ref = version_to_install(repository) - - repository.logger.debug( - f"Running checks against {repository.ref.replace('tags/', '')}" - ) - - try: - repository.tree = await get_tree(repository.repository_object, repository.ref) - if not repository.tree: - raise HacsException("No files in tree") - repository.treefiles = [] - for treefile in repository.tree: - repository.treefiles.append(treefile.full_path) - except (AIOGitHubException, HacsException) as exception: - if not hacs.system.status.startup: - repository.logger.error(exception) - raise HacsException(exception) diff --git a/custom_components/hacs/http.py b/custom_components/hacs/http.py deleted file mode 100644 index cab3fb1..0000000 --- a/custom_components/hacs/http.py +++ /dev/null @@ -1,83 +0,0 @@ -"""HACS http endpoints.""" -import os -from integrationhelper import Logger -from homeassistant.components.http import HomeAssistantView -from aiohttp import web -from hacs_frontend import locate_gz, locate_debug_gz - -from custom_components.hacs.globals import get_hacs - - -class HacsFrontend(HomeAssistantView): - """Base View Class for HACS.""" - - requires_auth = False - name = "hacs_files" - url = r"/hacsfiles/{requested_file:.+}" - - async def get(self, request, requested_file): # pylint: disable=unused-argument - """Handle HACS Web requests.""" - return await get_file_response(requested_file) - - -class HacsPluginViewLegacy(HacsFrontend): - """Alias for legacy, remove with 1.0""" - - name = "community_plugin" - url = r"/community_plugin/{requested_file:.+}" - - async def get(self, request, requested_file): # pylint: disable=unused-argument - """DEPRECATED.""" - hacs = get_hacs() - if hacs.system.ha_version.split(".")[1] >= "107": - logger = Logger("hacs.deprecated") - logger.warning( - "The '/community_plugin/*' is deprecated and will be removed in an upcomming version of HACS, it has been replaced by '/hacsfiles/*', if you use the UI to manage your lovelace configuration, you can update this by going to the settings tab in HACS, if you use YAML to manage your lovelace configuration, you manually need to replace the URL in your resources." - ) - - return await get_file_response(requested_file) - - -async def get_file_response(requested_file): - """Get file.""" - hacs = get_hacs() - - if requested_file.startswith("frontend-"): - if hacs.configuration.debug: - servefile = await hacs.hass.async_add_executor_job(locate_debug_gz) - hacs.logger.debug("Serving DEBUG frontend") - else: - servefile = await hacs.hass.async_add_executor_job(locate_gz) - - if os.path.exists(servefile): - return web.FileResponse(servefile) - elif requested_file == "iconset.js": - return web.FileResponse( - f"{hacs.system.config_path}/custom_components/hacs/iconset.js" - ) - - try: - if requested_file.startswith("themes"): - file = f"{hacs.system.config_path}/{requested_file}" - else: - file = f"{hacs.system.config_path}/www/community/{requested_file}" - - # Serve .gz if it exist - if os.path.exists(file + ".gz"): - file += ".gz" - - if os.path.exists(file): - hacs.logger.debug("Serving {} from {}".format(requested_file, file)) - response = web.FileResponse(file) - response.headers["Cache-Control"] = "no-store, max-age=0" - response.headers["Pragma"] = "no-store" - return response - else: - hacs.logger.error(f"Tried to serve up '{file}' but it does not exist") - - except Exception as error: # pylint: disable=broad-except - hacs.logger.debug( - "there was an issue trying to serve {} - {}".format(requested_file, error) - ) - - return web.Response(status=404) diff --git a/custom_components/hacs/iconset.js b/custom_components/hacs/iconset.js deleted file mode 100644 index ab225c7..0000000 --- a/custom_components/hacs/iconset.js +++ /dev/null @@ -1,34 +0,0 @@ -const iconset = document.createElement("ha-iconset-svg"); -iconset.name = "hacs"; -iconset.size = "1024"; -iconset.innerHTML = ` - - - - - - - - - - - - - - - -` -document.body.appendChild(iconset); \ No newline at end of file diff --git a/custom_components/hacs/manifest.json b/custom_components/hacs/manifest.json index 95c49e0..700ed1f 100644 --- a/custom_components/hacs/manifest.json +++ b/custom_components/hacs/manifest.json @@ -1,24 +1,25 @@ { - "codeowners": [ - "@ludeeus" - ], - "config_flow": true, - "dependencies": [ - "websocket_api", - "frontend", - "persistent_notification", - "lovelace" - ], - "documentation": "https://hacs.xyz/docs/configuration/start", - "domain": "hacs", - "issues": "https://hacs.xyz/docs/issues", - "name": "HACS (Home Assistant Community Store)", - "requirements": [ - "aiofiles==0.4.0", - "aiogithubapi==0.5.0", - "backoff==1.10.0", - "hacs_frontend==20200309184730", - "integrationhelper==0.2.2", - "semantic_version==2.8.4" - ] + "codeowners": [ + "@ludeeus" + ], + "config_flow": true, + "dependencies": [ + "http", + "websocket_api", + "frontend", + "persistent_notification", + "lovelace" + ], + "documentation": "https://hacs.xyz/docs/configuration/start", + "issue_tracker": "https://github.com/hacs/integration/issues", + "domain": "hacs", + "name": "HACS", + "requirements": [ + "aiofiles>=0.5.0", + "aiogithubapi>=2.0.0<3.0.0", + "backoff>=1.10.0", + "hacs_frontend==202012051441", + "semantic_version>=2.8.5", + "queueman==0.5" + ] } \ No newline at end of file diff --git a/custom_components/hacs/models/__init__.py b/custom_components/hacs/models/__init__.py new file mode 100644 index 0000000..3fb384a --- /dev/null +++ b/custom_components/hacs/models/__init__.py @@ -0,0 +1 @@ +"""Hacs models.""" diff --git a/custom_components/hacs/models/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/models/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f31a36a3504c299e1bdfedba78ac6004d12d81bc GIT binary patch literal 164 zcmWIL<>g`k0{@<~@xnm*F^Gc<7=auIATH(r5-AK(3@MDk44O<;JRXV3#R|FkDXBTd zdVZRWx7g$3Q}UDJ<5w~iF#(l=iC>2L$@zI{nd$n;rNt%rx$(*Qxdr)osd**E`WZm| i`Vjs4@$s2?nI-Y@dIgoYIBbA|r8%i~Ae%n}F#`b1%qdd< literal 0 HcmV?d00001 diff --git a/custom_components/hacs/models/__pycache__/core.cpython-38.pyc b/custom_components/hacs/models/__pycache__/core.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a184e32ba237e4b259fd83a9edcc48480d759d36 GIT binary patch literal 554 zcmYjPy-ve05cXdhlcqxO1YIiueF21uI=}!0sba}uh1k#_CoXo1#9T%m0!AK&7x2o& zDGb&RjxNDu@KS}0JFSltS9BI4QhP9 zF!fz|FP5-{rw)#iMJ}F{b)~6o1e9!`H#>#9)d#YBfVSc1aNMsa4&$ z@dD(ILxXlevg&C0Y9EdR4hripQ>H|10RS X{lMeL-RdXd|G4ll7)~)KDP{N%Rd9w8 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/models/__pycache__/frontend.cpython-38.pyc b/custom_components/hacs/models/__pycache__/frontend.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e9488f284d9198e9efdece5ebb15d919d8d7e8a0 GIT binary patch literal 510 zcmY*Wu};G<5Otii4N=p=!asB&Vy_TlC`@HQU9z0q*e#I~JJ?PIGmQKQzkF~hw z*f4g(46f@jVP?(GdF4*&a8k2kB(K7 z#Ki!T)dIjJ0JY+6vCvbV1qa!52U}i{(TzfqIw! zTf2+=tMQ?AHnNbIW3HM3!p&+w7E)T(8!2NcW$*OZB|n$)%c#1487`%Ejg+24gGjf| zb#W=BvepeM9TXr}K+dxyvs9IZ!AGhFP||LFx*VVG&+mc!LjG0qNgk@IarV01Rm~U% z*UQHBH_jS6gz6JaxdJHDg(^({cyx2?bNDKeggHn_D4FB*yuedtRrn?B`K+-i0jPu+ahPiVQ#`tZlq=ScV|bIs84@$aMe2}I{1=cVn4JIs literal 0 HcmV?d00001 diff --git a/custom_components/hacs/models/core.py b/custom_components/hacs/models/core.py new file mode 100644 index 0000000..5b72b9f --- /dev/null +++ b/custom_components/hacs/models/core.py @@ -0,0 +1,14 @@ +"""HACS Core info.""" +from pathlib import Path +import attr + +from ..enums import LovelaceMode + + +@attr.s +class HacsCore: + """HACS Core info.""" + + config_path = attr.ib(Path) + ha_version = attr.ib(str) + lovelace_mode = LovelaceMode("storage") diff --git a/custom_components/hacs/models/frontend.py b/custom_components/hacs/models/frontend.py new file mode 100644 index 0000000..1b9d875 --- /dev/null +++ b/custom_components/hacs/models/frontend.py @@ -0,0 +1,10 @@ +"""HacsFrontend.""" + + +class HacsFrontend: + """HacsFrontend.""" + + version_running: bool = None + version_available: bool = None + version_expected: bool = None + update_pending: bool = False diff --git a/custom_components/hacs/models/system.py b/custom_components/hacs/models/system.py new file mode 100644 index 0000000..c92023d --- /dev/null +++ b/custom_components/hacs/models/system.py @@ -0,0 +1,15 @@ +"""HACS System info.""" +import attr +from ..enums import HacsStage +from ..const import VERSION + + +@attr.s +class HacsSystem: + """HACS System info.""" + + disabled: bool = False + running: bool = False + version: str = VERSION + stage: HacsStage = attr.ib(HacsStage) + action: bool = False diff --git a/custom_components/hacs/operational/__init__.py b/custom_components/hacs/operational/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/custom_components/hacs/operational/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/operational/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0009e4913220777de006ee66c7dd34dbf0011c09 GIT binary patch literal 145 zcmWIL<>g`k0{@<~@gVv!h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vEKRG`yEi+v| zxwN<>KQ}%(Ker%1FEy{ESU)2%xmZ8HAhjs5Br`uRF-Jc>J~J<~BtBlRpz;=nO>TZl OX-=vg$h^-$%m4tT$|86G literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/__pycache__/backup.cpython-38.pyc b/custom_components/hacs/operational/__pycache__/backup.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec2ae017ffb783f6cdde76227cba52f1a433ff33 GIT binary patch literal 3611 zcmcgv%a0sK8L#U1Ja#-@Ki07m7zqpvgc(5Q6(Mle4i3Va1ji9DfSOKM&F<{>OpmK; zOg!4;Vjn@`3Ra|9iOWieJNI1k5A>A-%8e6;m5}*;)jf}WAUM&Z`s%CKS5@ER_tm_+ zu~B9C-umq);p2;p{f!#ypN+CUNblyg;$}s|%_hlE=gqaga4=1hww*hfOa`igLnt!#5mOCR$G5t1 zIvxyny74sA=_riT(Ig$qvCfR%y6Hr!NDtF-lynQJoyn6t2tztr7;ZvF=7BKF;~t+~ zC{)o|jK`#t#Ya3Hz^LP&U^G-P$A$OoL!NC4{tnX)wVv{{L;EaAhXJhMU2o`vZcf55+$wMMi_tzghJmPsE-ydc=O!sFm7iw({Qq{Vdz1oV+azV>(X6;M4 zm1enP+`%(zvck~hf5zImm+emVFv&eN(n?B2UYQ4@XkUt<%5ra*4Tg!#gE*Z$Ax_n4 zf>(rA(M{`eKfM3ey?bvw$Xy}do9^V5H{Orsg#07l*p4!}Y#wp7LA#Ta736}ESe*PC z`0Q&S%xZ9lS5X?gj#A}K%R=4c4Xau9INZXx^RK|&wHyLlAnERpj=f=%WI0i}iP*+P=9AM7E)r<^X zpKl0qJmV~fa1`SV&g}97iO^HBHBjlObcjw*QE1I>lw-n}eQg5Sy`ipPj=D-@iwGG_ z-5_G({1xIz)#_y;FA*WbtIrVmERhzG&k;FgC|?za`Xu4@BH}Px5KwzfB2?q($PLoUG5SSQqn1DN0-3wj`E}8xq{U<&Pt=lLj0M}EVNp}nxyhwL$neMzY-T7s@ zD>1$&_1y@mcRXtrnqGEvV%lQy5l_X0WeZG^1T~N!&j7xK_MW}xpk|>fEa0O(tNmz| z_+~eX430#U0S=Lx!m-rCY~Ag(y}YXAB+Z5hAtWX(M-u~ORXhQx0rTgMh;)=Yq-q0) zZL0vME>5$iPoqjw`9qXNR;S>Q+q{e&a_BBg0}USG*QUC9IvbcyfHqq zKcK+z*V;CAZr~kag6Yw&K_Xw_?Z*n>d8jUdKXyA$qEVuJ@a+rAMx{t+xsOK!S;AmX z?waRF5mp9=&Wot)W?cdcl0^bJ7TIrxde=bHkz?$U2V^<2Qoos{Qx(gQI!y5Nn>tc3 z>8j_n;V4Xgfszs8F=UVRoN^VJL(y?nnZi~g=NpSPAla9h~LGnDbcAffb`TCi9IJB8RUD6!P97a z=JZS04OE+ieVNEth|v4{e+JYJCjSIw38;Y|G=rL=enbMv>c4j8$=sp88l7F45LR^t z({W5MqpWl0&q@(WCVLyp(NidQ^bq(&&X=EkjsGXniTRhK2zS)iiF}oaNnex{%z@Y3 h!8iu_L>_#v$VA_#S9x|B#6eCE%&%_I&$Aob{|2Q$Ep-3@ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/__pycache__/factory.cpython-38.pyc b/custom_components/hacs/operational/__pycache__/factory.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b96d98e17e7f32c2b85372e3d4de99056c48c11d GIT binary patch literal 1939 zcmd5-O>f&a7$zxMl4Uz>UAODdZODf8;9;{dpapg)h89Dc1SpcNS+`7wYKlnPYW3lf z63`fKF6nW=?<>`@ngrW*6!`G_CHcG$Up?z|Tmot5*FXHf zEJFUoMm-tOcmP>F2jGO$n0Q#Sm?q3)6zfK8B&KJUbu(@xmS>gqMrbczZTEaRaUVDRZhd194-aKMnhtplscTZstvfZcp#>1_hjVRwN_8)$`_2^Y7rg@a5 zFwofyLRIf*jz#_?8;^yA(d$x-Bb5v3OEJwz}74wM#e;z{)tJjnNfdigjAahs5Wjb*|*8vh2ex(YBR zbK(PefUpS|VB3%TOgC~cptLQEG>y_R+`v>K9-R=`hDCqh8-`gripImRP$Q7_pbG+@s_*XkvCVHx~`KE`4FW zu;=X9z?t{J%4>GW_Q+2p1d*>K9WUfb;P>67QjH|H;nA#-pdYA*9TtHeo!_%~27@ei|e~ z+krYr!zj}Y6$>%dc3B8{Vn`%GyDJ^(rdaB*^iBmGoYhsqNT7FChqwv-3Y|#k8fCOg zd-PLd_Q`n{TwM5nc80{K)Ne^tw7yocU3+OO;w36iUPtI5+(1A|o9bY!P}QISgi=r=gWk$KhyCX8Z{mN3<~Y#pw2lvVk`BfcWX-i0hA1?opqN= zo*Ok48;8c3imip6JO8fYinwM;oWxLcDOK+h)Hf>$nkYf-grMv(#2I-Tm!IRUQ}VV9 zexJdN8go6+-nudS@KWZMYqt9~#tnQFzOP;1PcmM_Sa19OFGUbnBXSLQ{{Z15gpUzc zhbe3LB%}jy4YI literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/__pycache__/reload.cpython-38.pyc b/custom_components/hacs/operational/__pycache__/reload.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd7b9798e5dc6781a7056c4c01e86bb73bef6fb5 GIT binary patch literal 522 zcma)3y-veG4EFt?7D%PS6EKn`Sr{t>Vkt|Js1Q<@Q*?LGN=YudOOR5v6O6nF&%w+K zWM$$NnD8Y+1hHVrmhH2@&-UGnMtgvA_53Do3BbD#`a&@{Lv>>`3KpP-}q;nnWE<&oAcE7k>*!hoQcrx(OQJm_kOjlz5+|G-F#vU_&>sA&>MD6rTdd?02j< z678&m9#Qmxco352FmZJ*h1HpP&_d^>tucGhWt}doqD#&~-h{GprOAXeSz&Ulb@ zeww&9O>EoKMn(Ozr@wUv?eFV`Xn+PJpkrcp(VvC183>__6rxSEtUcp9n&Bifs!H`K K{x~?lEoWc;golm* literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/__pycache__/remove.cpython-38.pyc b/custom_components/hacs/operational/__pycache__/remove.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..033ef3c6ac0ed096be63cb7a52789b80efdfe73c GIT binary patch literal 917 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 Isp85115d>?(*OVf literal 0 HcmV?d00001 diff --git a/custom_components/hacs/hacsbase/backup.py b/custom_components/hacs/operational/backup.py similarity index 81% rename from custom_components/hacs/hacsbase/backup.py rename to custom_components/hacs/operational/backup.py index acd20c1..f0e8bb9 100644 --- a/custom_components/hacs/hacsbase/backup.py +++ b/custom_components/hacs/operational/backup.py @@ -1,117 +1,124 @@ -"""Backup.""" -import os -import shutil -import tempfile -from time import sleep - -from integrationhelper import Logger - -BACKUP_PATH = tempfile.gettempdir() + "/hacs_backup/" - - -class Backup: - """Backup.""" - - def __init__(self, local_path, backup_path=BACKUP_PATH): - """initialize.""" - self.logger = Logger("hacs.backup") - self.local_path = local_path - self.backup_path = backup_path - self.backup_path_full = f"{self.backup_path}{self.local_path.split('/')[-1]}" - - def create(self): - """Create a backup in /tmp""" - if not os.path.exists(self.local_path): - return - if os.path.exists(self.backup_path): - shutil.rmtree(self.backup_path) - while os.path.exists(self.backup_path): - sleep(0.1) - os.makedirs(self.backup_path, exist_ok=True) - - try: - if os.path.isfile(self.local_path): - shutil.copyfile(self.local_path, self.backup_path_full) - os.remove(self.local_path) - else: - shutil.copytree(self.local_path, self.backup_path_full) - shutil.rmtree(self.local_path) - while os.path.exists(self.local_path): - sleep(0.1) - self.logger.debug( - f"Backup for {self.local_path}, created in {self.backup_path_full}" - ) - except Exception: # pylint: disable=broad-except - pass - - def restore(self): - """Restore from backup.""" - if not os.path.exists(self.backup_path_full): - return - - if os.path.isfile(self.backup_path_full): - if os.path.exists(self.local_path): - os.remove(self.local_path) - shutil.copyfile(self.backup_path_full, self.local_path) - else: - if os.path.exists(self.local_path): - shutil.rmtree(self.local_path) - while os.path.exists(self.local_path): - sleep(0.1) - shutil.copytree(self.backup_path_full, self.local_path) - self.logger.debug( - f"Restored {self.local_path}, from backup {self.backup_path_full}" - ) - - def cleanup(self): - """Cleanup backup files.""" - if os.path.exists(self.backup_path): - shutil.rmtree(self.backup_path) - while os.path.exists(self.backup_path): - sleep(0.1) - self.logger.debug(f"Backup dir {self.backup_path} cleared") - - -class BackupNetDaemon: - """BackupNetDaemon.""" - - def __init__(self, repository): - """Initialize.""" - self.repository = repository - self.logger = Logger("hacs.backup") - self.backup_path = ( - tempfile.gettempdir() + "/hacs_persistent_netdaemon/" + repository.data.name - ) - - def create(self): - """Create a backup in /tmp""" - if os.path.exists(self.backup_path): - shutil.rmtree(self.backup_path) - while os.path.exists(self.backup_path): - sleep(0.1) - os.makedirs(self.backup_path, exist_ok=True) - - for filename in os.listdir(self.repository.content.path.local): - if filename.endswith(".yaml"): - source_file_name = f"{self.repository.content.path.local}/{filename}" - target_file_name = f"{self.backup_path}/{filename}" - shutil.copyfile(source_file_name, target_file_name) - - def restore(self): - """Create a backup in /tmp""" - if os.path.exists(self.backup_path): - for filename in os.listdir(self.backup_path): - if filename.endswith(".yaml"): - source_file_name = f"{self.backup_path}/{filename}" - target_file_name = ( - f"{self.repository.content.path.local}/{filename}" - ) - shutil.copyfile(source_file_name, target_file_name) - - def cleanup(self): - """Create a backup in /tmp""" - if os.path.exists(self.backup_path): - shutil.rmtree(self.backup_path) - while os.path.exists(self.backup_path): - sleep(0.1) - self.logger.debug(f"Backup dir {self.backup_path} cleared") +"""Backup.""" +from custom_components.hacs.helpers.functions.is_safe_to_remove import is_safe_to_remove +import os +import shutil +import tempfile +from time import sleep + +from custom_components.hacs.helpers.functions.logger import getLogger + +BACKUP_PATH = tempfile.gettempdir() + "/hacs_backup/" + +_LOGGER = getLogger() + + +class Backup: + """Backup.""" + + def __init__(self, local_path, backup_path=BACKUP_PATH): + """initialize.""" + self.local_path = local_path + self.backup_path = backup_path + self.backup_path_full = f"{self.backup_path}{self.local_path.split('/')[-1]}" + + def create(self): + """Create a backup in /tmp""" + if not os.path.exists(self.local_path): + return + if not is_safe_to_remove(self.local_path): + return + if os.path.exists(self.backup_path): + shutil.rmtree(self.backup_path) + while os.path.exists(self.backup_path): + sleep(0.1) + os.makedirs(self.backup_path, exist_ok=True) + + try: + if os.path.isfile(self.local_path): + shutil.copyfile(self.local_path, self.backup_path_full) + os.remove(self.local_path) + else: + shutil.copytree(self.local_path, self.backup_path_full) + shutil.rmtree(self.local_path) + while os.path.exists(self.local_path): + sleep(0.1) + _LOGGER.debug( + "Backup for %s, created in %s", + self.local_path, + self.backup_path_full, + ) + except (Exception, BaseException): # pylint: disable=broad-except + pass + + def restore(self): + """Restore from backup.""" + if not os.path.exists(self.backup_path_full): + return + + if os.path.isfile(self.backup_path_full): + if os.path.exists(self.local_path): + os.remove(self.local_path) + shutil.copyfile(self.backup_path_full, self.local_path) + else: + if os.path.exists(self.local_path): + shutil.rmtree(self.local_path) + while os.path.exists(self.local_path): + sleep(0.1) + shutil.copytree(self.backup_path_full, self.local_path) + _LOGGER.debug( + "Restored %s, from backup %s", self.local_path, self.backup_path_full + ) + + def cleanup(self): + """Cleanup backup files.""" + if os.path.exists(self.backup_path): + shutil.rmtree(self.backup_path) + while os.path.exists(self.backup_path): + sleep(0.1) + _LOGGER.debug("Backup dir %s cleared", self.backup_path) + + +class BackupNetDaemon: + """BackupNetDaemon.""" + + def __init__(self, repository): + """Initialize.""" + self.repository = repository + self.backup_path = ( + tempfile.gettempdir() + "/hacs_persistent_netdaemon/" + repository.data.name + ) + + def create(self): + """Create a backup in /tmp""" + if not is_safe_to_remove(self.repository.content.path.local): + return + if os.path.exists(self.backup_path): + shutil.rmtree(self.backup_path) + while os.path.exists(self.backup_path): + sleep(0.1) + os.makedirs(self.backup_path, exist_ok=True) + + for filename in os.listdir(self.repository.content.path.local): + if filename.endswith(".yaml"): + source_file_name = f"{self.repository.content.path.local}/{filename}" + target_file_name = f"{self.backup_path}/{filename}" + shutil.copyfile(source_file_name, target_file_name) + + def restore(self): + """Create a backup in /tmp""" + if os.path.exists(self.backup_path): + for filename in os.listdir(self.backup_path): + if filename.endswith(".yaml"): + source_file_name = f"{self.backup_path}/{filename}" + target_file_name = ( + f"{self.repository.content.path.local}/{filename}" + ) + shutil.copyfile(source_file_name, target_file_name) + + def cleanup(self): + """Create a backup in /tmp""" + if os.path.exists(self.backup_path): + shutil.rmtree(self.backup_path) + while os.path.exists(self.backup_path): + sleep(0.1) + _LOGGER.debug("Backup dir %s cleared", self.backup_path) diff --git a/custom_components/hacs/operational/factory.py b/custom_components/hacs/operational/factory.py new file mode 100644 index 0000000..06b6231 --- /dev/null +++ b/custom_components/hacs/operational/factory.py @@ -0,0 +1,50 @@ +# pylint: disable=missing-docstring,invalid-name +import asyncio +from aiogithubapi import AIOGitHubAPIException + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.logger import getLogger +from custom_components.hacs.helpers.functions.register_repository import ( + register_repository, +) + +max_concurrent_tasks = asyncio.Semaphore(15) +sleeper = 5 + +_LOGGER = getLogger() + + +class HacsTaskFactory: + def __init__(self): + self.tasks = [] + self.running = False + + async def safe_common_update(self, repository): + async with max_concurrent_tasks: + try: + await repository.common_update() + except (AIOGitHubAPIException, HacsException) as exception: + _LOGGER.error("%s - %s", repository.data.full_name, exception) + + # Due to GitHub ratelimits we need to sleep a bit + await asyncio.sleep(sleeper) + + async def safe_update(self, repository): + async with max_concurrent_tasks: + try: + await repository.update_repository() + except (AIOGitHubAPIException, HacsException) as exception: + _LOGGER.error("%s - %s", repository.data.full_name, exception) + + # Due to GitHub ratelimits we need to sleep a bit + await asyncio.sleep(sleeper) + + async def safe_register(self, repo, category): + async with max_concurrent_tasks: + try: + await register_repository(repo, category) + except (AIOGitHubAPIException, HacsException) as exception: + _LOGGER.error("%s - %s", repo, exception) + + # Due to GitHub ratelimits we need to sleep a bit + await asyncio.sleep(sleeper) diff --git a/custom_components/hacs/operational/reload.py b/custom_components/hacs/operational/reload.py new file mode 100644 index 0000000..037887e --- /dev/null +++ b/custom_components/hacs/operational/reload.py @@ -0,0 +1,10 @@ +"""Reload HACS""" + + +async def async_reload_entry(hass, config_entry): + """Reload HACS.""" + from custom_components.hacs.operational.remove import async_remove_entry + from custom_components.hacs.operational.setup import async_setup_entry + + await async_remove_entry(hass, config_entry) + await async_setup_entry(hass, config_entry) diff --git a/custom_components/hacs/operational/remove.py b/custom_components/hacs/operational/remove.py new file mode 100644 index 0000000..84d8232 --- /dev/null +++ b/custom_components/hacs/operational/remove.py @@ -0,0 +1,24 @@ +"""Remove HACS.""" +from custom_components.hacs.share import get_hacs + + +async def async_remove_entry(hass, config_entry): + """Handle removal of an entry.""" + hacs = get_hacs() + hacs.log.info("Disabling HACS") + hacs.log.info("Removing recurring tasks") + for task in hacs.recuring_tasks: + task() + if config_entry.state == "loaded": + hacs.log.info("Removing sensor") + try: + await hass.config_entries.async_forward_entry_unload(config_entry, "sensor") + except ValueError: + pass + hacs.log.info("Removing sidepanel") + try: + hass.components.frontend.async_remove_panel("hacs") + except AttributeError: + pass + hacs.system.disabled = True + hacs.log.info("HACS is now disabled") diff --git a/custom_components/hacs/operational/runtime.py b/custom_components/hacs/operational/runtime.py new file mode 100644 index 0000000..95f5985 --- /dev/null +++ b/custom_components/hacs/operational/runtime.py @@ -0,0 +1 @@ +"""Runtime...""" diff --git a/custom_components/hacs/operational/setup.py b/custom_components/hacs/operational/setup.py new file mode 100644 index 0000000..e89eaa2 --- /dev/null +++ b/custom_components/hacs/operational/setup.py @@ -0,0 +1,209 @@ +"""Setup HACS.""" +from custom_components.hacs.enums import HacsStage +from aiogithubapi import AIOGitHubAPIException, GitHub +from homeassistant.const import EVENT_HOMEASSISTANT_STARTED +from homeassistant.const import __version__ as HAVERSION +from homeassistant.exceptions import ConfigEntryNotReady +from homeassistant.helpers.aiohttp_client import async_create_clientsession +from homeassistant.helpers.event import async_call_later + +from custom_components.hacs.const import DOMAIN, STARTUP, VERSION +from custom_components.hacs.hacsbase.configuration import Configuration +from custom_components.hacs.hacsbase.data import HacsData +from custom_components.hacs.helpers.functions.constrains import check_constrains +from custom_components.hacs.helpers.functions.remaining_github_calls import ( + get_fetch_updates_for, +) +from custom_components.hacs.operational.reload import async_reload_entry +from custom_components.hacs.operational.remove import async_remove_entry +from custom_components.hacs.operational.setup_actions.clear_storage import ( + async_clear_storage, +) +from custom_components.hacs.operational.setup_actions.frontend import ( + async_setup_frontend, +) +from custom_components.hacs.operational.setup_actions.load_hacs_repository import ( + async_load_hacs_repository, +) +from custom_components.hacs.operational.setup_actions.sensor import async_add_sensor +from custom_components.hacs.operational.setup_actions.websocket_api import ( + async_setup_hacs_websockt_api, +) +from custom_components.hacs.share import get_hacs + +try: + from homeassistant.components.lovelace import system_health_info +except ImportError: + from homeassistant.components.lovelace.system_health import system_health_info + + +async def _async_common_setup(hass): + """Common setup stages.""" + hacs = get_hacs() + hacs.hass = hass + hacs.system.running = True + hacs.session = async_create_clientsession(hass) + + +async def async_setup_entry(hass, config_entry): + """Set up this integration using UI.""" + from homeassistant import config_entries + + hacs = get_hacs() + if hass.data.get(DOMAIN) is not None: + return False + if config_entry.source == config_entries.SOURCE_IMPORT: + hass.async_create_task(hass.config_entries.async_remove(config_entry.entry_id)) + return False + + await _async_common_setup(hass) + + hacs.configuration = Configuration.from_dict( + config_entry.data, config_entry.options + ) + hacs.configuration.config_type = "flow" + hacs.configuration.config_entry = config_entry + + return await async_startup_wrapper_for_config_entry() + + +async def async_setup(hass, config): + """Set up this integration using yaml.""" + hacs = get_hacs() + if DOMAIN not in config: + return True + if hacs.configuration and hacs.configuration.config_type == "flow": + return True + + await _async_common_setup(hass) + + hass.data[DOMAIN] = config[DOMAIN] + hacs.configuration = Configuration.from_dict(config[DOMAIN]) + hacs.configuration.config_type = "yaml" + await async_startup_wrapper_for_yaml() + return True + + +async def async_startup_wrapper_for_config_entry(): + """Startup wrapper for ui config.""" + hacs = get_hacs() + hacs.configuration.config_entry.add_update_listener(async_reload_entry) + try: + startup_result = await async_hacs_startup() + except AIOGitHubAPIException: + startup_result = False + if not startup_result: + hacs.system.disabled = True + raise ConfigEntryNotReady + hacs.system.disabled = False + return startup_result + + +async def async_startup_wrapper_for_yaml(): + """Startup wrapper for yaml config.""" + hacs = get_hacs() + try: + startup_result = await async_hacs_startup() + except AIOGitHubAPIException: + startup_result = False + if not startup_result: + hacs.system.disabled = True + hacs.hass.components.frontend.async_remove_panel( + hacs.configuration.sidepanel_title.lower() + .replace(" ", "_") + .replace("-", "_") + ) + hacs.log.info("Could not setup HACS, trying again in 15 min") + if int(hacs.system.ha_version.split(".")[1]) >= 117: + async_call_later(hacs.hass, 900, async_startup_wrapper_for_yaml) + else: + async_call_later(hacs.hass, 900, async_startup_wrapper_for_yaml()) + return + hacs.system.disabled = False + + +async def async_hacs_startup(): + """HACS startup tasks.""" + hacs = get_hacs() + + try: + lovelace_info = await system_health_info(hacs.hass) + except TypeError: + # If this happens, the users YAML is not valid, we assume YAML mode + lovelace_info = {"mode": "yaml"} + hacs.log.debug(f"Configuration type: {hacs.configuration.config_type}") + hacs.version = VERSION + hacs.log.info(STARTUP) + hacs.core.config_path = hacs.hass.config.path() + hacs.system.ha_version = HAVERSION + + # Setup websocket API + await async_setup_hacs_websockt_api() + + # Set up frontend + await async_setup_frontend() + + # Clear old storage files + await async_clear_storage() + + hacs.system.lovelace_mode = lovelace_info.get("mode", "yaml") + hacs.system.disabled = False + hacs.github = GitHub( + hacs.configuration.token, async_create_clientsession(hacs.hass) + ) + hacs.data = HacsData() + + can_update = await get_fetch_updates_for(hacs.github) + if can_update is None: + hacs.log.critical("Your GitHub token is not valid") + return False + + if can_update != 0: + hacs.log.debug(f"Can update {can_update} repositories") + else: + hacs.log.info( + "HACS is ratelimited, repository updates will resume when the limit is cleared, this can take up to 1 hour" + ) + return False + + # Check HACS Constrains + if not await hacs.hass.async_add_executor_job(check_constrains): + if hacs.configuration.config_type == "flow": + if hacs.configuration.config_entry is not None: + await async_remove_entry(hacs.hass, hacs.configuration.config_entry) + return False + + # Load HACS + if not await async_load_hacs_repository(): + if hacs.configuration.config_type == "flow": + if hacs.configuration.config_entry is not None: + await async_remove_entry(hacs.hass, hacs.configuration.config_entry) + return False + + # Restore from storefiles + if not await hacs.data.restore(): + hacs_repo = hacs.get_by_name("hacs/integration") + hacs_repo.pending_restart = True + if hacs.configuration.config_type == "flow": + if hacs.configuration.config_entry is not None: + await async_remove_entry(hacs.hass, hacs.configuration.config_entry) + return False + + # Setup startup tasks + if hacs.status.new: + if int(hacs.system.ha_version.split(".")[1]) >= 117: + async_call_later(hacs.hass, 5, hacs.startup_tasks) + else: + async_call_later(hacs.hass, 5, hacs.startup_tasks()) + else: + hacs.hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STARTED, hacs.startup_tasks) + + # Set up sensor + await async_add_sensor() + + # Mischief managed! + await hacs.async_set_stage(HacsStage.WAITING) + hacs.log.info( + "Setup complete, waiting for Home Assistant before startup tasks starts" + ) + return True diff --git a/custom_components/hacs/operational/setup_actions/__init__.py b/custom_components/hacs/operational/setup_actions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/custom_components/hacs/operational/setup_actions/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/operational/setup_actions/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba681d2866d32c0bc2c7775238dc4e421735c8bd GIT binary patch literal 159 zcmWIL<>g`k0{@<~@gVv!h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6v7KRG`yEi+v| zxwN<>KQ}%(Ker%1FEy{ESU)2%xmZ8HAhjs5Br`uRF-N~RwWPElJ~0`@E!K~Z&&};HdF04Q*;G$M25=zcg2oa@*tVFX>6Aq08me(_zwKulI%mmnI zd)gj3_Q)Z*(f@%15@!xv=gO)70vC8QPWD4(qM3Q`dEWQ$4kAr1k<$UE&fCZjU;o z{tG61r18R<)`>qx?dIyEv51*Si&?;_SS|t)^Rq_*{YkJm;G$%dcV2^E)W*^ezYd>- z!$~xGHVVhDRa_u-XH-O|al+e%Yz*M;$AZpEb}oIzjg{327)_Lw-7#giA`*kbZzmmPp ze%s34Q8HpdCGFLxkZJ%Gyji;-ErLO>KMvB9fEMxboDwZ6a~kwGPq~N-(OKR9-zJ1N znjqa1Ru+O5MEZ=*%a}ysl;)HV+Xu{o0!njU&ZJG~@p2|j=%sX$GSACG)|09sDdkMn zUEZl8iMH}{raB@0(X+|mXc&$APY*{EX`RNLD;vd0Is+SJz9pk5*Bgx<_rfPfL+KS%?4-kRQb}JK%5MTbcCo!e z>_dp|pD(+W(QdK?ROZojJYA*GEf=(M7tgyI$Wfdq@4S1ReP?kl_aS5eWTcxERRFJn z1O5OGWH`8i>)6FDqlKHeVIE-PU-(%-8!mivF}n}%ZebaNKy}~`*!&YXZev-PnYF1f zXs;bn+u%b)i~$1N7=Rmd^`OVkizN7wL7gZIPD)md;;k50zK&auXYdfZ?&_{H5By0h zEkMgrWWC>;gkK#!Jq*XJr344DvL3~RfIXTlVWgw1Jl1KnF?%20=n7Xypj6(iRNgF< z`pT6XRj3wb4}k(3AHbC;WbKxsRYml42SC7lp*1G>0&go}Waioe4(5DY1;0l~uAg4b)KPfq0NS{y#>M8RF{9Ox*|b>ZTn~({w=ScugEoC503AvIZl(KKUXtaUey)JR QRoE6FsexOj`<`3-4|Xw>`2YX_ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/setup_actions/__pycache__/clear_storage.cpython-38.pyc b/custom_components/hacs/operational/setup_actions/__pycache__/clear_storage.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53857abc02aeca4b22f4b65ea49b1439fdaf4274 GIT binary patch literal 977 zcmZWoPjAyO6u09f&AJxGkT`*egg8_}%@rXuF^!4svPpDYDk00n?mv>a*eS58J;9N0 zfRNfF65oO|U%*#R`wE@+2+U#9w<9&%r7z8*6$@q<2IBC<0^i`}_91NaGJiOsC4k18r(l ze1mGhu}lx0KQ15u%e&^m{@LM+)0fAGJ?D>vweF6<%v?yx)kl@i z;FWx$-?$sRdiG@jcpmtt_B$~xNc878Np&$850i8T5OSXCe5wmolr}-PL<3Awj&&ij zq~el`6puE!mbN=RceiT<*X|>zVM9>F3+WCuH(+;-P<%}QasXn_K-GjwI;2awpwG$c zy`_irPQ0%`;sW$Hwq!{3Dp;^JH9KGpR}IM)3~b(-Qc|*2gf>dv?MNSp59ap{s^;rV z#)EODY&|bDKj^_Z?r*F>Z~Fth?$p0f4A>qe%MCCLlb|QQ$M-&d{P?6Fg1|>`9sl|V`N>7- zPvhLYEO4H{BagsPL~(|4JhQMFTbZ3ZGp8QgStoaAZasFgZtl%I7Tma!?;Vm zU(jra_D;~){{(1gjCJ?NgO?{Sj;Ao}A4Rb|6a`m|Q}Fi|Op*DoRnfMZN zKiLgNU;(yzKRWUdzlOmo)7TIk6;wKLYL?QrFd;otnAdehP{B_N!@iwv6Xjnu^F3-k-j42}^$xX7uIfW>KnA8_Jyg5b_ywZQk;x3mE!t_2*0-sf zplb^de>Y0d9`)(Yd*>1-=+e5t7Z&Yr(C?%{x<>=3+fVP-_A5ZKpl0yS=<9%f!`1y~ z1)nF2O0bbyveD7gXD6dptP#st&bX4{DyVfCDG+9KU+Q~p3Sn=Rh)7=0Dr2M)*=m1c zG@U0IlanMaxMV6!OoxOWx)ZP)Np}JC_9S; zQHja`y>-nl^sSbQ!30FL1f*Iz9SHVwl?X;6nkRhxwdg>DXgezw+DZ7l5MASYhcAz( zrw7yLMhPjwDpi`=MIwPQd7ZE~!t|MTXrv--gK*l8DAoSq0ok;-_F5WxxanjfD$WzW z(03zw&SM}!K?4?7st_bC&V*wyciU|Cwq&tlRNK)0+TThb^|wv^me5(1WQrubkxvKB zZYvQf_5d=aU9_vPt~HsBVNw zQL<*qD4T#JRY{`QFp!g0im*J_dky&}p`9=U5 zegEZl9qd^?c&?c<_h%78-E`ca?VAqW{y@Twkiz`X#0bpn(Z867rX*al47kfM&fvV6 z3|m?^j{5&f=M`@@8`h1K*V;EH`pti=DwmO9O$&AyuX0(pU%i`J@EhhKu-D$>=HvYy PgD1^Tyl?e!ecS&4%$w1r literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/setup_actions/__pycache__/load_hacs_repository.cpython-38.pyc b/custom_components/hacs/operational/setup_actions/__pycache__/load_hacs_repository.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..330778c8c8755b10e8a774831edcab9f5bb38c9b GIT binary patch literal 1469 zcma)6Pj4JG6t`z)XJ`M+vZ1L|p@N13hefd4ii!iOP^%J1B$Pzml$JqgG#NXa*`2Xh zwzuhyHa!rId;^5kaN{d*<;;X6gol2XoaTukwdU@r`{=`QdKMTs6m`le2iCs z&@2$2D4#gQJ8`xUaW@eLpBHxQsU~Th;BPSTS9lsEE!DPThcxZj1sSeT(o?U&d41?> z)7MFG;?@yi<-%RW_B^DTv$psHqhpiFhorS~zDF}({A{yaC+$;=5nKPq>X=p1{lPV} zchQ&VM~^hekmIrQ(76XT14(?SP*`33{KaAjz8qGe#md$zdPi1$gXJh^icUmoim|Ob z=d3Ec!)A;hv4{%6#onh?*E$e-kS+=pyVtIKP+k2jSDTCd=;rQLl*&>}B$Mag>+ z7m+%ok>WGT%;J&YY%+X;zp5^M9lamj;){YrjH@&1ygj%md?=Q73qH|a&JMT;jNLNC zN^WoL?&ywT!TvH~X-Ne<10+nXwMSBw>Q>IAN{fOLVP1n?duAv1sgQ6-?J|0#*UtEs z@ck*xl=jS&@QhbeQfc>w8D|IiWKqMT8}=%3%N%8?se?@9D$mkl;A+pHquXbZb&Ecw z`A(%v|855I#2t05R#I+T{Fb)ByN zZ`YUnP%L*|c`jKyUC`z>y_~T;8WjG5P92QYYXB2QGXwfO-Sq!)%;?HQ-72PQs^seroOfAPtbBG?ii6(J~$u8vR5y2{c^&Ffm~^jztb3ju1q!mFeq z+o(<-8W*m4SvcAT$Ad#10#O$fYVN|kg1glFbozWeoxFBQrmFLNzS7+0s&ad+%j~+A zE?jV9+#&amvrJ2FCG)-2euD0HdtN9mSY2=nl}qh==xAHSC2a;!WlQzW?f3)RkBQ#wW{E{8Tqu4f*v($ zcrj|qF`jvG20Y)eVZC+-f2p;HeyrvJ7(^&Ozi8d?L(g*vDvWJA_P-Rf;99l=jAXScO*6da g`KA{;<{nUT@#9j7RUx1FU#jtc!w~mLALR6dUyFDM&;S4c literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/setup_actions/__pycache__/websocket_api.cpython-38.pyc b/custom_components/hacs/operational/setup_actions/__pycache__/websocket_api.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87b1f3428fb068b9a47e946bfa7ebc5a45638029 GIT binary patch literal 1714 zcma)6OK;Oa5VrGv*hyMSTS_Y;Asz*kTsR?wcnFCK2@ol&v=?i!w~6Vl*V^4swSp5I z`4Q!Ugv7t#%pdTTQ~v@dW+y?xRBf=eJ?rW4bnip%A3L?9_lsj zixz32UgvGmAsy5kd_gRdMbw-8oLC}DsC)dpSSHJ;`+P;Ll2y=KQJY^7Yh(@dPPFjR zAs3^?fwOt;@bXJGjJ08^yEEuMc(K)GNtDVsF}k}eRrkq*M}yumgyn3ynZM6oYZ>k_ z6VNmU!zCK-C32s$Xvl(4#U>6Z4-`wKj*V0Y;L-_4EZhsYw0}}+Mqs-zgcVM@$4tY| z=7@$m2xYPx4*_}z6%+C;ivTZ7@pOyHoOa%5_g(tO6hzceu={A&%$PVC&i2!Wnz4m7 zdp7HHT$~2|1i*lqak@?QUT9PC=W#(P!T1hCe*s~f56*k%o%6;$Dnza+zA1eM2AJ|u z1#DGQ%b|J>HFBt#LtYN~In>Ibb`EuNXu+bW097v@u0TNDar&p`Bbr2db6WpX2_irNAB5Od(c3=Io*T#AUDrg;NRZDgp%85 z%s934Fz@8*8B;M?lR98A$+daT)2_whl3Vk=XFZKXAvgYe$^nv!oaDzW*?9cf)r9u} zvXES#w+)a5Zwn;y{2Wd066AHZe#+W literal 0 HcmV?d00001 diff --git a/custom_components/hacs/operational/setup_actions/categories.py b/custom_components/hacs/operational/setup_actions/categories.py new file mode 100644 index 0000000..f3c5688 --- /dev/null +++ b/custom_components/hacs/operational/setup_actions/categories.py @@ -0,0 +1,42 @@ +"""Starting setup task: extra stores.""" +from custom_components.hacs.const import ELEMENT_TYPES +from ...share import get_hacs +from ...enums import HacsCategory, HacsSetupTask + + +def _setup_extra_stores(): + """Set up extra stores in HACS if enabled in Home Assistant.""" + hacs = get_hacs() + hacs.log.debug("Starting setup task: Extra stores") + hacs.common.categories = set() + for category in ELEMENT_TYPES: + enable_category(hacs, HacsCategory(category)) + + if HacsCategory.PYTHON_SCRIPT in hacs.hass.config.components: + if HacsCategory.PYTHON_SCRIPT not in hacs.common.categories: + enable_category(hacs, HacsCategory.PYTHON_SCRIPT) + + if ( + hacs.hass.services._services.get("frontend", {}).get("reload_themes") + is not None + ): + if HacsCategory.THEME not in hacs.common.categories: + enable_category(hacs, HacsCategory.THEME) + + if hacs.configuration.appdaemon: + enable_category(hacs, HacsCategory.APPDAEMON) + if hacs.configuration.netdaemon: + enable_category(hacs, HacsCategory.NETDAEMON) + + +async def async_setup_extra_stores(): + """Async wrapper for setup_extra_stores""" + hacs = get_hacs() + hacs.log.info("setup task %s", HacsSetupTask.CATEGORIES) + await hacs.hass.async_add_executor_job(_setup_extra_stores) + + +def enable_category(hacs, category: HacsCategory): + """Add category.""" + hacs.log.debug("Enable category: %s", category) + hacs.common.categories.add(category) diff --git a/custom_components/hacs/operational/setup_actions/clear_storage.py b/custom_components/hacs/operational/setup_actions/clear_storage.py new file mode 100644 index 0000000..3e55873 --- /dev/null +++ b/custom_components/hacs/operational/setup_actions/clear_storage.py @@ -0,0 +1,23 @@ +"""Starting setup task: clear storage.""" +import os + +from custom_components.hacs.share import get_hacs +from ...enums import HacsSetupTask + + +async def async_clear_storage(): + """Async wrapper for clear_storage""" + hacs = get_hacs() + hacs.log.info("Setup task %s", HacsSetupTask.CATEGORIES) + await hacs.hass.async_add_executor_job(_clear_storage) + + +def _clear_storage(): + """Clear old files from storage.""" + hacs = get_hacs() + storagefiles = ["hacs"] + for s_f in storagefiles: + path = f"{hacs.core.config_path}/.storage/{s_f}" + if os.path.isfile(path): + hacs.log.info(f"Cleaning up old storage file {path}") + os.remove(path) diff --git a/custom_components/hacs/operational/setup_actions/frontend.py b/custom_components/hacs/operational/setup_actions/frontend.py new file mode 100644 index 0000000..f5950ed --- /dev/null +++ b/custom_components/hacs/operational/setup_actions/frontend.py @@ -0,0 +1,46 @@ +from hacs_frontend.version import VERSION as FE_VERSION + +from custom_components.hacs.helpers.classes.frontend_view import HacsFrontend +from custom_components.hacs.helpers.functions.information import get_frontend_version +from custom_components.hacs.share import get_hacs +from ...enums import HacsSetupTask + + +async def async_setup_frontend(): + """Configure the HACS frontend elements.""" + hacs = get_hacs() + hacs.log.info("Setup task %s", HacsSetupTask.FRONTEND) + + # Custom view + hacs.hass.http.register_view(HacsFrontend()) + + # Custom iconset + if "frontend_extra_module_url" not in hacs.hass.data: + hacs.hass.data["frontend_extra_module_url"] = set() + hacs.hass.data["frontend_extra_module_url"].add("/hacsfiles/iconset.js") + + hacs.frontend.version_running = FE_VERSION + hacs.frontend.version_expected = await hacs.hass.async_add_executor_job( + get_frontend_version + ) + + # Add to sidepanel + if "hacs" not in hacs.hass.data.get("frontend_panels", {}): + custom_panel_config = { + "name": "hacs-frontend", + "embed_iframe": True, + "trust_external": False, + "js_url": "/hacsfiles/frontend/entrypoint.js", + } + + config = {} + config["_panel_custom"] = custom_panel_config + + hacs.hass.components.frontend.async_register_built_in_panel( + component_name="custom", + sidebar_title=hacs.configuration.sidepanel_title, + sidebar_icon=hacs.configuration.sidepanel_icon, + frontend_url_path="hacs", + config=config, + require_admin=True, + ) diff --git a/custom_components/hacs/operational/setup_actions/load_hacs_repository.py b/custom_components/hacs/operational/setup_actions/load_hacs_repository.py new file mode 100644 index 0000000..9ea9c8c --- /dev/null +++ b/custom_components/hacs/operational/setup_actions/load_hacs_repository.py @@ -0,0 +1,37 @@ +"""Starting setup task: load HACS repository.""" +from custom_components.hacs.const import VERSION +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.functions.information import get_repository +from custom_components.hacs.helpers.functions.register_repository import ( + register_repository, +) +from custom_components.hacs.share import get_hacs +from ...enums import HacsSetupTask + + +async def async_load_hacs_repository(): + """Load HACS repositroy.""" + hacs = get_hacs() + hacs.log.info("Setup task %s", HacsSetupTask.HACS_REPO) + + try: + repository = hacs.get_by_name("hacs/integration") + if repository is None: + await register_repository("hacs/integration", "integration") + repository = hacs.get_by_name("hacs/integration") + if repository is None: + raise HacsException("Unknown error") + repository.data.installed = True + repository.data.installed_version = VERSION + repository.data.new = False + hacs.repo = repository.repository_object + hacs.data_repo = await get_repository( + hacs.session, hacs.configuration.token, "hacs/default" + ) + except HacsException as exception: + if "403" in f"{exception}": + hacs.log.critical("GitHub API is ratelimited, or the token is wrong.") + else: + hacs.log.critical(f"[{exception}] - Could not load HACS!") + return False + return True diff --git a/custom_components/hacs/operational/setup_actions/sensor.py b/custom_components/hacs/operational/setup_actions/sensor.py new file mode 100644 index 0000000..d7c8ec3 --- /dev/null +++ b/custom_components/hacs/operational/setup_actions/sensor.py @@ -0,0 +1,24 @@ +""""Starting setup task: Sensor".""" +from homeassistant.helpers import discovery + +from custom_components.hacs.const import DOMAIN +from custom_components.hacs.share import get_hacs +from ...enums import HacsSetupTask + + +async def async_add_sensor(): + """Async wrapper for add sensor""" + hacs = get_hacs() + hacs.log.info("Setup task %s", HacsSetupTask.SENSOR) + if hacs.configuration.config_type == "yaml": + hacs.hass.async_create_task( + discovery.async_load_platform( + hacs.hass, "sensor", DOMAIN, {}, hacs.configuration.config + ) + ) + else: + hacs.hass.async_add_job( + hacs.hass.config_entries.async_forward_entry_setup( + hacs.configuration.config_entry, "sensor" + ) + ) diff --git a/custom_components/hacs/operational/setup_actions/websocket_api.py b/custom_components/hacs/operational/setup_actions/websocket_api.py new file mode 100644 index 0000000..f77d33e --- /dev/null +++ b/custom_components/hacs/operational/setup_actions/websocket_api.py @@ -0,0 +1,35 @@ +"""Register WS API endpoints for HACS.""" +from homeassistant.components import websocket_api + +from custom_components.hacs.api.acknowledge_critical_repository import ( + acknowledge_critical_repository, +) +from custom_components.hacs.api.check_local_path import check_local_path +from custom_components.hacs.api.get_critical_repositories import ( + get_critical_repositories, +) +from custom_components.hacs.api.hacs_config import hacs_config +from custom_components.hacs.api.hacs_removed import hacs_removed +from custom_components.hacs.api.hacs_repositories import hacs_repositories +from custom_components.hacs.api.hacs_repository import hacs_repository +from custom_components.hacs.api.hacs_repository_data import hacs_repository_data +from custom_components.hacs.api.hacs_settings import hacs_settings +from custom_components.hacs.api.hacs_status import hacs_status +from custom_components.hacs.share import get_hacs +from ...enums import HacsSetupTask + + +async def async_setup_hacs_websockt_api(): + """Set up WS API handlers.""" + hacs = get_hacs() + hacs.log.info("Setup task %s", HacsSetupTask.WEBSOCKET) + websocket_api.async_register_command(hacs.hass, hacs_settings) + websocket_api.async_register_command(hacs.hass, hacs_config) + websocket_api.async_register_command(hacs.hass, hacs_repositories) + websocket_api.async_register_command(hacs.hass, hacs_repository) + websocket_api.async_register_command(hacs.hass, hacs_repository_data) + websocket_api.async_register_command(hacs.hass, check_local_path) + websocket_api.async_register_command(hacs.hass, hacs_status) + websocket_api.async_register_command(hacs.hass, hacs_removed) + websocket_api.async_register_command(hacs.hass, acknowledge_critical_repository) + websocket_api.async_register_command(hacs.hass, get_critical_repositories) diff --git a/custom_components/hacs/repositories/__init__.py b/custom_components/hacs/repositories/__init__.py index ddd6003..91d89c4 100644 --- a/custom_components/hacs/repositories/__init__.py +++ b/custom_components/hacs/repositories/__init__.py @@ -1,16 +1,16 @@ -"""Initialize repositories.""" -from custom_components.hacs.repositories.theme import HacsTheme -from custom_components.hacs.repositories.integration import HacsIntegration -from custom_components.hacs.repositories.python_script import HacsPythonScript -from custom_components.hacs.repositories.appdaemon import HacsAppdaemon -from custom_components.hacs.repositories.netdaemon import HacsNetdaemon -from custom_components.hacs.repositories.plugin import HacsPlugin - -RERPOSITORY_CLASSES = { - "theme": HacsTheme, - "integration": HacsIntegration, - "python_script": HacsPythonScript, - "appdaemon": HacsAppdaemon, - "netdaemon": HacsNetdaemon, - "plugin": HacsPlugin, -} +"""Initialize repositories.""" +from custom_components.hacs.repositories.appdaemon import HacsAppdaemon +from custom_components.hacs.repositories.integration import HacsIntegration +from custom_components.hacs.repositories.netdaemon import HacsNetdaemon +from custom_components.hacs.repositories.plugin import HacsPlugin +from custom_components.hacs.repositories.python_script import HacsPythonScript +from custom_components.hacs.repositories.theme import HacsTheme + +RERPOSITORY_CLASSES = { + "theme": HacsTheme, + "integration": HacsIntegration, + "python_script": HacsPythonScript, + "appdaemon": HacsAppdaemon, + "netdaemon": HacsNetdaemon, + "plugin": HacsPlugin, +} diff --git a/custom_components/hacs/repositories/__pycache__/__init__.cpython-37.pyc b/custom_components/hacs/repositories/__pycache__/__init__.cpython-37.pyc deleted file mode 100644 index 4aad74a78b2946934b8fad2848ad99cead9ea64b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 796 zcma)4J#X7E5T#^UmJ};B&?RG56{v+wS%M%*VIT$yC$PMfLkU%4Z36rdD0;Am{*wNI z?D-$rxpwN9HB(6zc4A}^2_4=&@jc!hc$20HFzfm0*Y`&sfIl|bt$_ho=4*eLPy+~R zulA|OeCT@?_h^p=FmO1aeHOyd;XWO(2u2QvG-e4*93IdkHiSbDU>^rK#1T&Lu#SF* zFsVBhEf)|w}jWS$nx$CB>OU&FMN6fI=6NyvN^^))`)O}_B^$~e>)@kEBQR_@~T<+|2 z%y&jQY@Ed`JJth)YEdBsvufQc!B8bwBRJ-&9j{C|T=Kj=}G(Q^vC&Ret%a?7kBe-=*!pZqL>s9K#t5m z7hcZuO7Nd#`F>uxop1hjzBxj~yeovp%|=HTOw?HkeJqz9Y>Bkz&G#-j4XT% zKj4*#Utq#Eq7<>jR!;9O_wLTmzKP=q=rw(MLoY4>@5WhQJ?)(8uesBv1Q3)?=~9Qe z&~*&%&<^vUXR$}S%!j_kUD{&-3@r9($RZe7+@l+;5Bm^|pa!G8+=3HXA z%85+Q6zg7(^t(c6SU;%XnDKH+@rk2rHM`n)PXBjzTxYdEzW}QN|8f8T literal 0 HcmV?d00001 diff --git a/custom_components/hacs/repositories/__pycache__/appdaemon.cpython-37.pyc b/custom_components/hacs/repositories/__pycache__/appdaemon.cpython-37.pyc deleted file mode 100644 index b3e8e259d646cf4230924c45cde813c2981521f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2631 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 cLsx4UI1~J~&eQL)%5C&q+F|g6+{4fLA41|t<8 diff --git a/custom_components/hacs/repositories/__pycache__/integration.cpython-38.pyc b/custom_components/hacs/repositories/__pycache__/integration.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2d60350a28e84d3baeb6a6936b097f724b71383b GIT binary patch literal 3510 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 diff --git a/custom_components/hacs/repositories/__pycache__/netdaemon.cpython-38.pyc b/custom_components/hacs/repositories/__pycache__/netdaemon.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9c28b172c7eb86577403e134282a216bca761c38 GIT binary patch literal 2916 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 JJ64B^{eS5;JMaJi diff --git a/custom_components/hacs/repositories/__pycache__/plugin.cpython-38.pyc b/custom_components/hacs/repositories/__pycache__/plugin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..635cbc057e9dd02b4be2dc84886450b0fe4cc998 GIT binary patch literal 2643 zcma)8TW=Fb6rS1pZtPr0R0^WVy`a`@W%ojPs47H(LXawoAoOCjnr?T-&N}PeWoAtp zE4(z3c;O!)r1+8F(dYhwdF>N_Ausftv6GktwOwn@&YV4GX3lqRzF)6L1fK7n{Keje z+26RBeH>U^fuZgI;iN-2_O2!0otZ?q=S=@Azi!Wx*iqgp}MQ z+~>hA;el{>>`uh3d!!kT&R)x6r5fErHp*<+OLNso^Tz7e*Y34mz=`B`lj{0voTzVp zPefUzMGlO4WZV^Hq0*|5Tgd6AIcME86D*Gh0ytq$RNof8o{$OdGyB-ng}MO~LBJN$ zu{iD6k4YD-7Gz(QC> zFy{I-R_7G%V2|c*#+|RYy4IfeQ09$8Yg?m*_U7g$7%&(N0p_jtK5f5v2~X40 z4&GinNg;)vo0!ND6N%|(kN_u@Sz0xzd;?i$4#`=ZodR+ihBAR;#Ut=X0AoV?@AxX7A8HObi-1 zZ^M#n+($hY01f+*+?~9ylV8q1LP?0peqC{x&F?L6klLnWs1py66ZRKP=7--yoI)5v zr0$icG{xtc~0Ys&PRz zm-GU{U*zoJba#mrOfDl?0ir!A21O+#!lP{|y0}T1#fi|KO7mVOba0@nJt1Wwm3+%+ zsw%FAO8fAZ)v(lFX6lP}jTG>QNg|YLS`!a`f@i+!M!kHnH9HLCt#cr!(85HDLOhowhvWi~wdS0zPn|O~!pB@A-$&7pkbDe8 zdvRHcoXZc8(?s$ikSV&lp7wG`CYCBS6zVw6OOq33Q2rcr)glmLEgF=ADaxaBGuWO` zJCRaYX$-JyFd1C}sGxLDO7z$QfUQ;Xh*tKVvrYT%m_j;Gd2!p64v_Vj{{zY2bVwCc zVb|J-F}__>iSj4sl(*~;{Npf<5(}_ zS<`O^XI_P^73LeUEb38i+W;t~6cr?F8xk+z8tU$BERu0W7Vr)?GA@p{pYm7Y@ z#@Y0X&loQf#!UU!K`9GpbJdoqmhv+=U0ylZdIk^g2aU{!3W?zS57^0<-~ilf0{J(gjXIvzc+p}@4ese&3@c!MFg(D z|NQZrD-A;a!k78wfv^s@+JJ$RKH-!*IUPBDhZ62`FLy^?-vhbE{k%5v`@WUyd3_Z0 z14=$4JmBF&!b9QhyZw+yTcq2lF5bvvr8UCNzj&Q#q(^`{RKG1h5Wz-R42T5vWy0(63=o4@uwWbsoU8&SCrT3?37Y^g|59EaSqr zZ3tU7g6g%G1Y}yr3<>TWFMQVE#$BmzVbEls9MD5T$u8X`nZsN@eR|x1_gdz&Iy{3c z%%Ueygr{?DgL`OaP2J)({4(>v?(fnkPY|PcA!~fN|A1Tl4#O@vAcy1@uxA6yN-~-`vSZ8XW*)0? zcaJ`J5A^;gaywhfma`T02;@@-tGn(YrR4r2@P}WV27rWB z_$7b?>L_}fhTN=NNXp&noaLRi#Oo+6CyAa&VM$!obUajR)k-H-oubqo%y*u~h3>v$ zmf&Rs6#staQ??)_sI^!C*f3d^S}ucDuHbMA?V*-JAc2*Ml#ZoVKcs+}Kos0c5;JUE zDF&tv)y(5Wm|(ty@r9J7RB{#loyM6yIMEYj>ekt0EYF~}X6J!&k|aW@t|!l;I$Z4E z1oM5!F8X?XF`UPVtMe7v0#Q5YSY3jFxNR43<9M_Q*zw`%Q;$0Ij2F;0=$2`9`2?E& z!{Gv02r#681=dv%jsi=a;?5Ropm)rnrlB|NLwPxcvbt64ju@s;xfr`-Z}Vo?mG6QV zvpTDgJYtr0uym=-$BR)nk50S>^Hcx>aoW^_kgE$Pf;u{X5e;^{1xTsjG7h$i?zD>UpaNm@Vm(); zYaGGwhcHXM0RwTCspq^5$JM11r9R=R8{y%Ar!X825o4p0PvBT)rpeg-Nu19tb;fv^ zFeaC9gV=#BHsu;hh%wWeHTXop$-I$W diff --git a/custom_components/hacs/repositories/__pycache__/python_script.cpython-38.pyc b/custom_components/hacs/repositories/__pycache__/python_script.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a366aa77494ba27587d5e1281fb30df0527e29e GIT binary patch literal 2688 zcma)8UvC>l5Z}E&_MKzrPg`1AmC7wGs!MQ$_%Esg3X~S1v`W)ToP<`V+qHAn`Oev0 zQ(`$1Y9jT8Z%|cAcq?CnXTHF`A|de=@&Yq!JBbs5IP2!-X6I&Sc78MC$F*98!1dtg z-`StQ{edq>FArWmhg+=y;UprQawnr*CvqtAZsvBq$g{kc`Q1`fvb>*_yFnCKzLbUC zN>ribYr@Ms*daU+-nJXf@bD37R0da{)-ie8~oUC2$4R8g`VYte@HSD?{KbIw|6CRiSK1!#nA zp}#HKZ6Omhd-QV0FX|g$1c@j_>~I>n+=;yBqy_PEZzqU+QQ|%??T~F6l}9l}0S|d) zheRQtJr+agI2MJl^Q1^-+^;vP<^} zB||zS9f!F?y6f!`%lo?2DO)b!xEgjUYzCgyF<;{ja%`5n;G;K~xu2&xjk9zhnr3#& z3$?ZaLHMfQ6Ve2XrJ%`}@p!Cb6Sn$UW>44nS@9G!<_;vx3M9hF)E<7fcJILod$hcI z|KXYml5v(yIVo~2a&5d`tk+FRif*BW39}-JGb;kSz?zau^L8d0j`5VpT5=Z5LXJbm z5`++AugL!GC9u{?+e=Ab>7vV$qT4HSu&$QYAqPuxf|gXMrKupBy-isKi)d9LAx?z` zv`Q=RtJ1;6<2h_j4kDqbJ-(2~BrU_O=7GQ=lMd}TFQ8yvI&jp^U}070zRc?f5jF>N z)6S@dYdmz=cu66JnHfdJ@Hk#kc@gZ&%Ln8QA+oIVvTo7hc0A^DcMI`U`jUKqbITbzyNEJa>G&_m9+kI15^xVW z?r%{_Hr6(74XKhkVID2>?!Ap0WK0|BOPEGzYdV<|6TQ2akow?4udIsFW z?in5q$PpLg0;er(huQaa+*V8b z3-wgh^Fr4VA6Xjbdf?ws^&6^j&dhDknqutg z%@yf`ioA;CnEMxEwV5Xj+P#JV4!`*XbSJ34dBVL@KDs;ATBnNOC$NV_Fr<6*SQ%WK zDg$hwqcU)Z?y)jh1j(oj7NHDaTy8wsSR6V^pI!zUTJyoVgoEHu*#dbJ`GUWK-n!MZzDlC%0(paAi)kW!BZ`5=g@Rms#ISn z`2ntfh~!v47Dihh0n;7OQix&V%>M_N2G>qx>4d2Uz1yhbu)(i(+ZE+UxbQKOPjR(29vF#$!Nrb5bqQ5$GYxL zzdI~d>EeEuPuWl8{jM5~+V@XUnYh&I=Lr^@YNk2-@9Nshvk)*R=jL=Yho6pmk}QKy d_CSuDqs0lHqX}-0l6D81fWq8(FiXcj_a9!w+>`(S literal 0 HcmV?d00001 diff --git a/custom_components/hacs/repositories/__pycache__/removed.cpython-37.pyc b/custom_components/hacs/repositories/__pycache__/removed.cpython-37.pyc deleted file mode 100644 index f7df1ddfbb51f1aeec46b492916ed36b64ba9361..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 829 zcmZWoF^|(Q6t*2VNz;^rk%3btq#)F8Ocgo-f~`mgCr~7?(%8LAo5nrc1yU83-pW7V z5AbW)y)ZB_vNG|U(xVbDdN1Geb6&phIeFCYM+ofKw@+`sV}yRVWN#F}3Anxofujs@ z%t?haEbxqQkJHci8R8x8Um@;q$rfiGCofPMtdr-HnJ7#$l`2tUA>RoO_odXOk*XAW z@DnRb@$d`gT^?Q0Ean)<_Ux_G9mCT-!DeiuYqpK`mB_WMEv?FWZsS%@ zUNO@w#Z#bP%xn3+68v0nkVAQH@`3jz5wO*`OJk`^qo546GHe`&Kg<;GFS{OIS)U#BJ zYO1>MLhH>%^A)KZfPX(27P6j}=fh&9ja;xoE|#(uwb8?iywJmI(QrhNwC6ThHnz8F zX)s5l@1!_Qh(tKT>pTBVZ*Z_X?K^^Xz7Rk#Vr(J#s&Y7H>~fV?yNYsOn)S3%_K2~( zt|bU5Wvv-g9VjXHJ{7nSx}9_)Wd)-K-Rh=Wb?OenkxSj|-NQPD5Rn-Fy+~V0sSRLq hvH&jBP0eE677p69U0p95j&{T!!Y_ifV@q-9{Q=Ws*l_>= diff --git a/custom_components/hacs/repositories/__pycache__/repository.cpython-37.pyc b/custom_components/hacs/repositories/__pycache__/repository.cpython-37.pyc deleted file mode 100644 index 94f2ced28d7b2f4a5c172d1ba8b8e992bda65b86..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11706 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 diff --git a/custom_components/hacs/repositories/__pycache__/theme.cpython-37.pyc b/custom_components/hacs/repositories/__pycache__/theme.cpython-37.pyc deleted file mode 100644 index 8cf0e6ef73089e55382c8feb88521a61a4bdcfbc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2335 zcmaJ@OOG5i5VpH}`Y}5*`%EC4hem<}Bf-ppNFad_5C|Iytxz5ywFIWq_RMzEkBQv_ z+173jY^3~%XwmKkiQmBw;O-mZlp|M8RJrG|A`v~ZySi*w*~oHi75wKc0N| zXF$kb__25#5I%;c_Mqb=B%E?9qq!AYlyI9nnVmbK19FSISu1x#*T`Px<$mZ>@&(~O z51tYp2`{dTW;R|p?22{IWmXnE)FKF}G z+lyj#S4>NlRHZz8VKwS5sy}}mi)odV1uW~0lY+Bxk_lEsxrou);<4v~dLINqLJ9}6 zI1TM*WDLjR_ESG}xWijdNeIW`9=zRV`=R%YI3)C8^KBiV)BCvXdfmKMyN_{S_~5$* z4SlNbp{vLVIiu%dRaiL?DhS^h(24u!7KAQ1wNL2J7<@1FWyXDwAn0rj-kr27e=#0F0w33oZP518 z^>icMR8K(OI@>-1kCZ%m0v>S14wAACzJ|acJ0lBUAKmZQJ&qecTMV4CU^R( zlC!v)Nn!G@ZdK7ljWnq@`-$ooWz|QFvm`32!KH8=KJv0)KhDkTpprshf+^irRU|9* zQvww5MZt|E5kGB9G1eXgCyQbsF$x+i8O{K`m@LamZh}^BVYiLr{?dgjq%5V9J2-X; z$6WAK&6M_x$Jta~L2b*-1LZ7^g;E1Y?t(fDmiyQKVzY99wyrI^>$rY<;gKy_bcpU$ zA39?9Y=?H}4t1yt&7!Z{z#(Wo&~DKB`ie)du(PBTQfo=6yYO+5QfiA^_fP|sVL`GA zGXDeu48gTiCv9Oh0NG8wGasuHlIRpQhBTZ)_}#jDS4iVIS1qoa6RbA zx8W6T&x0y2ERr6KtRR<{@zVq@Se8@y1}ax^dksDZz1ktBw0-vIX#oP!Mmm3AjU{@Fv_2INJSvxQPf6$ii!I<7mphQd>79WTV?xW_WXs;4fVvP>B z51{xzjFcFP#qF<5<$)2iP7RX+{$AuJAsT$q`)YHIWa@zbVRPz_FyLl?nE2*r{|o3Q BUI_pI diff --git a/custom_components/hacs/repositories/__pycache__/theme.cpython-38.pyc b/custom_components/hacs/repositories/__pycache__/theme.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..69a60f1220c0d2fd2c42a03556c9bc2414b07d27 GIT binary patch literal 2835 zcma)8TW=dh6rS0e*SFlEp_j;j`d}eBs#JkgDlQETDS{xSDHKa+wAvXvYu3B&%(^8j z=Y>S-3%>wV$>FWPgJ=H0yz<0f=nI@PYdcOu1iRX^XV1>%T)uP8JgV0#1g@`t{)0up z{fQs*mjfT4z@zqoaMC86ax12Nt8G!_?bz-+ZO8CV?DoC3XLvU*_5HSQcrPyZEA0v; zpA%l<{t4l}aE|SEm6soqW+i`XD-M)ubW+*K4n$w5MwB#mKHYlQ`U^HCO{(iVL8$Kk z7>Yp_r3nZYP_QQksfw~x9)hHXlC2;U-Qs(p6D6E=qFAsb=nEK8?uzWowA&Rj#AW9% zd-9=nff1xl!BUIUw*8!Rz+P^j_-#kH+~MvCIi_uod%Sc)+F%VY!`tT-UWIp=FY-EH zIHB#zbK;P873^EmWmIh+Ez1|p>&(E=no|RLd{7(p1(1v!lQBIdl#J+z^ekqN=-4?W zhIccsS2CQ>@o%|TVO4n7CVZV+$gu@(gBDI+eUL<16vR<3TG}fdy}jF9)UFy1gw#G` z5sY9=J3Pn&UG5Cy*!WJnarz8KEY2j%2qc0-yZbx$zq+seaN>Ji3ezMLNv54akR51G zihi02U5?W*h>Zx=16F$~O1iOVTG~+}?#LP}_Y&g7ScrgN>?L`1dlOdcMBUABsIs)r z!n8j~6Ifks9)MRjeCvnz+I#H$}AeKsl_41VUr7a z51uV}R09Zjk@RTK`W5gFyxnugE@I*oBrgizF7JtKD3iv_#8&PX+NjM}ZtF1U$C@-f z?V!%u2~#O_wJ?mKM`RWD0gq47s!L#39E9Q}l~>@GygDP-kXr-tCOpc($Ie&JY?`TX z!KgA!_#G$R1(I(}%^KU7(h)he9}sTu0NB1I$+|-_Cv!X24;A@71sNsZk;HloBMg|X z?+ud%WM2lUsT90RSBjaa3c&YF6?BvGfe_208+3){Z=d<-k~!x* zhS)|J7?1r1%!rK1DFGi*^bxm?5DfG)@_pmT8d+lm1eNQ++1x>nyGN9gCwHE#kEnW_ zIoyNs?idOO_Ype0{K7t_KjA8+o-cdc2X2@BGrxaIK* zuZ+mZKDA*7l+=2w5SMCR{ss~p(pR*0xARir^-Xyl)ZfpqntGX;f(en5!w@hlOxexd zEa*Zq-GG7lawAfWB+VL_YjG4LS?=Ccjhm{urWZlhPZRcZvW+fhQV7H?1QEP1GxaQj zr1wR_4JQ$y+Lodt-vO=wI;>(U1VoIzyz3>wwUL~K|ngO&v*+Ii{Gpg^mEUbC$06Mzkk+`vWNf?eheMu^OL z0_|^FMa1h-H-Xy9BBh2xy&mS}V$r!E--Eddn+36!z!@0Q{MuPCFN|-}?Pd)-B_1iD zQ&t%3r+f%!45n+0JsAe^R;2xbOrghShehz-gAL_}NH7-iV<5Ud>3X4n zuFK|BN03NU^-I8E5m!q#SNG#mOA#;8-IgftS{@o&IA7L^oJSRACFqqu-Ki`-66#xJL literal 0 HcmV?d00001 diff --git a/custom_components/hacs/repositories/appdaemon.py b/custom_components/hacs/repositories/appdaemon.py index 92d19e4..15639f7 100644 --- a/custom_components/hacs/repositories/appdaemon.py +++ b/custom_components/hacs/repositories/appdaemon.py @@ -1,85 +1,72 @@ -"""Class for appdaemon apps in HACS.""" -from aiogithubapi import AIOGitHubException -from integrationhelper import Logger - -from .repository import HacsRepository -from ..hacsbase.exceptions import HacsException - - -class HacsAppdaemon(HacsRepository): - """Appdaemon apps in HACS.""" - - def __init__(self, full_name): - """Initialize.""" - super().__init__() - self.data.full_name = full_name - self.data.category = "appdaemon" - self.content.path.local = self.localpath - self.content.path.remote = "apps" - self.logger = Logger(f"hacs.repository.{self.data.category}.{full_name}") - - @property - def localpath(self): - """Return localpath.""" - return f"{self.hacs.system.config_path}/appdaemon/apps/{self.data.name}" - - async def validate_repository(self): - """Validate.""" - await self.common_validate() - - # Custom step 1: Validate content. - try: - addir = await self.repository_object.get_contents("apps", self.ref) - except AIOGitHubException: - raise HacsException( - f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" - ) - - if not isinstance(addir, list): - self.validate.errors.append("Repostitory structure not compliant") - - self.content.path.remote = addir[0].path - self.content.objects = await self.repository_object.get_contents( - self.content.path.remote, self.ref - ) - - # Handle potential errors - if self.validate.errors: - for error in self.validate.errors: - if not self.hacs.system.status.startup: - self.logger.error(error) - return self.validate.success - - async def registration(self): - """Registration.""" - if not await self.validate_repository(): - return False - - # Run common registration steps. - await self.common_registration() - - # Set local path - self.content.path.local = self.localpath - - async def update_repository(self): - """Update.""" - if self.hacs.github.ratelimits.remaining == 0: - return - await self.common_update() - - # Get appdaemon objects. - if self.repository_manifest: - if self.data.content_in_root: - self.content.path.remote = "" - - if self.content.path.remote == "apps": - addir = await self.repository_object.get_contents( - self.content.path.remote, self.ref - ) - self.content.path.remote = addir[0].path - self.content.objects = await self.repository_object.get_contents( - self.content.path.remote, self.ref - ) - - # Set local path - self.content.path.local = self.localpath +"""Class for appdaemon apps in HACS.""" +from aiogithubapi import AIOGitHubAPIException + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.classes.repository import HacsRepository +from custom_components.hacs.enums import HacsCategory + + +class HacsAppdaemon(HacsRepository): + """Appdaemon apps in HACS.""" + + def __init__(self, full_name): + """Initialize.""" + super().__init__() + self.data.full_name = full_name + self.data.full_name_lower = full_name.lower() + self.data.category = HacsCategory.APPDAEMON + self.content.path.local = self.localpath + self.content.path.remote = "apps" + + @property + def localpath(self): + """Return localpath.""" + return f"{self.hacs.core.config_path}/appdaemon/apps/{self.data.name}" + + async def validate_repository(self): + """Validate.""" + await self.common_validate() + + # Custom step 1: Validate content. + try: + addir = await self.repository_object.get_contents("apps", self.ref) + except AIOGitHubAPIException: + raise HacsException( + f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" + ) from None + + if not isinstance(addir, list): + self.validate.errors.append("Repostitory structure not compliant") + + self.content.path.remote = addir[0].path + self.content.objects = await self.repository_object.get_contents( + self.content.path.remote, self.ref + ) + + # Handle potential errors + if self.validate.errors: + for error in self.validate.errors: + if not self.hacs.status.startup: + self.logger.error("%s %s", self, error) + return self.validate.success + + async def update_repository(self, ignore_issues=False): + """Update.""" + await self.common_update(ignore_issues) + + # Get appdaemon objects. + if self.repository_manifest: + if self.data.content_in_root: + self.content.path.remote = "" + + if self.content.path.remote == "apps": + addir = await self.repository_object.get_contents( + self.content.path.remote, self.ref + ) + self.content.path.remote = addir[0].path + self.content.objects = await self.repository_object.get_contents( + self.content.path.remote, self.ref + ) + + # Set local path + self.content.path.local = self.localpath diff --git a/custom_components/hacs/repositories/integration.py b/custom_components/hacs/repositories/integration.py index 7b9f874..d8625ff 100644 --- a/custom_components/hacs/repositories/integration.py +++ b/custom_components/hacs/repositories/integration.py @@ -1,93 +1,97 @@ -"""Class for integrations in HACS.""" -from integrationhelper import Logger - -from homeassistant.loader import async_get_custom_components - -from custom_components.hacs.hacsbase.exceptions import HacsException -from custom_components.hacs.helpers.filters import get_first_directory_in_directory -from custom_components.hacs.helpers.information import get_integration_manifest -from custom_components.hacs.repositories.repository import HacsRepository - - -class HacsIntegration(HacsRepository): - """Integrations in HACS.""" - - def __init__(self, full_name): - """Initialize.""" - super().__init__() - self.data.full_name = full_name - self.data.category = "integration" - self.content.path.remote = "custom_components" - self.content.path.local = self.localpath - self.logger = Logger(f"hacs.repository.{self.data.category}.{full_name}") - - @property - def localpath(self): - """Return localpath.""" - return f"{self.hacs.system.config_path}/custom_components/{self.data.domain}" - - async def validate_repository(self): - """Validate.""" - await self.common_validate() - - # Custom step 1: Validate content. - if self.data.content_in_root: - self.content.path.remote = "" - - if self.content.path.remote == "custom_components": - name = get_first_directory_in_directory(self.tree, "custom_components") - if name is None: - raise HacsException( - f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" - ) - self.content.path.remote = f"custom_components/{name}" - - try: - await get_integration_manifest(self) - except HacsException as exception: - self.logger.error(exception) - - # Handle potential errors - if self.validate.errors: - for error in self.validate.errors: - if not self.hacs.system.status.startup: - self.logger.error(error) - return self.validate.success - - async def registration(self): - """Registration.""" - if not await self.validate_repository(): - return False - - # Run common registration steps. - await self.common_registration() - - # Set local path - self.content.path.local = self.localpath - - async def update_repository(self): - """Update.""" - if self.hacs.github.ratelimits.remaining == 0: - return - await self.common_update() - - if self.data.content_in_root: - self.content.path.remote = "" - - if self.content.path.remote == "custom_components": - name = get_first_directory_in_directory(self.tree, "custom_components") - self.content.path.remote = f"custom_components/{name}" - - try: - await get_integration_manifest(self) - except HacsException as exception: - self.logger.error(exception) - - # Set local path - self.content.path.local = self.localpath - - async def reload_custom_components(self): - """Reload custom_components (and config flows)in HA.""" - self.logger.info("Reloading custom_component cache") - del self.hacs.hass.data["custom_components"] - await async_get_custom_components(self.hacs.hass) +"""Class for integrations in HACS.""" +from homeassistant.loader import async_get_custom_components + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.classes.repository import HacsRepository +from custom_components.hacs.enums import HacsCategory +from custom_components.hacs.helpers.functions.filters import ( + get_first_directory_in_directory, +) +from custom_components.hacs.helpers.functions.information import ( + get_integration_manifest, +) +from custom_components.hacs.helpers.functions.logger import getLogger + + +class HacsIntegration(HacsRepository): + """Integrations in HACS.""" + + def __init__(self, full_name): + """Initialize.""" + super().__init__() + self.data.full_name = full_name + self.data.full_name_lower = full_name.lower() + self.data.category = HacsCategory.INTEGRATION + self.content.path.remote = "custom_components" + self.content.path.local = self.localpath + + @property + def localpath(self): + """Return localpath.""" + return f"{self.hacs.core.config_path}/custom_components/{self.data.domain}" + + async def async_post_installation(self): + """Run post installation steps.""" + if self.data.config_flow: + if self.data.full_name != "hacs/integration": + await self.reload_custom_components() + if self.data.first_install: + self.pending_restart = False + return + self.pending_restart = True + + async def validate_repository(self): + """Validate.""" + await self.common_validate() + + # Custom step 1: Validate content. + if self.data.content_in_root: + self.content.path.remote = "" + + if self.content.path.remote == "custom_components": + name = get_first_directory_in_directory(self.tree, "custom_components") + if name is None: + raise HacsException( + f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" + ) + self.content.path.remote = f"custom_components/{name}" + + try: + await get_integration_manifest(self) + except HacsException as exception: + if self.hacs.system.action: + raise HacsException(f"::error:: {exception}") from exception + self.logger.error("%s %s", self, exception) + + # Handle potential errors + if self.validate.errors: + for error in self.validate.errors: + if not self.hacs.status.startup: + self.logger.error("%s %s", self, error) + return self.validate.success + + async def update_repository(self, ignore_issues=False): + """Update.""" + await self.common_update(ignore_issues) + + if self.data.content_in_root: + self.content.path.remote = "" + + if self.content.path.remote == "custom_components": + name = get_first_directory_in_directory(self.tree, "custom_components") + self.content.path.remote = f"custom_components/{name}" + + try: + await get_integration_manifest(self) + except HacsException as exception: + self.logger.error("%s %s", self, exception) + + # Set local path + self.content.path.local = self.localpath + + async def reload_custom_components(self): + """Reload custom_components (and config flows)in HA.""" + self.logger.info("Reloading custom_component cache") + del self.hacs.hass.data["custom_components"] + await async_get_custom_components(self.hacs.hass) + self.logger.info("Custom_component cache reloaded") diff --git a/custom_components/hacs/repositories/netdaemon.py b/custom_components/hacs/repositories/netdaemon.py index 5c3ed1e..770add1 100644 --- a/custom_components/hacs/repositories/netdaemon.py +++ b/custom_components/hacs/repositories/netdaemon.py @@ -1,90 +1,87 @@ -"""Class for netdaemon apps in HACS.""" -from integrationhelper import Logger - -from .repository import HacsRepository -from ..hacsbase.exceptions import HacsException - -from custom_components.hacs.helpers.filters import get_first_directory_in_directory - - -class HacsNetdaemon(HacsRepository): - """Netdaemon apps in HACS.""" - - def __init__(self, full_name): - """Initialize.""" - super().__init__() - self.data.full_name = full_name - self.data.category = "netdaemon" - self.content.path.local = self.localpath - self.content.path.remote = "apps" - self.logger = Logger(f"hacs.repository.{self.data.category}.{full_name}") - - @property - def localpath(self): - """Return localpath.""" - return f"{self.hacs.system.config_path}/netdaemon/apps/{self.data.name}" - - async def validate_repository(self): - """Validate.""" - await self.common_validate() - - # Custom step 1: Validate content. - if self.repository_manifest: - if self.data.content_in_root: - self.content.path.remote = "" - - if self.content.path.remote == "apps": - self.data.domain = get_first_directory_in_directory( - self.tree, self.content.path.remote - ) - self.content.path.remote = f"apps/{self.data.name}" - - compliant = False - for treefile in self.treefiles: - if treefile.startswith(f"{self.content.path.remote}") and treefile.endswith( - ".cs" - ): - compliant = True - break - if not compliant: - raise HacsException( - f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" - ) - - # Handle potential errors - if self.validate.errors: - for error in self.validate.errors: - if not self.hacs.system.status.startup: - self.logger.error(error) - return self.validate.success - - async def registration(self): - """Registration.""" - if not await self.validate_repository(): - return False - - # Run common registration steps. - await self.common_registration() - - # Set local path - self.content.path.local = self.localpath - - async def update_repository(self): - """Update.""" - if self.hacs.github.ratelimits.remaining == 0: - return - await self.common_update() - - # Get appdaemon objects. - if self.repository_manifest: - if self.data.content_in_root: - self.content.path.remote = "" - - if self.content.path.remote == "apps": - self.data.domain = get_first_directory_in_directory( - self.tree, self.content.path.remote - ) - self.content.path.remote = f"apps/{self.data.name}" - - # Set local path - self.content.path.local = self.localpath +"""Class for netdaemon apps in HACS.""" +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.classes.repository import HacsRepository +from custom_components.hacs.enums import HacsCategory +from custom_components.hacs.helpers.functions.filters import ( + get_first_directory_in_directory, +) +from custom_components.hacs.helpers.functions.logger import getLogger + + +class HacsNetdaemon(HacsRepository): + """Netdaemon apps in HACS.""" + + def __init__(self, full_name): + """Initialize.""" + super().__init__() + self.data.full_name = full_name + self.data.full_name_lower = full_name.lower() + self.data.category = HacsCategory.NETDAEMON + self.content.path.local = self.localpath + self.content.path.remote = "apps" + + @property + def localpath(self): + """Return localpath.""" + return f"{self.hacs.core.config_path}/netdaemon/apps/{self.data.name}" + + async def validate_repository(self): + """Validate.""" + await self.common_validate() + + # Custom step 1: Validate content. + if self.repository_manifest: + if self.data.content_in_root: + self.content.path.remote = "" + + if self.content.path.remote == "apps": + self.data.domain = get_first_directory_in_directory( + self.tree, self.content.path.remote + ) + self.content.path.remote = f"apps/{self.data.name}" + + compliant = False + for treefile in self.treefiles: + if treefile.startswith(f"{self.content.path.remote}") and treefile.endswith( + ".cs" + ): + compliant = True + break + if not compliant: + raise HacsException( + f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" + ) + + # Handle potential errors + if self.validate.errors: + for error in self.validate.errors: + if not self.hacs.status.startup: + self.logger.error("%s %s", self, error) + return self.validate.success + + async def update_repository(self, ignore_issues=False): + """Update.""" + await self.common_update(ignore_issues) + + # Get appdaemon objects. + if self.repository_manifest: + if self.data.content_in_root: + self.content.path.remote = "" + + if self.content.path.remote == "apps": + self.data.domain = get_first_directory_in_directory( + self.tree, self.content.path.remote + ) + self.content.path.remote = f"apps/{self.data.name}" + + # Set local path + self.content.path.local = self.localpath + + async def async_post_installation(self): + """Run post installation steps.""" + try: + await self.hacs.hass.services.async_call( + "hassio", "addon_restart", {"addon": "c6a2317c_netdaemon"} + ) + except (Exception, BaseException): # pylint: disable=broad-except + pass diff --git a/custom_components/hacs/repositories/plugin.py b/custom_components/hacs/repositories/plugin.py index 277e604..8b961d6 100644 --- a/custom_components/hacs/repositories/plugin.py +++ b/custom_components/hacs/repositories/plugin.py @@ -1,107 +1,77 @@ -"""Class for plugins in HACS.""" -import json -from integrationhelper import Logger - -from .repository import HacsRepository -from ..hacsbase.exceptions import HacsException - -from custom_components.hacs.helpers.information import find_file_name - - -class HacsPlugin(HacsRepository): - """Plugins in HACS.""" - - def __init__(self, full_name): - """Initialize.""" - super().__init__() - self.data.full_name = full_name - self.data.file_name = None - self.data.category = "plugin" - self.information.javascript_type = None - self.content.path.local = ( - f"{self.hacs.system.config_path}/www/community/{full_name.split('/')[-1]}" - ) - self.logger = Logger(f"hacs.repository.{self.data.category}.{full_name}") - - async def validate_repository(self): - """Validate.""" - # Run common validation steps. - await self.common_validate() - - # Custom step 1: Validate content. - find_file_name(self) - - if self.content.path.remote is None: - raise HacsException( - f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" - ) - - if self.content.path.remote == "release": - self.content.single = True - - # Handle potential errors - if self.validate.errors: - for error in self.validate.errors: - if not self.hacs.system.status.startup: - self.logger.error(error) - return self.validate.success - - async def registration(self): - """Registration.""" - if not await self.validate_repository(): - return False - - # Run common registration steps. - await self.common_registration() - - async def update_repository(self): - """Update.""" - if self.hacs.github.ratelimits.remaining == 0: - return - # Run common update steps. - await self.common_update() - - # Get plugin objects. - find_file_name(self) - - # Get JS type - await self.parse_readme_for_jstype() - - if self.content.path.remote is None: - self.validate.errors.append("Repostitory structure not compliant") - - if self.content.path.remote == "release": - self.content.single = True - - async def get_package_content(self): - """Get package content.""" - try: - package = await self.repository_object.get_contents("package.json") - package = json.loads(package.content) - - if package: - self.data.authors = package["author"] - except Exception: # pylint: disable=broad-except - pass - - async def parse_readme_for_jstype(self): - """Parse the readme looking for js type.""" - readme = None - readme_files = ["readme", "readme.md"] - root = await self.repository_object.get_contents("") - for file in root: - if file.name.lower() in readme_files: - readme = await self.repository_object.get_contents(file.name) - break - - if readme is None: - return - - readme = readme.content - for line in readme.splitlines(): - if "type: module" in line: - self.information.javascript_type = "module" - break - elif "type: js" in line: - self.information.javascript_type = "js" - break +"""Class for plugins in HACS.""" +import json + +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.classes.repository import HacsRepository +from custom_components.hacs.helpers.functions.information import find_file_name +from custom_components.hacs.helpers.functions.logger import getLogger + + +class HacsPlugin(HacsRepository): + """Plugins in HACS.""" + + def __init__(self, full_name): + """Initialize.""" + super().__init__() + self.data.full_name = full_name + self.data.full_name_lower = full_name.lower() + self.data.file_name = None + self.data.category = "plugin" + self.information.javascript_type = None + self.content.path.local = self.localpath + + @property + def localpath(self): + """Return localpath.""" + return f"{self.hacs.core.config_path}/www/community/{self.data.full_name.split('/')[-1]}" + + async def validate_repository(self): + """Validate.""" + # Run common validation steps. + await self.common_validate() + + # Custom step 1: Validate content. + find_file_name(self) + + if self.content.path.remote is None: + raise HacsException( + f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" + ) + + if self.content.path.remote == "release": + self.content.single = True + + # Handle potential errors + if self.validate.errors: + for error in self.validate.errors: + if not self.hacs.status.startup: + self.logger.error("%s %s", self, error) + return self.validate.success + + async def update_repository(self, ignore_issues=False): + """Update.""" + await self.common_update(ignore_issues) + + # Get plugin objects. + find_file_name(self) + + if self.content.path.remote is None: + self.validate.errors.append( + f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" + ) + + if self.content.path.remote == "release": + self.content.single = True + + async def get_package_content(self): + """Get package content.""" + try: + package = await self.repository_object.get_contents( + "package.json", self.ref + ) + package = json.loads(package.content) + + if package: + self.data.authors = package["author"] + except (Exception, BaseException): # pylint: disable=broad-except + pass diff --git a/custom_components/hacs/repositories/python_script.py b/custom_components/hacs/repositories/python_script.py index c9f2db7..e848eb0 100644 --- a/custom_components/hacs/repositories/python_script.py +++ b/custom_components/hacs/repositories/python_script.py @@ -1,87 +1,83 @@ -"""Class for python_scripts in HACS.""" -from integrationhelper import Logger - -from .repository import HacsRepository -from ..hacsbase.exceptions import HacsException -from ..helpers.information import find_file_name - - -class HacsPythonScript(HacsRepository): - """python_scripts in HACS.""" - - category = "python_script" - - def __init__(self, full_name): - """Initialize.""" - super().__init__() - self.data.full_name = full_name - self.data.category = "python_script" - self.content.path.remote = "python_scripts" - self.content.path.local = f"{self.hacs.system.config_path}/python_scripts" - self.content.single = True - self.logger = Logger(f"hacs.repository.{self.data.category}.{full_name}") - - async def validate_repository(self): - """Validate.""" - # Run common validation steps. - await self.common_validate() - - # Custom step 1: Validate content. - if self.data.content_in_root: - self.content.path.remote = "" - - compliant = False - for treefile in self.treefiles: - if treefile.startswith(f"{self.content.path.remote}") and treefile.endswith( - ".py" - ): - compliant = True - break - if not compliant: - raise HacsException( - f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" - ) - - # Handle potential errors - if self.validate.errors: - for error in self.validate.errors: - if not self.hacs.system.status.startup: - self.logger.error(error) - return self.validate.success - - async def registration(self): - """Registration.""" - if not await self.validate_repository(): - return False - - # Run common registration steps. - await self.common_registration() - - # Set name - find_file_name(self) - - async def update_repository(self): # lgtm[py/similar-function] - """Update.""" - if self.hacs.github.ratelimits.remaining == 0: - return - # Run common update steps. - await self.common_update() - - # Get python_script objects. - if self.data.content_in_root: - self.content.path.remote = "" - - compliant = False - for treefile in self.treefiles: - if treefile.startswith(f"{self.content.path.remote}") and treefile.endswith( - ".py" - ): - compliant = True - break - if not compliant: - raise HacsException( - f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" - ) - - # Update name - find_file_name(self) +"""Class for python_scripts in HACS.""" +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.classes.repository import HacsRepository +from custom_components.hacs.enums import HacsCategory +from custom_components.hacs.helpers.functions.information import find_file_name +from custom_components.hacs.helpers.functions.logger import getLogger + + +class HacsPythonScript(HacsRepository): + """python_scripts in HACS.""" + + category = "python_script" + + def __init__(self, full_name): + """Initialize.""" + super().__init__() + self.data.full_name = full_name + self.data.full_name_lower = full_name.lower() + self.data.category = HacsCategory.PYTHON_SCRIPT + self.content.path.remote = "python_scripts" + self.content.path.local = self.localpath + self.content.single = True + + @property + def localpath(self): + """Return localpath.""" + return f"{self.hacs.core.config_path}/python_scripts" + + async def validate_repository(self): + """Validate.""" + # Run common validation steps. + await self.common_validate() + + # Custom step 1: Validate content. + if self.data.content_in_root: + self.content.path.remote = "" + + compliant = False + for treefile in self.treefiles: + if treefile.startswith(f"{self.content.path.remote}") and treefile.endswith( + ".py" + ): + compliant = True + break + if not compliant: + raise HacsException( + f"Repository structure for {self.ref.replace('tags/','')} is not compliant" + ) + + # Handle potential errors + if self.validate.errors: + for error in self.validate.errors: + if not self.hacs.status.startup: + self.logger.error("%s %s", self, error) + return self.validate.success + + async def async_post_registration(self): + """Registration.""" + # Set name + find_file_name(self) + + async def update_repository(self, ignore_issues=False): + """Update.""" + await self.common_update(ignore_issues) + + # Get python_script objects. + if self.data.content_in_root: + self.content.path.remote = "" + + compliant = False + for treefile in self.treefiles: + if treefile.startswith(f"{self.content.path.remote}") and treefile.endswith( + ".py" + ): + compliant = True + break + if not compliant: + raise HacsException( + f"Repository structure for {self.ref.replace('tags/','')} is not compliant" + ) + + # Update name + find_file_name(self) diff --git a/custom_components/hacs/repositories/theme.py b/custom_components/hacs/repositories/theme.py index 6e09657..57094ee 100644 --- a/custom_components/hacs/repositories/theme.py +++ b/custom_components/hacs/repositories/theme.py @@ -1,72 +1,76 @@ -"""Class for themes in HACS.""" -from integrationhelper import Logger -from .repository import HacsRepository -from ..hacsbase.exceptions import HacsException -from ..helpers.information import find_file_name - - -class HacsTheme(HacsRepository): - """Themes in HACS.""" - - def __init__(self, full_name): - """Initialize.""" - super().__init__() - self.data.full_name = full_name - self.data.category = "theme" - self.content.path.remote = "themes" - self.content.path.local = f"{self.hacs.system.config_path}/themes/" - self.content.single = False - self.logger = Logger(f"hacs.repository.{self.data.category}.{full_name}") - - async def validate_repository(self): - """Validate.""" - # Run common validation steps. - await self.common_validate() - - # Custom step 1: Validate content. - compliant = False - for treefile in self.treefiles: - if treefile.startswith("themes/") and treefile.endswith(".yaml"): - compliant = True - break - if not compliant: - raise HacsException( - f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" - ) - - if self.data.content_in_root: - self.content.path.remote = "" - - # Handle potential errors - if self.validate.errors: - for error in self.validate.errors: - if not self.hacs.system.status.startup: - self.logger.error(error) - return self.validate.success - - async def registration(self): - """Registration.""" - if not await self.validate_repository(): - return False - - # Run common registration steps. - await self.common_registration() - - # Set name - find_file_name(self) - self.content.path.local = f"{self.hacs.system.config_path}/themes/{self.data.file_name.replace('.yaml', '')}" - - async def update_repository(self): # lgtm[py/similar-function] - """Update.""" - if self.hacs.github.ratelimits.remaining == 0: - return - # Run common update steps. - await self.common_update() - - # Get theme objects. - if self.data.content_in_root: - self.content.path.remote = "" - - # Update name - find_file_name(self) - self.content.path.local = f"{self.hacs.system.config_path}/themes/{self.data.file_name.replace('.yaml', '')}" +"""Class for themes in HACS.""" +from custom_components.hacs.helpers.classes.exceptions import HacsException +from custom_components.hacs.helpers.classes.repository import HacsRepository +from custom_components.hacs.enums import HacsCategory +from custom_components.hacs.helpers.functions.information import find_file_name +from custom_components.hacs.helpers.functions.logger import getLogger + + +class HacsTheme(HacsRepository): + """Themes in HACS.""" + + def __init__(self, full_name): + """Initialize.""" + super().__init__() + self.data.full_name = full_name + self.data.full_name_lower = full_name.lower() + self.data.category = HacsCategory.THEME + self.content.path.remote = "themes" + self.content.path.local = self.localpath + self.content.single = False + + @property + def localpath(self): + """Return localpath.""" + return f"{self.hacs.core.config_path}/themes/{self.data.file_name.replace('.yaml', '')}" + + async def async_post_installation(self): + """Run post installation steps.""" + try: + await self.hacs.hass.services.async_call("frontend", "reload_themes", {}) + except (Exception, BaseException): # pylint: disable=broad-except + pass + + async def validate_repository(self): + """Validate.""" + # Run common validation steps. + await self.common_validate() + + # Custom step 1: Validate content. + compliant = False + for treefile in self.treefiles: + if treefile.startswith("themes/") and treefile.endswith(".yaml"): + compliant = True + break + if not compliant: + raise HacsException( + f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" + ) + + if self.data.content_in_root: + self.content.path.remote = "" + + # Handle potential errors + if self.validate.errors: + for error in self.validate.errors: + if not self.hacs.status.startup: + self.logger.error("%s %s", self, error) + return self.validate.success + + async def async_post_registration(self): + """Registration.""" + # Set name + find_file_name(self) + self.content.path.local = self.localpath + + async def update_repository(self, ignore_issues=False): + """Update.""" + await self.common_update(ignore_issues) + + # Get theme objects. + if self.data.content_in_root: + self.content.path.remote = "" + + # Update name + find_file_name(self) + self.content.path.local = self.localpath diff --git a/custom_components/hacs/sensor.py b/custom_components/hacs/sensor.py index cda52de..482baa4 100644 --- a/custom_components/hacs/sensor.py +++ b/custom_components/hacs/sensor.py @@ -1,96 +1,122 @@ -"""Sensor platform for HACS.""" -# pylint: disable=unused-argument -from homeassistant.helpers.entity import Entity -from .hacsbase import Hacs as hacs -from .const import DOMAIN, VERSION, NAME_SHORT - - -async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): - """Setup sensor platform.""" - async_add_entities([HACSSensor()]) - - -async def async_setup_entry(hass, config_entry, async_add_devices): - """Setup sensor platform.""" - async_add_devices([HACSSensor()]) - - -class HACSDevice(Entity): - """HACS Device class.""" - - @property - def device_info(self): - """Return device information about HACS.""" - return { - "identifiers": {(DOMAIN, self.unique_id)}, - "name": NAME_SHORT, - "manufacturer": "hacs.xyz", - "model": "", - "sw_version": VERSION, - } - - -class HACSSensor(HACSDevice): - """HACS Sensor class.""" - - def __init__(self): - """Initialize.""" - self._state = None - self.repositories = [] - - async def async_update(self): - """Update the sensor.""" - if hacs.system.status.background_task: - return - - self.repositories = [] - - for repository in hacs.repositories: - if ( - repository.pending_upgrade - and repository.data.category in hacs.common.categories - ): - self.repositories.append(repository) - self._state = len(self.repositories) - - @property - def unique_id(self): - """Return a unique ID to use for this sensor.""" - return ( - "0717a0cd-745c-48fd-9b16-c8534c9704f9-bc944b0f-fd42-4a58-a072-ade38d1444cd" - ) - - @property - def name(self): - """Return the name of the sensor.""" - return "hacs" - - @property - def state(self): - """Return the state of the sensor.""" - return self._state - - @property - def icon(self): - """Return the icon of the sensor.""" - return "hacs:hacs" - - @property - def unit_of_measurement(self): - """Return the unit of measurement.""" - return "pending update(s)" - - @property - def device_state_attributes(self): - """Return attributes for the sensor.""" - data = [] - for repository in self.repositories: - data.append( - { - "name": repository.data.full_name, - "display_name": repository.display_name, - "installed version": repository.display_installed_version, - "available version": repository.display_available_version, - } - ) - return {"repositories": data} +"""Sensor platform for HACS.""" +from homeassistant.helpers.entity import Entity +from custom_components.hacs.const import DOMAIN, NAME_SHORT, VERSION +from custom_components.hacs.share import get_hacs +from homeassistant.core import callback + + +async def async_setup_platform( + _hass, _config, async_add_entities, _discovery_info=None +): + """Setup sensor platform.""" + async_add_entities([HACSSensor()]) + + +async def async_setup_entry(_hass, _config_entry, async_add_devices): + """Setup sensor platform.""" + async_add_devices([HACSSensor()]) + + +class HACSDevice(Entity): + """HACS Device class.""" + + @property + def device_info(self): + """Return device information about HACS.""" + return { + "identifiers": {(DOMAIN, self.unique_id)}, + "name": NAME_SHORT, + "manufacturer": "hacs.xyz", + "model": "", + "sw_version": VERSION, + "entry_type": "service", + } + + +class HACSSensor(HACSDevice): + """HACS Sensor class.""" + + def __init__(self): + """Initialize.""" + self._state = None + self.repositories = [] + + @property + def should_poll(self): + """No polling needed.""" + return False + + async def async_update(self): + """Manual updates of the sensor.""" + self._update() + + @callback + def _update_and_write_state(self, *_): + """Update the sensor and write state.""" + self._update() + self.async_write_ha_state() + + @callback + def _update(self): + """Update the sensor.""" + hacs = get_hacs() + if hacs.status.background_task: + return + + self.repositories = [] + + for repository in hacs.repositories: + if ( + repository.pending_upgrade + and repository.data.category in hacs.common.categories + ): + self.repositories.append(repository) + self._state = len(self.repositories) + + @property + def unique_id(self): + """Return a unique ID to use for this sensor.""" + return ( + "0717a0cd-745c-48fd-9b16-c8534c9704f9-bc944b0f-fd42-4a58-a072-ade38d1444cd" + ) + + @property + def name(self): + """Return the name of the sensor.""" + return "hacs" + + @property + def state(self): + """Return the state of the sensor.""" + return self._state + + @property + def icon(self): + """Return the icon of the sensor.""" + return "hacs:hacs" + + @property + def unit_of_measurement(self): + """Return the unit of measurement.""" + return "pending update(s)" + + @property + def device_state_attributes(self): + """Return attributes for the sensor.""" + repositories = [] + for repository in self.repositories: + repositories.append( + { + "name": repository.data.full_name, + "display_name": repository.display_name, + "installed_version": repository.display_installed_version, + "available_version": repository.display_available_version, + } + ) + return {"repositories": repositories} + + async def async_added_to_hass(self) -> None: + """Register for status events.""" + self.async_on_remove( + self.hass.bus.async_listen("hacs/status", self._update_and_write_state) + ) diff --git a/custom_components/hacs/services.yaml b/custom_components/hacs/services.yaml deleted file mode 100644 index 982a956..0000000 --- a/custom_components/hacs/services.yaml +++ /dev/null @@ -1,15 +0,0 @@ -install: - description: This is NOT intended to be used here, this is intended for developers! - fields: - repository: - description: The repository ID - example: '"123456789"' -register: - description: This is NOT intended to be used here, this is intended for developers! - fields: - repository: - description: The full name of the repository - example: 'developer/repo' - repository_type: - description: The repository type - example: 'plugin' \ No newline at end of file diff --git a/custom_components/hacs/setup.py b/custom_components/hacs/setup.py deleted file mode 100644 index af7f9a0..0000000 --- a/custom_components/hacs/setup.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Setup functions for HACS.""" -# pylint: disable=bad-continuation -from hacs_frontend.version import VERSION as FE_VERSION -from homeassistant.helpers import discovery - -from custom_components.hacs.hacsbase.exceptions import HacsException -from custom_components.hacs.const import VERSION, DOMAIN -from custom_components.hacs.globals import get_hacs -from custom_components.hacs.helpers.information import get_repository -from custom_components.hacs.helpers.register_repository import register_repository - - -async def load_hacs_repository(): - """Load HACS repositroy.""" - hacs = get_hacs() - - try: - repository = hacs.get_by_name("hacs/integration") - if repository is None: - await register_repository("hacs/integration", "integration") - repository = hacs.get_by_name("hacs/integration") - if repository is None: - raise HacsException("Unknown error") - repository.status.installed = True - repository.versions.installed = VERSION - repository.status.new = False - hacs.repo = repository.repository_object - hacs.data_repo = await get_repository( - hacs.session, hacs.configuration.token, "hacs/default" - ) - except HacsException as exception: - if "403" in f"{exception}": - hacs.logger.critical("GitHub API is ratelimited, or the token is wrong.") - else: - hacs.logger.critical(f"[{exception}] - Could not load HACS!") - return False - return True - - -def setup_extra_stores(): - """Set up extra stores in HACS if enabled in Home Assistant.""" - hacs = get_hacs() - if "python_script" in hacs.hass.config.components: - if "python_script" not in hacs.common.categories: - hacs.common.categories.append("python_script") - - if hacs.hass.services.services.get("frontend", {}).get("reload_themes") is not None: - if "theme" not in hacs.common.categories: - hacs.common.categories.append("theme") - - -def add_sensor(): - """Add sensor.""" - hacs = get_hacs() - - try: - if hacs.configuration.config_type == "yaml": - hacs.hass.async_create_task( - discovery.async_load_platform( - hacs.hass, "sensor", DOMAIN, {}, hacs.configuration.config - ) - ) - else: - hacs.hass.async_add_job( - hacs.hass.config_entries.async_forward_entry_setup( - hacs.configuration.config_entry, "sensor" - ) - ) - except ValueError: - pass - - -async def setup_frontend(): - """Configure the HACS frontend elements.""" - from .http import HacsFrontend, HacsPluginViewLegacy - from .ws_api_handlers import setup_ws_api - - hacs = get_hacs() - - hacs.hass.http.register_view(HacsFrontend()) - hacs.frontend.version_running = FE_VERSION - - # Legacy views, remove with 2.0 - hacs.hass.http.register_view(HacsPluginViewLegacy()) - - # Add to sidepanel - custom_panel_config = { - "name": "hacs-frontend", - "embed_iframe": False, - "trust_external": False, - "js_url": f"/hacsfiles/frontend-{hacs.frontend.version_running}.js", - } - - config = {} - config["_panel_custom"] = custom_panel_config - - hacs.hass.components.frontend.async_register_built_in_panel( - component_name="custom", - sidebar_title=hacs.configuration.sidepanel_title, - sidebar_icon=hacs.configuration.sidepanel_icon, - frontend_url_path="hacs", - config=config, - require_admin=True, - ) - - if "frontend_extra_module_url" not in hacs.hass.data: - hacs.hass.data["frontend_extra_module_url"] = set() - hacs.hass.data["frontend_extra_module_url"].add("/hacsfiles/iconset.js") - - await setup_ws_api(hacs.hass) diff --git a/custom_components/hacs/share.py b/custom_components/hacs/share.py new file mode 100644 index 0000000..a3665c2 --- /dev/null +++ b/custom_components/hacs/share.py @@ -0,0 +1,68 @@ +"""Shared HACS elements.""" +import os + +from .base import HacsBase + +SHARE = { + "hacs": None, + "factory": None, + "queue": None, + "removed_repositories": [], + "rules": {}, +} + + +def get_hacs() -> HacsBase: + if SHARE["hacs"] is None: + from custom_components.hacs.hacsbase.hacs import Hacs as Legacy + + _hacs = Legacy() + + if not "PYTEST" in os.environ and "GITHUB_ACTION" in os.environ: + _hacs.system.action = True + + SHARE["hacs"] = _hacs + + return SHARE["hacs"] + + +def get_factory(): + if SHARE["factory"] is None: + from custom_components.hacs.operational.factory import HacsTaskFactory + + SHARE["factory"] = HacsTaskFactory() + + return SHARE["factory"] + + +def get_queue(): + if SHARE["queue"] is None: + from queueman import QueueManager + + SHARE["queue"] = QueueManager() + + return SHARE["queue"] + + +def is_removed(repository): + return repository in [x.repository for x in SHARE["removed_repositories"]] + + +def get_removed(repository): + if not is_removed(repository): + from custom_components.hacs.helpers.classes.removed import RemovedRepository + + removed_repo = RemovedRepository() + removed_repo.repository = repository + SHARE["removed_repositories"].append(removed_repo) + filter_repos = [ + x + for x in SHARE["removed_repositories"] + if x.repository.lower() == repository.lower() + ] + + return filter_repos.pop() or None + + +def list_removed_repositories(): + return SHARE["removed_repositories"] diff --git a/custom_components/hacs/store.py b/custom_components/hacs/store.py deleted file mode 100644 index 848d4af..0000000 --- a/custom_components/hacs/store.py +++ /dev/null @@ -1,19 +0,0 @@ -"""Storage handers.""" -from homeassistant.helpers.json import JSONEncoder -from homeassistant.helpers.storage import Store -from .hacsbase.const import STORAGE_VERSION - - -async def async_load_from_store(hass, key): - """Load the retained data from store and return de-serialized data.""" - store = Store(hass, STORAGE_VERSION, f"hacs.{key}", encoder=JSONEncoder) - restored = await store.async_load() - if restored is None: - return {} - return restored - - -async def async_save_to_store(hass, key, data): - """Generate dynamic data to store and save it to the filesystem.""" - store = Store(hass, STORAGE_VERSION, f"hacs.{key}", encoder=JSONEncoder) - await store.async_save(data) diff --git a/custom_components/hacs/translations/cs.json b/custom_components/hacs/translations/cs.json new file mode 100644 index 0000000..a7f4bb2 --- /dev/null +++ b/custom_components/hacs/translations/cs.json @@ -0,0 +1,358 @@ +{ + "common": { + "about": "O", + "add": "přidat", + "appdaemon_apps": "Aplikace AppDaemon", + "appdaemon_plural": "Aplikace AppDaemon", + "background_task": "Probíhá úloha na pozadí, tato stránka se znovu načte, až bude hotová.", + "cancel": "Zrušit", + "check_log_file": "Zkontrolujte protokol pro další podrobnosti", + "continue": "Pokračovat", + "disabled": "Zakázáno", + "documentation": "Dokumentace", + "element": "prvek", + "hacs_is_disabled": "HACS je zakázán", + "ignore": "Ignorovat", + "install": "Nainstalovat", + "installed": "nainstalováno", + "integration": "Integrace", + "integration_plural": "Integrace", + "integrations": "Integrace", + "lovelace": "Lovelace", + "lovelace_element": "Prvek Lovelace", + "lovelace_elements": "Prvky Lovelace", + "manage": "spravovat", + "netdaemon": "NetDaemon", + "netdaemon_apps": "Aplikace NetDaemon", + "netdaemon_plural": "Aplikace NetDaemon", + "plugin": "Lovelace", + "plugin_plural": "Prvky Lovelace", + "plugins": "Prvky Lovelace", + "python_script": "Skript v Pythonu", + "python_script_plural": "Skripty v Pythonu", + "python_scripts": "Skripty v Pythonu", + "reload": "Znovu načíst", + "repositories": "Repozitáře", + "repository": "Repozitář", + "settings": "nastavení", + "theme": "Motiv", + "theme_plural": "Motivy", + "themes": "Motivy", + "uninstall": "Odinstalovat", + "update": "Aktualizovat", + "version": "Verze" + }, + "config": { + "abort": { + "single_instance_allowed": "Je povolena pouze jediná konfigurace HACS." + }, + "error": { + "acc": "Než budete pokračovat, musíte potvrdit všechna prohlášení", + "auth": "Osobní přístupový token není správný." + } + }, + "confirm": { + "add_to_lovelace": "Opravdu to chcete přidat ke svým zdrojům Lovelace?", + "bg_task": "Akce je zakázána, když jsou spuštěny úlohy na pozadí.", + "cancel": "Zrušit", + "continue": "Jste si jistý, že chcete pokračovat?", + "delete": "Opravdu chcete smazat \"{item}\"?", + "delete_installed": "\"{item}\" je nainstalována, je třeba ji před smazáním odinstalovat.", + "exist": "{item} již existuje", + "generic": "Jste si jist?", + "home_assistant_is_restarting": "Počkejte, Home Assistant se nyní restartuje.", + "home_assistant_version_not_correct": "Používáte Home Assistant ve verzi \"{haversion}\", ale tento repozitář vyžaduje instalaci minimálně verzi \"{minversion}\".", + "no": "Ne", + "no_upgrades": "Žádné aktualizace čekající na vyřízení", + "ok": "OK", + "overwrite": "Pokud toto uděláte, tak to přepíšete.", + "reload_data": "Tím se znovu načtou data všech repozitářů, o kterých HACS ví. Bude to nějakou dobu trvat.", + "restart_home_assistant": "Opravdu chcete restartovat Home Assistant?", + "uninstall": "Opravdu chcete odinstalovat \"{item}\"?", + "upgrade_all": "Tím se aktualizují všechny tyto repozitáře. Před pokračováním se ujistěte, že jste si přečetli poznámky k vydání všech z nich.", + "yes": "Ano" + }, + "dialog_about": { + "frontend_version": "Verze rozhraní", + "installed_repositories": "Nainstalované repozitáře", + "integration_version": "Verze integrace", + "useful_links": "Užitečné odkazy" + }, + "dialog_add_repo": { + "limit": "Zobrazeno je pouze prvních 100 repozitářů, pomocí vyhledávání můžete filtrovat, co potřebujete", + "no_match": "Vašemu filtru neodpovídají žádné repozitáře", + "sort_by": "Řadit dle", + "title": "Přidat repozitář" + }, + "dialog_custom_repositories": { + "category": "Kategorie", + "no_category": "Chybí kategorie", + "no_repository": "Chybí repozitář", + "title": "Vlastní repozitáře", + "url_placeholder": "Přidat URL adresu vlastního repozitáře" + }, + "dialog_info": { + "author": "Autor", + "downloads": "Staženo", + "install": "Instalovat tento repozitář v HACS", + "loading": "Načítání informací...", + "no_info": "Vývojář neposkytl pro tento repozitář žádné další informace", + "open_issues": "Nahlásit problémy", + "open_repo": "Otevřít repozitář", + "stars": "Hvězdičky", + "version_installed": "Nainstalovaná verze" + }, + "dialog_install": { + "restart": "Nezapomeňte, že změny integrace (custom_components) se projeví až po restartu Home Assistant.", + "select_version": "Vyberte verzi", + "show_beta": "Zobrazit beta verze", + "type": "Typ", + "url": "URL adresa" + }, + "dialog_removed": { + "link": "Externí odkaz pro další informace", + "name": "Název repozitáře", + "reason": "Důvod odstranění", + "type": "Typ odstranění" + }, + "dialog_update": { + "available_version": "Dostupná verze", + "changelog": "Seznam změn", + "installed_version": "Nainstalovaná verze", + "releasenotes": "Poznámky k vydáno pro {release}", + "title": "Aktualizace čeká na vyřízení" + }, + "dialog": { + "reload": { + "confirm": "Chcete to udělat hned teď?", + "description": "Při změně zdrojů Lovelace musíte vymazat mezipaměť prohlížeče." + } + }, + "entry": { + "information": "Informace", + "intro": "Zde budou zobrazeny aktualizace a důležité zprávy", + "messages": { + "disabled": { + "content": "Zkontrolujte protokol pro další podrobnosti", + "title": "HACS je deaktivován" + }, + "has_pending_tasks": { + "content": "Některé repozitáře se nemusí zobrazit, dokud toto není dokončeno.", + "title": "Úkoly na pozadí čekají na vyřízení" + }, + "removed": "Odebrán repozitář \"{repository}\"", + "resources": { + "content": "Máte {number} prvků Lovelace, které nejsou v Lovelace správně načteny.", + "title": "Nenačteno v Lovelace" + }, + "restart": { + "content": "Máte {number} integrací, které vyžadují restart Home Assistant, můžete to udělat v sekci \"Ovládání serveru\" v nastavení Home Assistant.", + "title": "Čeká se na restart" + }, + "setup": { + "content": "HACS se nastavuje, během této doby mohou některé informace chybět nebo být nesprávné", + "title": "HACS se nastavuje" + }, + "startup": { + "content": "HACS se spouští, během této doby mohou některé informace chybět nebo být nesprávné", + "title": "HACS se spouští" + }, + "waiting": { + "content": "HACS čeká na dokončení spuštění Home Assistant, než bude moci spustit své úlohy", + "title": "HACS čeká" + }, + "wrong_frontend_installed": { + "content": "Používáte verzi {running} rozhraní HACS, ale je očekávána verze {expected}. Pokud vidíte tuto zprávu, Home Assistant nebyl schopen nainstalovat novou verzi. Zkuste restartovat Home Assistant.", + "title": "Neočekávaná verze rozhraní" + }, + "wrong_frontend_loaded": { + "content": "Používáte verzi {running} rozhraní HACS, ale je očekávána verze {expected}, měli byste vymazat mezipaměť prohlížeče.", + "title": "Neočekávaná verze rozhraní" + } + }, + "pending_updates": "Čekající aktualizace" + }, + "menu": { + "about": "O HACS", + "clear": "Vymazat vše nové", + "custom_repositories": "Vlastní repozitáře", + "dismiss": "Odmítnout všechny nové repozitáře", + "documentation": "Dokumentace", + "open_issue": "Nahlásit problém", + "reload": "Znovu načíst okno" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Povolit zjišťování a sledování aplikací AppDaemon", + "country": "Filtrovat pomocí kódu země.", + "debug": "Povolit ladění.", + "experimental": "Povolit experimentální funkce", + "netdaemon": "Povolit zjišťování a sledování aplikací NetDaemon", + "not_in_use": "Nepoužívá se s YAML", + "release_limit": "Počet vydání, která se mají zobrazit.", + "sidepanel_icon": "Ikona postranního panelu", + "sidepanel_title": "Název postranního panelu" + } + } + } + }, + "repository_banner": { + "config_flow": "Tato integrace podporuje nastavení v uživatelském rozhraní, což znamená, že nyní můžete přejít do sekce Integrace ve vašem uživatelském rozhraní a nastavit ji.", + "config_flow_title": "Podporováno nastavení z uživatelského rozhraní", + "integration_not_loaded": "Tato integrace není načtena v Home Assistant.", + "no_restart_required": "Není vyžadován restart", + "not_loaded": "Nenačteno", + "plugin_not_loaded": "Tento prvek není přidán do vašich zdrojů Lovelace.", + "restart": "Musíte restartovat Home Assistant.", + "restart_pending": "Restart čeká na vyřízení" + }, + "repository_card": { + "dismiss": "zamítnout", + "hide": "Skrýt", + "information": "Informace", + "new_repository": "Nový repozitář", + "not_loaded": "Nenačteno", + "open_issue": "Nahlásit problém", + "open_source": "Otevřít zdrojový kód", + "pending_restart": "Čeká se na restart", + "pending_update": "Čeká na aktualizaci", + "reinstall": "Přeinstalovat", + "report": "Zpráva o odstranění", + "update_information": "Informace o aktualizaci" + }, + "repository": { + "add_to_lovelace": "Přidat do Lovelace", + "authors": "Autoři", + "available": "K dispozici", + "back_to": "Zpět k", + "changelog": "Seznam změn", + "downloads": "Staženo", + "flag_this": "Označit", + "frontend_version": "Verze rozhraní", + "github_stars": "Hvězdičky na GitHubu", + "goto_integrations": "Přejít na integrace", + "hide": "Skrýt", + "hide_beta": "Skrýt beta verze", + "install": "Nainstalovat", + "installed": "Nainstalováno", + "lovelace_copy_example": "Zkopírovat příklad do schránky", + "lovelace_instruction": "Při přidávávání do vaší Lovelace konfigurace použijte toto", + "lovelace_no_js_type": "Nelze určit typ tohoto prvku, zkontrolujte repozitář.", + "newest": "nejnovější", + "note_appdaemon": "stále je třeba to přidat do souboru \"apps.yaml\"", + "note_installed": "Po instalaci bude umístěno v", + "note_integration": "stále je třeba to přidat do souboru \"configuration.yaml\"", + "note_plugin": "stále je třeba to přidat do konfigurace Lovelace (\"ui-lovelace.yaml\" nebo v editoru kódu konfigurace uživatelského rozhraní)", + "note_plugin_post_107": "stále je třeba to přidat do konfigurace Lovelace (\"configuration.yaml\" nebo v editoru \"\/config\/lovelace\/resources\")", + "open_issue": "Nahlásit problém", + "open_plugin": "Otevřít prvek", + "reinstall": "Přeinstalovat", + "repository": "Repozitář", + "restart_home_assistant": "Restartovat Home Assistant", + "show_beta": "Zobrazit beta verze", + "uninstall": "Odinstalovat", + "update_information": "Informace o aktualizaci", + "upgrade": "Aktualizovat" + }, + "search": { + "installed": "Hledejte nainstalované repozitáře", + "installed_new": "Hledejte nainstalované nebo nové repozitáře", + "placeholder": "Hledat repozitář" + }, + "sections": { + "about": { + "description": "Zobrazit informace o HACS", + "title": "O HACS" + }, + "addon": { + "description": "V HACS nejsou žádné doplňky, ale kliknutím sem se dostanete k Supervisoru", + "title": "Doplňky" + }, + "automation": { + "description": "Zde najdete skripty v Pythonu, aplikace AppDaemon a aplikace NetDaemon", + "title": "Automatizace" + }, + "frontend": { + "description": "Zde najdete motivy, vlastní karty a další prvky pro Lovelace", + "title": "Rozhraní" + }, + "integrations": { + "description": "Zde najdete vlastní integrace (custom_components)", + "title": "Integrace" + }, + "pending_repository_upgrade": "Používáte verzi {installed}, verze {available} je k dispozici" + }, + "settings": { + "add_custom_repository": "PŘIDAT VLASTNÍ REPOZITÁŘ", + "adding_new_repo": "Přidání nového repozitáře \"{repo}\"", + "adding_new_repo_category": "S kategorií \"{category}\".", + "bg_task_custom": "Vlastní repozitáře jsou skryty, když jsou spuštěný úlohy na pozadí.", + "category": "Kategorie", + "compact_mode": "Kompaktní režim", + "custom_repositories": "VLASTNÍ REPOZITÁŘE", + "delete": "Smazat", + "display": "Zobrazení", + "grid": "Mřížka", + "hacs_repo": "Repozitář HACS", + "hidden_repositories": "skryté repozitáře", + "missing_category": "Je třeba vybrat kategorii", + "open_repository": "Otevřít repozitář", + "reload_data": "Znovu načíst data", + "reload_window": "Znovu načíst okno", + "repository_configuration": "Nastavení repozitáře", + "save": "Uložit", + "table": "Tabulka", + "table_view": "V tabulce", + "unhide": "odkrýt", + "upgrade_all": "Aktualizovat vše" + }, + "store": { + "ascending": "vzestupně", + "clear_new": "Vymazat všechny nové repozitáře", + "descending": "sestupně", + "last_updated": "Naposledy aktualizováno", + "name": "Název", + "new_repositories": "Nové repozitáře", + "new_repositories_note": "Zobrazuje se zde více než 10 nových repozitářů. Chcete-li je všechny vymazat, klikněte na 3 tečky v pravém horním rohu a všechny odmítněte.", + "no_repositories": "Žádné repozitáře", + "no_repositories_desc1": "Vypadá to, že v této sekci ještě nemáte nainstalované žádné repozitáře.", + "no_repositories_desc2": "Kliknutím na + ve spodním rohu přidáte svůj první!", + "no_repositories_found_desc1": "V této části nebyly nalezeny žádné nainstalované repozitáře odpovídající \"{searchInput}\".", + "no_repositories_found_desc2": "Zkuste hledat něco jiného!", + "pending_upgrades": "Čekající aktualizace", + "placeholder_search": "Zadejte výraz k hledání...", + "sort": "řadit", + "stars": "Hvězdičky", + "status": "Stav" + }, + "time": { + "ago": "před", + "day": "den", + "days": "dní", + "hour": "hodina", + "hours": "hodin", + "minute": "minuta", + "minutes": "minut", + "month": "měsíc", + "months": "měsíce", + "one": "Jeden", + "one_day_ago": "před jedním dnem", + "one_hour_ago": "před hodinou", + "one_minute_ago": "před minutou", + "one_month_ago": "před měsícem", + "one_second_ago": "před sekundou", + "one_year_ago": "před rokem", + "second": "sekunda", + "seconds": "sekundy", + "x_days_ago": "před {x} dny", + "x_hours_ago": "před {x} hodinami", + "x_minutes_ago": "před {x} minutami", + "x_months_ago": "před {x} měsíci", + "x_seconds_ago": "před {x} sekundami", + "x_years_ago": "před {x} lety", + "year": "rok", + "years": "let" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/da.json b/custom_components/hacs/translations/da.json new file mode 100644 index 0000000..2ba2c4d --- /dev/null +++ b/custom_components/hacs/translations/da.json @@ -0,0 +1,345 @@ +{ + "common": { + "about": "Om", + "add": "tilføj", + "appdaemon_apps": "AppDaemon-apps", + "appdaemon_plural": "AppDaemon-apps", + "background_task": "Baggrundsopgave kører. Denne side vil genindlæses automatisk.", + "check_log_file": "Tjek din logfil for flere detaljer.", + "continue": "Fortsæt", + "disabled": "Deaktiveret", + "documentation": "Dokumentation", + "element": "element", + "hacs_is_disabled": "HACS er deaktiveret", + "install": "Installer", + "installed": "installeret", + "integration": "Integration", + "integration_plural": "Integrationer", + "integrations": "Integrationer", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace-element", + "lovelace_elements": "Lovelace-elementer", + "manage": "administrer", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon-apps", + "netdaemon_plural": "NetDaemon-apps", + "plugin": "Lovelace", + "plugin_plural": "Lovelace-elementer", + "plugins": "Lovelace-elementer", + "python_script": "Python-script", + "python_script_plural": "Python-scripts", + "python_scripts": "Python-scripts", + "repositories": "Repositories", + "repository": "Repository", + "settings": "indstillinger", + "theme": "Tema", + "theme_plural": "Temaer", + "themes": "Temaer", + "uninstall": "Afinstaller", + "update": "Opdater", + "version": "Version" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + } + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Er du sikker på, at du vil tilføje dette til dine Lovelace-ressourcer?", + "bg_task": "Handlingen er deaktiveret, mens baggrundsopgaver kører.", + "cancel": "Annuller", + "continue": "Er du sikker på, at du vil fortsætte?", + "delete": "Er du sikker på, at du vil slette '{Item}'?", + "delete_installed": "'{item}' er installeret, du skal afinstallere det, før du kan slette det.", + "exist": "{item} findes allerede", + "generic": "Er du sikker?", + "home_assistant_is_restarting": "Vent venligst - Home Assistant genstarter nu.", + "home_assistant_version_not_correct": "Du kører Home Assistant version '{haversion}', men dette repository kræver som minimum version '{minversion}'.", + "no": "Nej", + "no_upgrades": "Der er ingen opdateringer tilgængelig", + "ok": "OK", + "overwrite": "Dette vil overskrive den.", + "reload_data": "Dette genindlæser data fra alle repositories, som HACS kender til. Dette vil tage nogen tid at fuldføre.", + "restart_home_assistant": "Er du sikker på, at du vil genstarte Home Assistant?", + "uninstall": "Er du sikker på, at du vil afinstallere '{Item}'?", + "upgrade_all": "Dette vil opdatere alle repositories. Sørg for at du har læst udgivelsesnoterne for dem alle, inden du fortsætter.", + "yes": "Ja" + }, + "dialog_about": { + "frontend_version": "Frontend-version", + "installed_repositories": "Installerede repositories", + "integration_version": "Integrationsversion", + "useful_links": "Nyttige links" + }, + "dialog_add_repo": { + "limit": "Kun de første 100 repositories vises. Brug søgningen til at filtrere, hvad du har brug for", + "no_match": "Der blev ikke fundet nogen repositories, der matcher dit filter", + "sort_by": "Sorter efter", + "title": "Tilføj repository" + }, + "dialog_custom_repositories": { + "category": "Kategori", + "no_category": "Manglende kategori", + "no_repository": "Manglende repository", + "title": "Brugerdefinerede repositories", + "url_placeholder": "Tilføj brugerdefineret repository-webadresse" + }, + "dialog_info": { + "author": "Udvikler", + "downloads": "Downloads", + "install": "Installer dette repository i HACS", + "loading": "Indlæser oplysninger...", + "no_info": "Udvikleren har ikke givet flere oplysninger om dette repository", + "open_issues": "Åbn issues", + "open_repo": "Åbn repository", + "stars": "Stjerner", + "version_installed": "Installeret version" + }, + "dialog_install": { + "restart": "Husk, at du skal genstarte Home Assistant, før ændringer af integrationer (custom_components) træder i kræft.", + "select_version": "Vælg version", + "show_beta": "Vis betaversioner", + "type": "Type", + "url": "Webadresse" + }, + "dialog_update": { + "available_version": "Tilgængelig version", + "changelog": "Udgivelsesnoter", + "installed_version": "Installeret version", + "releasenotes": "Udgivelsesnoter for {release}", + "title": "Ventende opdatering" + }, + "entry": { + "information": "Oplysninger", + "intro": "Opdateringer og vigtige meddelelser vises her, hvis der er nogen", + "messages": { + "disabled": { + "content": "Tjek din logfil for flere detaljer", + "title": "HACS er deaktiveret" + }, + "has_pending_tasks": { + "content": "Nogle repositories vises muligvis ikke, før dette er fuldført", + "title": "Baggrundsopgaver venter" + }, + "resources": { + "content": "Du har {number} Lovelace-elementer, der ikke er indlæst korrekt i Lovelace.", + "title": "Ikke indlæst i Lovelace" + }, + "restart": { + "content": "Du har {number} integrationer, der kræver en genstart af Home Assistant. Du kan genstarte fra 'Serveradministration'-sektionen under Indstillinger i Home Assistant-brugerfladen.", + "title": "Afventer genstart" + }, + "startup": { + "content": "HACS starter op. Der kan i dette tidsrum mangle nogle oplysninger, eller de kan være ukorekte.", + "title": "HACS starter op" + }, + "wrong_frontend_installed": { + "content": "Du har version {running} af HACS-frontend installeret, men version {expected} var forventet, hvis dette ser du denne besked. Home Assistant kunne ikke installere den nye version. Prøv at genstarte Home Assistant.", + "title": "Uventet frontend-version" + }, + "wrong_frontend_loaded": { + "content": "Du kører version {running} af HACS-frontend, men version {expected} var forventet. Du bør rydde din browser-cache.", + "title": "Uventet frontend-version" + } + }, + "pending_updates": "Ventende opdateringer" + }, + "menu": { + "about": "Om HACS", + "clear": "Ryd alle nye", + "custom_repositories": "Brugerdefinerede repositories", + "dismiss": "Afvis alle nye repositories", + "documentation": "Dokumentation", + "open_issue": "Opret issue", + "reload": "Genindlæs vindue" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Aktiver opdagelse og sporing af AppDaemon-apps", + "country": "Filtrer med landekode.", + "debug": "Aktiver debug.", + "experimental": "Aktivér eksperimentelle funktioner", + "netdaemon": "Aktiver opdagelse og sporing af NetDaemon-apps", + "not_in_use": "Ikke i brug med YAML", + "release_limit": "Antal udgivelser, der skal vises.", + "sidepanel_icon": "Sidepanelikon", + "sidepanel_title": "Sidepanelets titel" + } + } + } + }, + "repository_banner": { + "config_flow": "Denne integration understøtter config_flow. Det betyder, at du nu kan gå til integrationssektionen i din brugerflade for at konfigurere den.", + "config_flow_title": "Brugerfladekonfiguration understøttet", + "integration_not_loaded": "Denne integration er ikke indlæst i Home Assistant.", + "no_restart_required": "Ingen genstart påkrævet", + "not_loaded": "Ikke indlæst", + "plugin_not_loaded": "Dette element er ikke føjet til dine Lovelace-ressourcer.", + "restart": "Du skal genstarte Home Assistant.", + "restart_pending": "Afventer genstart" + }, + "repository_card": { + "dismiss": "Afvis", + "hide": "Skjul", + "information": "Oplysninger", + "new_repository": "Nyt repository", + "not_loaded": "Ikke indlæst", + "open_issue": "Opret issue", + "open_source": "Åbn kilde", + "pending_restart": "Afventer genstart", + "pending_update": "Ventende opdatering", + "reinstall": "Geninstaller", + "report": "Rapporter til fjernelse", + "update_information": "Opdater oplysninger" + }, + "repository": { + "add_to_lovelace": "Tilføj til Lovelace", + "authors": "Forfattere", + "available": "Tilgængelig", + "back_to": "Tilbage til", + "changelog": "Udgivelsesnoter", + "downloads": "Downloads", + "flag_this": "Marker denne", + "frontend_version": "Frontend-version", + "github_stars": "GitHub-stjerner", + "goto_integrations": "Gå til integrationer", + "hide": "Skjul", + "hide_beta": "Skjul beta", + "install": "Installer", + "installed": "Installeret", + "lovelace_copy_example": "Kopiér eksemplet til din Udklipsholder", + "lovelace_instruction": "Tilføj dette til din lovelace-konfiguration", + "lovelace_no_js_type": "Kunne ikke afgøre typen af dette element, tjek venligst repository'et.", + "newest": "nyeste", + "note_appdaemon": "Du skal stadig føje den til filen 'apps.yaml'", + "note_installed": "Når det er installeret, vil dette være placeret i", + "note_integration": "du skal stadig føje den til filen 'configuration.yaml'", + "note_plugin": "du skal stadig tilføje det til din lovelace-konfiguration ('ui-lovelace.yaml' eller Tekstbaseret redigering)", + "note_plugin_post_107": "du skal stadig tilføje det til din lovelace-konfiguration ('configuration.yaml' eller ressourceeditoren '\/config\/lovelace\/resources')", + "open_issue": "Opret issue", + "open_plugin": "Åbn element", + "reinstall": "Geninstaller", + "repository": "Repository", + "restart_home_assistant": "Genstart Home Assistant", + "show_beta": "Vis beta", + "uninstall": "Afinstaller", + "update_information": "Opdater oplysninger", + "upgrade": "Opdater" + }, + "search": { + "installed": "Søg efter installerede repositories", + "installed_new": "Søg efter installerede eller nye repositories", + "placeholder": "Søg efter repository" + }, + "sections": { + "about": { + "description": "Vis information om HACS", + "title": "Om" + }, + "automation": { + "description": "Det er her, du finder python_scripts, AppDaemon-apps og NetDaemon-apps", + "title": "Automatisering" + }, + "frontend": { + "description": "Det er her, du finder temaer, brugerdefinerede kort og andre elementer til lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Det er her, du finder brugerdefinerede integrationer (custom_components)", + "title": "Integrationer" + }, + "pending_repository_upgrade": "Du kører version {installed}, version {available} er tilgængelig" + }, + "settings": { + "add_custom_repository": "TILFØJ ET BRUGERDEFINERET REPOSITORY", + "adding_new_repo": "Tilføjer nyt repository '{repo}'", + "adding_new_repo_category": "Med kategorien '{category}'.", + "bg_task_custom": "Brugerdefinerede repositories er skjult, mens opgaver i baggrunden kører.", + "category": "Kategori", + "compact_mode": "Kompakt tilstand", + "custom_repositories": "BRUGERDEFINEREDE REPOSITORIES", + "delete": "Slet", + "display": "Visning", + "grid": "Gitter", + "hacs_repo": "HACS-repo", + "hidden_repositories": "Skjulte repositories", + "missing_category": "Du skal vælge en kategori", + "open_repository": "Åbn repository", + "reload_data": "Genindlæs data", + "reload_window": "Genindlæs vindue", + "repository_configuration": "Konfiguration af repository", + "save": "Gem", + "table": "Tabel", + "table_view": "Tabelvisning", + "unhide": "Vis", + "upgrade_all": "Opdater alle" + }, + "store": { + "ascending": "stigende", + "clear_new": "Marker alle som set", + "descending": "faldende", + "last_updated": "Sidst opdateret", + "name": "Navn", + "new_repositories": "Nye repositories", + "new_repositories_note": "Du har over 10 nye repositories, der vises her. Hvis du vil rydde dem alle, skal du klikke på de 3 prikker i øverste højre hjørne og afvise dem alle.", + "no_repositories": "Ingen repositories", + "no_repositories_desc1": "Det ser ud til, at du ikke har nogen repositories installeret i denne sektion endnu.", + "no_repositories_desc2": "Klik på + i nederste hjørne for at tilføje dit første!", + "no_repositories_found_desc1": "Der blev ikke fundet installerede repositories, der matcher \"{searchInput}\" i denne sektion.", + "no_repositories_found_desc2": "Prøv at søge efter noget andet!", + "pending_upgrades": "Ventende opdateringer", + "placeholder_search": "Indtast en søgeterm...", + "sort": "sorter", + "stars": "Stjerner", + "status": "Status" + }, + "time": { + "ago": "siden", + "day": "dag", + "days": "dage", + "hour": "time", + "hours": "timer", + "minute": "minut", + "minutes": "minutter", + "month": "måned", + "months": "måneder", + "one": "Et", + "one_day_ago": "en dag siden", + "one_hour_ago": "en time siden", + "one_minute_ago": "et minut siden", + "one_month_ago": "en måned siden", + "one_second_ago": "et sekund siden", + "one_year_ago": "et år siden", + "second": "sekund", + "seconds": "sekunder", + "x_days_ago": "{x} dage siden", + "x_hours_ago": "{x} timer siden", + "x_minutes_ago": "{x} minutter siden", + "x_months_ago": "{x} måneder siden", + "x_seconds_ago": "{x} sekunder siden", + "x_years_ago": "{x} år siden", + "year": "år", + "years": "år" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/de.json b/custom_components/hacs/translations/de.json new file mode 100644 index 0000000..902b61d --- /dev/null +++ b/custom_components/hacs/translations/de.json @@ -0,0 +1,383 @@ +{ + "common": { + "about": "Über", + "add": "hinzufügen", + "appdaemon": "AppDaemon", + "appdaemon_apps": "AppDaemon Apps", + "appdaemon_plural": "AppDaemon Apps", + "background_task": "Hintergrundprozess läuft. Diese Seite lädt neu, sobald dieser fertig ist.", + "cancel": "Abbrechen", + "check_log_file": "Überprüfe die Logdatei für weitere Informationen.", + "continue": "Fortfahren", + "disabled": "Deaktiviert", + "documentation": "Dokumentation", + "element": "Element", + "hacs_is_disabled": "HACS ist deaktiviert", + "ignore": "Ignorieren", + "install": "Installieren", + "installed": "Installiert", + "integration": "Integration", + "integration_plural": "Integrationen", + "integrations": "Integrationen", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace-Element", + "lovelace_elements": "Lovelace-Elemente", + "manage": "verwalten", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon Apps", + "netdaemon_plural": "NetDaemon Apps", + "plugin": "Lovelace", + "plugin_plural": "Lovelace-Elemente", + "plugins": "Lovelace-Elemente", + "python_script": "Python Skript", + "python_script_plural": "Python Skripte", + "python_scripts": "Python Skripte", + "reload": "Neu laden", + "repositories": "Repositories", + "repository": "Repository", + "settings": "Einstellungen", + "theme": "Theme", + "theme_plural": "Themes", + "themes": "Themes", + "uninstall": "Deinstallieren", + "update": "Aktualisieren", + "version": "Version" + }, + "config": { + "abort": { + "single_instance_allowed": "Es ist nur eine einzelne HACS-Instanz erlaubt." + }, + "error": { + "acc": "Du musst alle Aussagen bestätigen, bevor du fortfahren kannst", + "auth": "Persönlicher Zugriffstoken ist falsch." + }, + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Möchtest du dies wirklich zu deinen Lovelace-Ressourcen hinzufügen?", + "bg_task": "Die Aktion ist deaktiviert, während Hintergrundprozesse ausgeführt werden.", + "cancel": "Abbrechen", + "continue": "Bist du dir sicher, dass du fortfahren möchtest?", + "delete": "Möchtest du '{item}' wirklich löschen?", + "delete_installed": "'{item}' ist installiert. Du musst es deinstallieren, bevor du es löschen kannst.", + "exist": "{item} existiert bereits", + "generic": "Bist du dir sicher?", + "home_assistant_is_restarting": "Bitte warte, Home Assistant wird jetzt neu gestartet.", + "home_assistant_version_not_correct": "Du benutzt die Home Assistant-Version '{haversion}', für dieses Repository muss jedoch die Mindestversion '{minversion}' installiert sein.", + "no": "Nein", + "no_upgrades": "Keine Upgrades ausstehend", + "ok": "OK", + "overwrite": "Dadurch wird die Datei überschrieben.", + "reload_data": "Hierdurch werden die Daten aller Repositories die HACS kennt neu geladen. Dies wird einige Zeit in Anspruch nehmen.", + "restart_home_assistant": "Bist du dir sicher, dass du Home Assistant neu starten möchtest?", + "uninstall": "Möchtest du '{item}' wirklich deinstallieren?", + "upgrade_all": "Hierdurch werden all diese Repositories aktualisiert. Stelle sicher, dass du die Versionshinweise vorher gelesen hast.", + "yes": "Ja" + }, + "dialog_about": { + "frontend_version": "Frontend Version", + "installed_repositories": "Installierte Repositories", + "integration_version": "Integrations Version", + "useful_links": "Nützliche Links" + }, + "dialog_add_repo": { + "limit": "Es werden nur die ersten 100 Repositories angezeigt. Verwende die Suche, um zu filtern, was du benötigst", + "no_match": "Es wurden keine Repositories gefunden, die deinen Filter entsprechen", + "sort_by": "Sortiere nach", + "title": "Repository hinzufügen" + }, + "dialog_custom_repositories": { + "category": "Kategorie", + "no_category": "Fehlende Kategorie", + "no_repository": "Fehlendes Repository", + "title": "Benutzerdefinierte Repositories", + "url_placeholder": "Füge eine benutzerdefinierte Repository-URL hinzu" + }, + "dialog_info": { + "author": "Autor", + "downloads": "Downloads", + "install": "Installiere dieses Repository in HACS", + "loading": "Informationen laden...", + "no_info": "Der Entwickler hat keine weiteren Informationen für dieses Repository bereitgestellt", + "open_issues": "Probleme melden", + "open_repo": "Repository öffnen", + "stars": "Sterne", + "version_installed": "Version installiert" + }, + "dialog_install": { + "restart": "Denke daran, dass du Home Assistant neu starten musst, bevor Änderungen an Integrationen (custom_components) angewendet werden.", + "select_version": "Version auswählen", + "show_beta": "Beta-Versionen anzeigen", + "type": "Typ", + "url": "URL" + }, + "dialog_removed": { + "link": "Externer Link zu weiteren Informationen", + "name": "Repository-Name", + "reason": "Grund für die Entfernung", + "type": "Art der Entfernung" + }, + "dialog_update": { + "available_version": "Verfügbare Version", + "changelog": "Änderungsprotokoll", + "installed_version": "Installierte Version", + "releasenotes": "Releasenotes für {release}", + "title": "Update ausstehend" + }, + "dialog": { + "reload": { + "confirm": "Willst du das jetzt machen?", + "description": "Du musst deinen Browser-Cache leeren, wenn du Lovelace-Ressourcen änderst." + } + }, + "entry": { + "information": "Information", + "intro": "Aktualisierungen und wichtige Meldungen werden hier angezeigt, falls vorhanden", + "messages": { + "disabled": { + "content": "Überprüfe die Logdatei für weitere Informationen", + "title": "HACS ist deaktiviert" + }, + "has_pending_tasks": { + "content": "Einige Repositorys werden möglicherweise erst angezeigt, wenn dies abgeschlossen ist", + "title": "Hintergrundaufgaben stehen noch aus" + }, + "removed": "Repository '{repository}' gelöscht", + "resources": { + "content": "Du hast {number} Lovelace-Elemente, die in Lovelace nicht richtig geladen sind.", + "title": "Nicht in Lovelace geladen" + }, + "restart": { + "content": "Du hast {number} Integrationen, die einen Neustart von Home Assistant erfordern. Dies kannst du im Abschnitt 'Server Controls' im Konfigurationsteil der Home Assistant-Benutzeroberfläche tun.", + "title": "Ausstehender Neustart" + }, + "setup": { + "content": "HACS wird gerade eingerichtet, während dieser Zeit könnten einige Informationen fehlen oder falsch sein", + "title": "HACS wird eingerichtet" + }, + "startup": { + "content": "HACS wird gestartet, während dieser Zeit könnten einige Informationen fehlen oder falsch sein", + "title": "HACS startet" + }, + "waiting": { + "content": "HACS wartet darauf, dass Home Assistant den Start beendet, bevor mit den Startaufgaben gestartet wird", + "title": "HACS wartet" + }, + "wrong_frontend_installed": { + "content": "Du hast {running} des HACS-Frontends installiert, aber Version {expected} wurde erwartet. Wenn diese Meldung angezeigt wird, dass Home Assistant die neue Version nicht installieren konnte, starte Home Assistant neu.", + "title": "Unerwartete Frontend-Version" + }, + "wrong_frontend_loaded": { + "content": "Du führst die Version {running} des HACS-Frontends aus, aber es wurde die Version {expected} erwartet. Du solltest deinen Browser-Cache leeren.", + "title": "Unerwartete Frontend-Version" + } + }, + "pending_updates": "Ausstehende Updates" + }, + "menu": { + "about": "Über HACS", + "clear": "Alles neue als gesehen markieren", + "custom_repositories": "Benutzerdefinierte Repositories", + "dismiss": "Alle neuen Repositories ausblenden", + "documentation": "Dokumentation", + "open_issue": "Problem melden", + "reload": "Fenster neu laden" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "AppDaemon App-Entdeckung & Nachverfolgung aktivieren", + "country": "Nach Ländercode filtern.", + "debug": "Debug aktivieren.", + "experimental": "Experimentelle Funktionen aktivieren", + "netdaemon": "NetDaemon App-Entdeckung & Nachverfolgung aktivieren", + "not_in_use": "Nicht in Verwendung mit YAML", + "release_limit": "Anzahl anzuzeigender Releases.", + "sidepanel_icon": "Sidepanel Symbol", + "sidepanel_title": "Sidepanel Titel" + } + } + } + }, + "repository_banner": { + "config_flow": "Diese Integration unterstützt config_flow. Das bedeutet, dass du jetzt in den Integrationsbereich deiner Benutzeroberfläche wechseln kannst, um es zu konfigurieren.", + "config_flow_title": "UI-Konfiguration unterstützt", + "integration_not_loaded": "Diese Integration ist in Home Assistant nicht geladen.", + "no_restart_required": "Kein Neustart erforderlich", + "not_loaded": "Nicht geladen", + "plugin_not_loaded": "Dieses Element wird nicht zu deinen Lovelace-Ressourcen hinzugefügt.", + "restart": "Du musst Home Assistant neu starten.", + "restart_pending": "Neustart ausstehend" + }, + "repository_card": { + "dismiss": "Ausblenden", + "hide": "Verstecken", + "information": "Information", + "new_repository": "Neues Repository", + "not_loaded": "Nicht geladen", + "open_issue": "Problem melden", + "open_source": "Quelldateien öffnen", + "pending_restart": "Ausstehender Neustart", + "pending_update": "Ausstehende Aktualisierung", + "reinstall": "Neu installieren", + "report": "Melden zur Entfernung des Repositorys", + "update_information": "Aktualisierungsinformationen" + }, + "repository": { + "add_to_lovelace": "Zu Lovelace hinzufügen", + "authors": "Autoren", + "available": "Verfügbar", + "back_to": "Zurück zu", + "changelog": "Änderungsprotokoll", + "downloads": "Downloads", + "flag_this": "Melden", + "frontend_version": "Frontend Version", + "github_stars": "GitHub Sterne", + "goto_integrations": "Gehe zu Integrationen", + "hide": "Verstecken", + "hide_beta": "Beta verstecken", + "install": "Installieren", + "installed": "Installiert", + "lovelace_copy_example": "Beispiel in die Zwischenablage kopieren", + "lovelace_instruction": "Zum Hinzufügen zu deinen Lovelace-Einstellungen, benutze Folgendes", + "lovelace_no_js_type": "Der Typ dieses Elements konnte nicht ermittelt werden. Überprüfe das Repository.", + "newest": "neueste", + "note_appdaemon": "du musst es dann noch in die Datei 'apps.yaml' hinzufügen", + "note_installed": "Wird installiert nach", + "note_integration": "du musst es dann noch in die Datei 'configuration.yaml' hinzufügen", + "note_plugin": "du musst es dann noch in deine Lovelace-Einstellungen ('ui-lovelace.yaml' oder im Raw-Konfigurationseditor) hinzufügen", + "note_plugin_post_107": "Du musst es noch zu deiner Lovelace-Konfiguration hinzufügen ('configuration.yaml' oder der Ressourceneditor '\/config\/lovelace\/resources')", + "open_issue": "Problem melden", + "open_plugin": "Element öffnen", + "reinstall": "Neu installieren", + "repository": "Repository", + "restart_home_assistant": "Home Assistant neu starten", + "show_beta": "Beta anzeigen", + "uninstall": "Deinstallieren", + "update_information": "Aktualisierungsinformationen", + "upgrade": "Aktualisieren" + }, + "search": { + "installed": "Suche nach installierten Repositories", + "installed_new": "Suche nach installierten oder neuen Repositories", + "placeholder": "Suche nach Repository" + }, + "sections": { + "about": { + "description": "Informationen zu HACS anzeigen", + "title": "Über" + }, + "addon": { + "description": "Es gibt in HACS selbst keine Add-ons, aber du kannst hier klicken, um zum Supervisor zu gelangen", + "title": "Add-ons" + }, + "automation": { + "description": "Hier findest du python_scripts, AppDaemon-Apps und NetDaemon-Apps", + "title": "Automatisierung" + }, + "frontend": { + "description": "Hier findest du Themen, individuelle Karten und andere Elemente für Lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Hier findest du benutzerdefinierte Integrationen (custom_components)", + "title": "Integrationen" + }, + "pending_repository_upgrade": "Du verwendest Version {installed}, Version {available} ist verfügbar" + }, + "settings": { + "add_custom_repository": "BENUTZERDEFINIERTES REPOSITORY HINZUFÜGEN", + "adding_new_repo": "Hinzufügen eines neuen Repository '{repo}'", + "adding_new_repo_category": "Mit der Kategorie '{category}'.", + "bg_task_custom": "Custom Repositorys werden ausgeblendet, während Hintergrundaufgaben ausgeführt werden.", + "category": "Kategorie", + "compact_mode": "Kompakter Modus", + "custom_repositories": "BENUTZERDEFINIERTE REPOSITORIES", + "delete": "Löschen", + "display": "Anzeige", + "grid": "Gitter", + "hacs_repo": "HACS repo", + "hidden_repositories": "versteckte Repositories", + "missing_category": "Du musst eine Kategorie auswählen.", + "open_repository": "Repository öffnen", + "reload_data": "Daten neu laden", + "reload_window": "Fenster neu laden", + "repository_configuration": "Repository Konfiguration", + "save": "Speichern", + "table": "Tabelle", + "table_view": "Tabellenansicht", + "unhide": "einblenden", + "upgrade_all": "Alles aktualisieren" + }, + "store": { + "ascending": "Aufsteigend", + "clear_new": "Alle neuen Repositories als gesehen markieren", + "descending": "Absteigend", + "last_updated": "Zuletzt aktualisiert", + "name": "Name", + "new_repositories": "Neue Repositories", + "new_repositories_note": "Hier werden über 10 neue Repositorys angezeigt. Wenn du alle als gelesen markieren möchten, klicke auf die 3 Punkte in der oberen rechten Ecke und blende alle aus.", + "no_repositories": "Keine Repositories", + "no_repositories_desc1": "Anscheinend sind in diesem Abschnitt noch keine Repositories installiert.", + "no_repositories_desc2": "Klicken auf das + in der unteren Ecke, um dein erstes hinzuzufügen!", + "no_repositories_found_desc1": "In diesem Abschnitt wurden keine installierten Repositorys gefunden, die mit \"{searchInput}\" übereinstimmen.", + "no_repositories_found_desc2": "Versuche, nach etwas anderem zu suchen!", + "pending_upgrades": "Ausstehende Upgrades", + "placeholder_search": "Suchbegriff eingeben…", + "sort": "Sortieren", + "stars": "Sterne", + "status": "Status" + }, + "time": { + "ago": "vor", + "day": "Tag", + "days": "Tage", + "hour": "Stunde", + "hours": "Stunden", + "minute": "Minute", + "minutes": "Minuten", + "month": "Monat", + "months": "Monate", + "one": "Eins", + "one_day_ago": "vor einem Tag", + "one_hour_ago": "vor einer Stunde", + "one_minute_ago": "vor einer Minute", + "one_month_ago": "vor einem Monat", + "one_second_ago": "vor einer Sekunde", + "one_year_ago": "vor einem Jahr", + "second": "Sekunde", + "seconds": "Sekunden", + "x_days_ago": "vor {x} Tagen", + "x_hours_ago": "vor {x} Stunden", + "x_minutes_ago": "vor {x} Minuten", + "x_months_ago": "vor {x} Monaten", + "x_seconds_ago": "vor {x} Sekunden", + "x_years_ago": "vor {x} Jahren", + "year": "Jahr", + "years": "Jahre" + } +} \ No newline at end of file diff --git a/custom_components/hacs/.translations/el.json b/custom_components/hacs/translations/el.json similarity index 85% rename from custom_components/hacs/.translations/el.json rename to custom_components/hacs/translations/el.json index 0d446ad..e783433 100644 --- a/custom_components/hacs/.translations/el.json +++ b/custom_components/hacs/translations/el.json @@ -12,9 +12,10 @@ "installed": "εγκατεστημένο", "integration": "Ενσωμάτωση", "integrations": "Ενσωματωμένα", + "manage": "διαχειρίζονται", "netdaemon": "NetDaemon", "netdaemon_apps": "NetDaemon Apps", - "plugin": "Πρόσθετο", + "plugin": "Lovelace", "plugins": "Πρόσθετα", "python_script": "Πρόγραμμα Python", "python_scripts": "Προγράμματα Python", @@ -25,25 +26,27 @@ "version": "Έκδοση" }, "config": { - "abort": { - "single_instance_allowed": "Μονάχα μία ρύθμιση των παραμέτρων του HACS επιτρέπεται." - }, - "error": { - "auth": "Το διακριτικό πρόσβασης δεν είναι σωστό." - }, "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, "user": { "data": { - "appdaemon": "Ενεργοποίηση εύρεσης & παρακολούθησης για το AppDaemon", - "netdaemon": "Ενεργοποίηση εύρεσης & παρακολούθησης για το NetDaemon", - "python_script": "Ενεργοποίηση εύρεσης & παρακολούθησης για τα python_scripts", - "sidepanel_icon": "Εικονίδιο πλαϊνού πάνελ", - "sidepanel_title": "Τίτλος πλαϊνού πάνελ", - "theme": "Ενεργοποίηση εύρεσης & παρακολούθησης για τα θεμάτων", - "token": "Διακριτικό πρόσβασης του GitHub" + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" }, - "description": "Εάν χρειαστείτε βοήθεια με τις ρυθμίσεις ανατρέξτε εδώ: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" } }, "title": "HACS (Home Assistant Community Store)" @@ -76,6 +79,7 @@ "debug": "Ενεργοποίηση εντοπισμού σφαλμάτων.", "experimental": "Ενεργοποίση πειραματικών λειτουργιών", "netdaemon": "Ενεργοποίηση εύρεσης & παρακολούθησης για το NetDaemon", + "not_in_use": "Δεν χρησιμοποιείται με το YAML", "release_limit": "Αριθμός εκδόσεων που να παραθέτονται.", "sidepanel_icon": "Εικονίδιο πλαϊνού πάνελ", "sidepanel_title": "Τίτλος πλαϊνού πάνελ" @@ -100,6 +104,8 @@ "downloads": "Λήψεις", "flag_this": "Σημείωσε αυτό", "frontend_version": "Έκδοση Frontend", + "github_stars": "GitHub αστέρια", + "goto_integrations": "Μετάβαση στις ενσωματώσεις", "hide": "Απόκρυψη", "hide_beta": "Απόκριση του beta", "install": "Εγκατάσταση", @@ -124,6 +130,7 @@ }, "settings": { "add_custom_repository": "ΠΡΟΣΘΕΣΤΕ ΕΝΑ ΕΙΔΙΚΟ ΑΠΟΘΕΤΗΡΙΟ", + "adding_new_repo": "Προσθήκη νέου αποθετηρίου '{repo}'", "adding_new_repo_category": "Με κατηγορία '{category}'.", "category": "Κατηγορία", "compact_mode": "Συμπαγής λειτουργία", @@ -137,9 +144,11 @@ "open_repository": "Ανοίξτε το αποθετήριο", "reload_data": "Επαναφόρτωση δεδομένων", "reload_window": "Επαναφόρτωση του παραθύρου", + "repository_configuration": "Διαμόρφωση αποθετηρίου", "save": "Αποθήκευση", "table": "Πίνακας", "table_view": "Προβολή πίνακα", + "unhide": "αποκρύψω", "upgrade_all": "Αναβάθμιση όλων" }, "store": { diff --git a/custom_components/hacs/translations/en.json b/custom_components/hacs/translations/en.json new file mode 100644 index 0000000..da9080c --- /dev/null +++ b/custom_components/hacs/translations/en.json @@ -0,0 +1,384 @@ +{ + "common": { + "about": "About", + "add": "add", + "appdaemon": "AppDaemon", + "appdaemon_apps": "AppDaemon Apps", + "appdaemon_plural": "AppDaemon Apps", + "background_task": "Background task running, this page will reload when it's done.", + "cancel": "Cancel", + "check_log_file": "Check your log file for more details.", + "continue": "Continue", + "disabled": "Disabled", + "documentation": "Documentation", + "element": "element", + "hacs_is_disabled": "HACS is disabled", + "ignore": "Ignore", + "install": "Install", + "installed": "installed", + "integration": "Integration", + "integration_plural": "Integrations", + "integrations": "Integrations", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace element", + "lovelace_elements": "Lovelace elements", + "manage": "manage", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon Apps", + "netdaemon_plural": "NetDaemon Apps", + "plugin": "Lovelace", + "plugin_plural": "Lovelace elements", + "plugins": "Lovelace elements", + "python_script": "Python Script", + "python_script_plural": "Python Scripts", + "python_scripts": "Python Scripts", + "reload": "Reload", + "repositories": "Repositories", + "repository": "Repository", + "settings": "settings", + "theme": "Theme", + "theme_plural": "Themes", + "themes": "Themes", + "uninstall": "Uninstall", + "update": "Update", + "version": "Version" + }, + "config": { + "abort": { + "single_instance_allowed": "Only a single configuration of HACS is allowed." + }, + "error": { + "acc": "You need to acknowledge all the statements before continuing", + "auth": "Personal Access Token is not correct" + }, + "step": { + "device": { + "description": "Open {url} and paste this key to authorize HACS: \\n```\\n{code}\\n```\\n When you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Are you sure you want to add this to your Lovelace resources?", + "bg_task": "Action is disabled while background tasks is running.", + "cancel": "Cancel", + "continue": "Are you sure you want to continue?", + "delete": "Are you sure you want to delete '{item}'?", + "delete_installed": "'{item}' is installed, you need to uninstall it before you can delete it.", + "exist": "{item} already exists", + "generic": "Are you sure?", + "home_assistant_is_restarting": "Hold on, Home Assistant is now restarting.", + "home_assistant_version_not_correct": "You are running Home Assistant version '{haversion}', but this repository requires minimum version '{minversion}' to be installed.", + "no": "No", + "no_upgrades": "No upgrades pending", + "ok": "OK", + "overwrite": "Doing this will overwrite it.", + "reload_data": "This reloads the data of all repositories HACS knows about, this will take some time to finish.", + "restart_home_assistant": "Are you sure you want to restart Home Assistant?", + "uninstall": "Are you sure you want to uninstall '{item}'?", + "upgrade_all": "This will upgrade all of these repositories, make sure that you have read the release notes for all of them before proceeding.", + "yes": "Yes" + }, + "dialog_about": { + "frontend_version": "Frontend version", + "installed_repositories": "Installed repositories", + "integration_version": "Integration version", + "useful_links": "Useful links" + }, + "dialog_add_repo": { + "limit": "Only the first 100 repositories are shown, use the search to filter what you need", + "no_match": "No repositories found matching your filter", + "sort_by": "Sort by", + "title": "Add repository" + }, + "dialog_custom_repositories": { + "category": "Category", + "no_category": "Missing category", + "no_repository": "Missing repository", + "title": "Custom repositories", + "url_placeholder": "Add custom repository URL" + }, + "dialog_info": { + "author": "Author", + "downloads": "Downloads", + "install": "Install this repository in HACS", + "loading": "Loading information...", + "no_info": "The developer has not provided any more information for this repository", + "open_issues": "Open issues", + "open_repo": "Open repository", + "stars": "Stars", + "version_installed": "Version installed" + }, + "dialog_install": { + "restart": "Remember that you need to restart Home Assistant before changes to integrations (custom_components) are applied.", + "select_version": "Select version", + "show_beta": "Show beta versions", + "type": "Type", + "url": "URL" + }, + "dialog_removed": { + "link": "External link to more information", + "name": "Repository name", + "reason": "Removal reason", + "type": "Removal type" + }, + "dialog_update": { + "available_version": "Available version", + "changelog": "Changelog", + "installed_version": "Installed version", + "releasenotes": "Release notes for {release}", + "title": "Update pending" + }, + "dialog": { + "reload": { + "confirm": "Do you want to do that now?", + "description": "You need to clear your browser cache when changing Lovelace resources." + } + }, + "entry": { + "information": "Information", + "intro": "Updates and important messages will show here if there are any", + "messages": { + "disabled": { + "content": "Check your log file for more details", + "title": "HACS is disabled" + }, + "has_pending_tasks": { + "content": "Some repositories might not show until this is completed", + "title": "Background tasks pending" + }, + "removed": "Removed repository '{repository}'", + "resources": { + "content": "You have {number} Lovelace elements that are not loaded properly in Lovelace.", + "title": "Not loaded in Lovelace" + }, + "restart": { + "content": "You have {number} integrations that requires a restart of Home Assistant, you can do that from the 'Server Controls' section under the configuration part of Home Assistant UI.", + "title": "Pending restart" + }, + "setup": { + "content": "HACS is setting up, during this time some information might be missing or incorrect", + "title": "HACS is setting up" + }, + "startup": { + "content": "HACS is starting up, during this time some information might be missing or incorrect", + "title": "HACS is starting up" + }, + "waiting": { + "content": "HACS is waiting for Home Assistant to finish startup before starting startup tasks", + "title": "HACS is waiting" + }, + "wrong_frontend_installed": { + "content": "You have {running} of the HACS frontend installed, but version {expected} was expected, if this you see this message Home Assistant was not able to install the new version, try restarting Home Assistant.", + "title": "Unexpected frontend version" + }, + "wrong_frontend_loaded": { + "content": "You are running version {running} of the HACS frontend, but version {expected} was expected, you should clear your browser cache.", + "title": "Unexpected frontend version" + } + }, + "pending_updates": "Pending updates" + }, + "menu": { + "about": "About HACS", + "clear": "Clear all new", + "custom_repositories": "Custom repositories", + "dismiss": "Dismiss all new repositories", + "documentation": "Documentation", + "open_issue": "Open issue", + "reload": "Reload window" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "country": "Filter with country code.", + "debug": "Enable debug.", + "experimental": "Enable experimental features", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "not_in_use": "Not in use with YAML", + "release_limit": "Number of releases to show.", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title" + } + } + } + }, + "repository_banner": { + "config_flow": "This integration supports config_flow, that means that you now can go to the integration section of your UI to configure it.", + "config_flow_title": "UI Configuration supported", + "integration_not_loaded": "This integration is not loaded in Home Assistant.", + "no_restart_required": "No restart required", + "not_loaded": "Not loaded", + "plugin_not_loaded": "This element is not added to your Lovelace resources.", + "restart": "You need to restart Home Assistant.", + "restart_pending": "Restart pending" + }, + "repository_card": { + "dismiss": "dismiss", + "hide": "Hide", + "information": "Information", + "new_repository": "New repository", + "not_loaded": "Not loaded", + "open_issue": "Open issue", + "open_source": "Open source", + "pending_restart": "Pending restart", + "pending_update": "Pending update", + "reinstall": "Reinstall", + "report": "Report for removal", + "update_information": "Update information" + }, + "repository": { + "add_to_lovelace": "Add to Lovelace", + "authors": "Authors", + "available": "Available", + "back_to": "Back to", + "changelog": "Change log", + "downloads": "Downloads", + "flag_this": "Flag this", + "frontend_version": "Frontend version", + "github_stars": "GitHub stars", + "goto_integrations": "Go to integrations", + "hide": "Hide", + "hide_beta": "Hide beta", + "install": "Install", + "installed": "Installed", + "lovelace_copy_example": "Copy the example to your clipboard", + "lovelace_instruction": "When you add this to your lovelace configuration use this", + "lovelace_no_js_type": "Could not determine the type of this element, check the repository.", + "newest": "newest", + "note_appdaemon": "you still need to add it to your 'apps.yaml' file", + "note_installed": "When installed, this will be located in", + "note_integration": "you still need to add it to your 'configuration.yaml' file", + "note_plugin": "you still need to add it to your lovelace configuration ('ui-lovelace.yaml' or the raw UI config editor)", + "note_plugin_post_107": "you still need to add it to your lovelace configuration ('configuration.yaml' or the resource editor '\/config\/lovelace\/resources')", + "open_issue": "Open issue", + "open_plugin": "Open element", + "reinstall": "Reinstall", + "repository": "Repository", + "restart_home_assistant": "Restart Home Assistant", + "show_beta": "Show beta", + "uninstall": "Uninstall", + "update_information": "Update information", + "upgrade": "Update" + }, + "search": { + "installed": "Search for installed repositories", + "installed_new": "Search for installed or new repositories", + "placeholder": "Search for repository" + }, + "sections": { + "about": { + "description": "Show information about HACS", + "title": "About" + }, + "addon": { + "description": "There are no addons in HACS, but you can click here to get to the supervisor", + "title": "Add-ons" + }, + "automation": { + "description": "This is where you find python_scripts, AppDaemon apps and NetDaemon apps", + "title": "Automation" + }, + "frontend": { + "description": "This is where you find themes, custom cards and other elements for lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "This is where you find custom integrations (custom_components)", + "title": "Integrations" + }, + "pending_repository_upgrade": "You are running version {installed}, version {available} is available" + }, + "settings": { + "add_custom_repository": "ADD CUSTOM REPOSITORY", + "adding_new_repo": "Adding new repository '{repo}'", + "adding_new_repo_category": "With category '{category}'.", + "bg_task_custom": "Custom repositories are hidden while background tasks is running.", + "category": "Category", + "compact_mode": "Compact mode", + "custom_repositories": "CUSTOM REPOSITORIES", + "delete": "Delete", + "display": "Display", + "grid": "Grid", + "hacs_repo": "HACS repo", + "hidden_repositories": "hidden repositories", + "missing_category": "You need to select a category", + "open_repository": "Open repository", + "reload_data": "Reload data", + "reload_window": "Reload window", + "repository_configuration": "Repository configuration", + "save": "Save", + "table": "Table", + "table_view": "Table view", + "unhide": "unhide", + "upgrade_all": "Upgrade all" + }, + "store": { + "add": "Explore & add repositories", + "ascending": "ascending", + "clear_new": "Clear all new repositories", + "descending": "descending", + "last_updated": "Last updated", + "name": "Name", + "new_repositories": "New Repositories", + "new_repositories_note": "You have over 10 new repositories showing here, if you want to clear them all click the 3 dots in the top right corner and dismiss all of them.", + "no_repositories": "No repositories", + "no_repositories_desc1": "It seems like you don't have any repositories installed in this section yet.", + "no_repositories_desc2": "Click on the + in the bottom corner to add your first!", + "no_repositories_found_desc1": "No installed repositories matching \"{searchInput}\" found in this section.", + "no_repositories_found_desc2": "Try searching for something else!", + "pending_upgrades": "Pending upgrades", + "placeholder_search": "Please enter a search term...", + "sort": "sort", + "stars": "Stars", + "status": "Status" + }, + "time": { + "ago": "ago", + "day": "day", + "days": "days", + "hour": "hour", + "hours": "hours", + "minute": "minute", + "minutes": "minutes", + "month": "month", + "months": "months", + "one": "One", + "one_day_ago": "one day ago", + "one_hour_ago": "one hour ago", + "one_minute_ago": "one minute ago", + "one_month_ago": "one month ago", + "one_second_ago": "one second ago", + "one_year_ago": "one year ago", + "second": "second", + "seconds": "seconds", + "x_days_ago": "{x} days ago", + "x_hours_ago": "{x} hours ago", + "x_minutes_ago": "{x} minutes ago", + "x_months_ago": "{x} months ago", + "x_seconds_ago": "{x} seconds ago", + "x_years_ago": "{x} years ago", + "year": "year", + "years": "years" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/es.json b/custom_components/hacs/translations/es.json new file mode 100644 index 0000000..363db32 --- /dev/null +++ b/custom_components/hacs/translations/es.json @@ -0,0 +1,376 @@ +{ + "common": { + "about": "Acerca de", + "add": "añadir", + "appdaemon": "AppDaemon", + "appdaemon_apps": "AppDaemon Apps", + "appdaemon_plural": "AppDaemon Apps", + "background_task": "Ejecutando tareas en segundo plano. Se refrescará automaticamente esta página al finalizar.", + "cancel": "Cancelar", + "check_log_file": "Compruebe el archivo de registro para obtener más detalles.", + "continue": "Continuar", + "disabled": "Deshabilitado", + "documentation": "Documentación", + "element": "elemento", + "hacs_is_disabled": "HACS está deshabilitado", + "ignore": "Ignorar", + "install": "Instalar", + "installed": "instalado", + "integration": "Integración", + "integration_plural": "Integraciones", + "integrations": "Integraciones", + "lovelace": "Lovelace", + "lovelace_element": "Elemento de Lovelace", + "lovelace_elements": "Elementos de Lovelace", + "manage": "Administrar", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon Apps", + "netdaemon_plural": "Aplicaciones NetDaemon", + "plugin": "Lovelace", + "plugin_plural": "Elementos de Lovelace", + "plugins": "Elementos de Lovelace", + "python_script": "Python Script", + "python_script_plural": "Python Scripts", + "python_scripts": "Python Scripts", + "reload": "Recargar", + "repositories": "Repositorios", + "repository": "Repositorio", + "settings": "configuraciones", + "theme": "Tema", + "theme_plural": "Temas", + "themes": "Temas", + "uninstall": "Desinstalar", + "update": "Actualizar", + "version": "Versión" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "¿Está seguro de que desea agregar esto a sus recursos de Lovelace?", + "bg_task": "La acción está deshabilitada mientras se ejecutan tareas en segundo plano.", + "cancel": "Cancelar", + "continue": "Estás seguro de que quieres continuar?", + "delete": "¿Seguro que quieres eliminar '{item}'?", + "delete_installed": "'{item}' está instalado, debe desinstalarlo antes de poder eliminarlo.", + "exist": "{item} ya existe", + "generic": "¿Estás seguro?", + "home_assistant_is_restarting": "Espera, Home Assistant se está reiniciando.", + "home_assistant_version_not_correct": "Está ejecutando la versión '{haversion}' de Home Assistant, pero este repositorio requiere la instalación de la versión '{minversion}' mínima.", + "no": "No", + "no_upgrades": "No hay actualizaciones pendientes", + "ok": "OK", + "overwrite": "Si haces esto, se sobrescribirá.", + "reload_data": "Esto recarga los datos de todos los repositorios que HACS conoce, esto tardará algún tiempo en finalizar.", + "restart_home_assistant": "¿Está seguro de que desea reiniciar Home Assistant?", + "uninstall": "¿Está seguro de que deseas desinstalar '{item}'?", + "upgrade_all": "Esto actualizará todos estos repositorios, asegúrese de que ha leído las notas de la versión de todos ellos antes de continuar.", + "yes": "Si" + }, + "dialog_about": { + "frontend_version": "Versión del front-end", + "installed_repositories": "Repositorios instalados", + "integration_version": "Versión de la integración", + "useful_links": "Enlaces útiles" + }, + "dialog_add_repo": { + "limit": "Sólo se muestran los primeros 100 repositorios, utilice la búsqueda para filtrar lo que necesita", + "no_match": "No se han encontrado repositorios que coincidan con el filtro", + "sort_by": "Ordenar por", + "title": "Añadir repositorio" + }, + "dialog_custom_repositories": { + "category": "Categoría", + "no_category": "Categoría que falta", + "no_repository": "Falta el repositorio", + "title": "Repositorios personalizados", + "url_placeholder": "Agrega la URL del repositorio personalizado que deseas añadir" + }, + "dialog_info": { + "author": "Autor", + "downloads": "Descargas", + "install": "Instalar este repositorio en HACS", + "loading": "Cargando información ...", + "no_info": "El desarrollador no ha proporcionado más información para este repositorio", + "open_issues": "Abrir incidencias", + "open_repo": "Abrir repositorio", + "stars": "Estrellas", + "version_installed": "Versión instalada" + }, + "dialog_install": { + "restart": "Recuerde que debe reiniciar Home Assistant para que se apliquen los cambios en las integraciones (custom_components).", + "select_version": "Seleccione la versión", + "show_beta": "Mostrar versiones beta", + "type": "Tipo", + "url": "URL" + }, + "dialog_removed": { + "link": "Enlace externo para más información", + "name": "Nombre del repositorio", + "reason": "Motivo de la eliminación", + "type": "Tipo de eliminación" + }, + "dialog_update": { + "available_version": "Versión disponible", + "changelog": "Registro de cambios", + "installed_version": "Versión instalada", + "releasenotes": "Notas de lanzamiento para {release}", + "title": "Actualización pendiente" + }, + "dialog": { + "reload": { + "confirm": "¿Quieres hacer eso ahora?", + "description": "Necesitas limpiar el caché de tu navegador cuando cambies los recursos de Lovelace." + } + }, + "entry": { + "information": "Información", + "intro": "Las actualizaciones y los mensajes importantes se mostrarán aquí si hay alguno que mostrar", + "messages": { + "disabled": { + "content": "Compruebe el archivo de registro para obtener más detalles", + "title": "HACS está deshabilitado" + }, + "has_pending_tasks": { + "content": "Es posible que algunos repositorios no se muestren hasta que esto se complete", + "title": "Tareas en segundo plano pendientes" + }, + "removed": "Repositorio '{repository}' eliminado", + "resources": { + "content": "Tienes {number} elementos de Lovelace que no se cargan correctamente en Lovelace.", + "title": "No está cargada en Lovelace" + }, + "restart": { + "content": "Tienes {number} integraciones que requieren un reinicio de Home Assistant, puedes hacerlo desde la sección 'Controles del Servidor' en la parte de configuración de la UI de Home Assistant.", + "title": "Pendiente de reinicio" + }, + "setup": { + "content": "HACS se está configurando, durante este tiempo alguna información puede estar perdida o ser incorrecta", + "title": "HACS se está configurando" + }, + "startup": { + "content": "HACS se está iniciando, durante este tiempo alguna información podría faltar o ser incorrecta", + "title": "HACS se está iniciando" + }, + "waiting": { + "content": "HACS está esperando a que Home Assistant finalice el inicio antes de iniciar las tareas de inicio", + "title": "HACS está esperando" + }, + "wrong_frontend_installed": { + "content": "Tienes instalada la versión {running} de la interfaz de HACS, pero se esperaba la versión {expected}, si ves este mensaje, Home Assistant no pudo instalar la nueva versión, intenta reiniciar Home Assistant.", + "title": "Versión inesperada de la interfaz" + }, + "wrong_frontend_loaded": { + "content": "Estás ejecutando la versión {running} de la interfaz HACS, pero se esperaba la versión {expected} , deberías de limpiar la memoria caché del navegador.", + "title": "Versión inesperada de la interfaz" + } + }, + "pending_updates": "Actualizaciones pendientes" + }, + "menu": { + "about": "Acerca de HACS", + "clear": "Borrar todo lo nuevo", + "custom_repositories": "Repositorios personalizados", + "dismiss": "Descartar todos los repositorios nuevos", + "documentation": "Documentación", + "open_issue": "Abrir incidencias", + "reload": "Recargar la ventana" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de AppDaemon", + "country": "Filtrar por el código de país.", + "debug": "Habilitar depuración.", + "experimental": "Habilitar funciones experimentales", + "netdaemon": "Habilitar el descubrimiento y seguimiento de las aplicaciones de NetDaemon", + "not_in_use": "No usarse con YAML", + "release_limit": "Número de versiones a mostrar.", + "sidepanel_icon": "Icono del panel lateral", + "sidepanel_title": "Título del panel lateral" + } + } + } + }, + "repository_banner": { + "config_flow": "Esta integración soporta config_flow, lo que significa que ahora puede ir a la sección de integración de su UI para configurarlo.", + "config_flow_title": "Configuración de UI soportada", + "integration_not_loaded": "Esta integración no se carga en Home Assistant.", + "no_restart_required": "No es necesario reiniciar", + "not_loaded": "No está cargado", + "plugin_not_loaded": "Este plugin aun no se ha añadido a sus recursos de Lovelace.", + "restart": "Es necesario reiniciar Home Assistant.", + "restart_pending": "Reinicio pendiente" + }, + "repository_card": { + "dismiss": "descartar", + "hide": "Ocultar", + "information": "Información", + "new_repository": "Nuevo repositorio", + "not_loaded": "Sin cargar", + "open_issue": "Abrir incidencias", + "open_source": "Código abierto", + "pending_restart": "Pendiente de reinicio", + "pending_update": "Actualización pendiente", + "reinstall": "Reinstalar", + "report": "Informe para la eliminación", + "update_information": "Actualizar información" + }, + "repository": { + "add_to_lovelace": "Añadir a Lovelace", + "authors": "Autores", + "available": "Disponible", + "back_to": "Volver a", + "changelog": "Registro de cambios", + "downloads": "Descargas", + "flag_this": "Marcar esto", + "frontend_version": "Versión del frontend", + "github_stars": "Estrellas de GitHub", + "goto_integrations": "Ir a integraciones", + "hide": "Ocultar", + "hide_beta": "Ocultar beta", + "install": "Instalar", + "installed": "Instalado", + "lovelace_copy_example": "Copiar el ejemplo al clipboard", + "lovelace_instruction": "Agregue lo siguiente en su configuración de lovelace", + "lovelace_no_js_type": "No se pudo determinar el tipo de elemento, revise el repositorio.", + "newest": "más nuevo", + "note_appdaemon": "deberá agregar esto a su archivo 'apps.yaml'", + "note_installed": "Cuando esté instalado, se ubicará en", + "note_integration": "deberá agregar esto a su archivo 'configuration.yaml'", + "note_plugin": "deberá agregar esto a su configuración de lovelace ('ui-lovelace.yaml' o en el editor UI de lovelace)", + "note_plugin_post_107": "todavía necesita agregarlo a su configuración de lovelace ('configuration.yaml' o al editor de recursos '\/config\/lovelace\/resources')", + "open_issue": "Abrir incidencias", + "open_plugin": "Abrir elemento", + "reinstall": "Reinstalar", + "repository": "Repositorio", + "restart_home_assistant": "Reiniciar Home Assistant", + "show_beta": "Mostrar beta", + "uninstall": "Desinstalar", + "update_information": "Actualizar información", + "upgrade": "Actualizar" + }, + "search": { + "installed": "Buscar repositorios instalados", + "installed_new": "Buscar repositorios instalados o nuevos", + "placeholder": "Buscar repositorio" + }, + "sections": { + "about": { + "description": "Mostrar información sobre HACS", + "title": "Acerca de" + }, + "addon": { + "description": "No hay complementos (addons) en HACS, pero puede hacer clic aquí para ir a la pestaña Supervisor", + "title": "Complementos (addons)" + }, + "automation": { + "description": "Aquí es donde se encuentran python_scripts, aplicaciones AppDaemon y aplicaciones NetDaemon", + "title": "Automatización" + }, + "frontend": { + "description": "Aquí es donde encontrarás temas, tarjetas personalizadas y otros elementos para lovelace", + "title": "Interfaz" + }, + "integrations": { + "description": "Aquí es donde se encuentran las integraciones personalizadas (custom_components)", + "title": "Integraciones" + }, + "pending_repository_upgrade": "Está ejecutando la versión {installed}, la versión {available} está disponible" + }, + "settings": { + "add_custom_repository": "AGREGAR REPOSITORIO PERSONALIZADO", + "adding_new_repo": "Añadiendo un nuevo repositorio '{repo}'.", + "adding_new_repo_category": "Con la categoría '{categoría}'.", + "bg_task_custom": "Los repositorios personalizados están ocultos mientras se ejecutan las tareas en segundo plano.", + "category": "Categoría", + "compact_mode": "Modo compacto", + "custom_repositories": "REPOSITORIOS PERSONALIZADOS", + "delete": "Eliminar", + "display": "Mostrar", + "grid": "Cuadrícula", + "hacs_repo": "HACS repo", + "hidden_repositories": "repositorios ocultos", + "missing_category": "Es necesario seleccionar una categoría", + "open_repository": "Abrir repositorio", + "reload_data": "Recargar datos", + "reload_window": "Recargar ventana", + "repository_configuration": "Configuración del repositorio", + "save": "Guardar", + "table": "Tabla", + "table_view": "Vista de la tabla", + "unhide": "mostrar", + "upgrade_all": "Actualizar todo" + }, + "store": { + "ascending": "ascendente", + "clear_new": "Eliminar la lista los nuevos repositorios", + "descending": "descendente", + "last_updated": "Última actualización", + "name": "Nombre", + "new_repositories": "Nuevos Repositorios", + "new_repositories_note": "Tienes más de 10 nuevos repositorios mostrados aquí, si quieres borrarlos todos haz clic en los 3 puntos de la esquina superior derecha y deséchalos todos.", + "no_repositories": "Sin repositorios", + "no_repositories_desc1": "Parece que todavía no tiene ningún repositorio instalado en esta sección.", + "no_repositories_desc2": "Haga clic en el + de la esquina inferior derecha para agregar su primer repositorio!", + "no_repositories_found_desc1": "No se ha encontrado ningún repositorio instalado que coincida con el valor de \"{searchInput}\" en esta sección.", + "no_repositories_found_desc2": "¡Intenta buscar otra cosa!", + "pending_upgrades": "Actualizaciones pendientes", + "placeholder_search": "Por favor escriba una palabra clave de búsqueda...", + "sort": "ordenar", + "stars": "Estrellas", + "status": "Estado" + }, + "time": { + "ago": "hace", + "day": "dia", + "days": "dias", + "hour": "hora", + "hours": "horas", + "minute": "minuto", + "minutes": "minutos", + "month": "mes", + "months": "meses", + "one": "Uno", + "one_day_ago": "hace un día", + "one_hour_ago": "hace una hora", + "one_minute_ago": "hace un minuto", + "one_month_ago": "hace un mes", + "one_second_ago": "hace un segundo", + "one_year_ago": "hace un año", + "second": "segundo", + "seconds": "segundos", + "x_days_ago": "hace {x} dias", + "x_hours_ago": "hace {x} horas", + "x_minutes_ago": "hace {x} minutos", + "x_months_ago": "hace {x} meses", + "x_seconds_ago": "hace {x} segundos", + "x_years_ago": "hace {x} años", + "year": "año", + "years": "años" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/et_EE.json b/custom_components/hacs/translations/et_EE.json new file mode 100644 index 0000000..4cf2789 --- /dev/null +++ b/custom_components/hacs/translations/et_EE.json @@ -0,0 +1,358 @@ +{ + "common": { + "about": "Üldteave", + "add": "lisa", + "appdaemon_apps": "AppDaemoni rakendused", + "appdaemon_plural": "AppDaemoni rakendused", + "background_task": "Taustatoiming on töös. See leht taaslaetakse kui toiming on lõpetatud.", + "cancel": "Loobu", + "check_log_file": "Lisateavet leiad oma logifailist", + "continue": "Jätka", + "disabled": "Keelatud", + "documentation": "Dokumentatsioon", + "element": "element", + "hacs_is_disabled": "HACS on keelatud", + "ignore": "Eira", + "install": "Paigalda", + "installed": "paigaldatud", + "integration": "Sidumine", + "integration_plural": "Sidumised", + "integrations": "Sidumised", + "lovelace": "Lovelace", + "lovelace_element": "Kasutajaliidese element", + "lovelace_elements": "Kasutajaliidese elemendid", + "manage": "halda", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemoni rakendused", + "netdaemon_plural": "NetDaemoni rakendused", + "plugin": "Kasutajaliides", + "plugin_plural": "Kasutajaliidese elemendid", + "plugins": "Kasutajaliidese elemendid", + "python_script": "Pythoni skript", + "python_script_plural": "Pythoni skriptid", + "python_scripts": "Pythoni skriptid", + "reload": "Taaslae", + "repositories": "Teegid", + "repository": "Hoidla", + "settings": "seaded", + "theme": "Kuva teema", + "theme_plural": "Kuvateemad", + "themes": "Kuvateemad", + "uninstall": "Desinstalli", + "update": "Uuendus", + "version": "Versioon" + }, + "config": { + "abort": { + "single_instance_allowed": "Lubatud on ainult üks HACS-i paigaldus." + }, + "error": { + "acc": "Enne jätkamist pead nõustuma kõiki tingimustega", + "auth": "Isiklik juurdepääsutõend pole õige" + } + }, + "confirm": { + "add_to_lovelace": "Kas oled kindel, et soovid lisada selle oma Lovelace'i ressurssidele?", + "bg_task": "Taustaülesannete töötamise ajal on toiming keelatud.", + "cancel": "Loobu", + "continue": "Kas soovite kindlasti jätkata?", + "delete": "Kas soovid kindlasti üksuse '{item}' kustutada?", + "delete_installed": "'{item}' on ipaigaldatud, peate selle enne kustutamist eemaldama.", + "exist": "{item} on juba olemas", + "generic": "Oled sa kindel?", + "home_assistant_is_restarting": "Oota, Home Assistant taaskäivitub.", + "home_assistant_version_not_correct": "Kasutad Home Assistanti versiooni '{haversion}' kuid see hoidla nõuab vähemalt versiooni '{minversion}' installimist.", + "no": "Ei", + "no_upgrades": "Värskendused puuduvad", + "ok": "Sobib", + "overwrite": "See toiming kirjutab selle üle.", + "reload_data": "See taaslaeb kõigi HACS-i teada olevate hoidlate andme. Selle lõpuleviimine võtab palju aega.", + "restart_home_assistant": "Kas soovid kindlasti Home Assistanti taaskäivitada?", + "uninstall": "Kas soovid kindlasti üksuse '{item}' desinstallida?", + "upgrade_all": "See uuendab kõiki valitud hoidlaid. Veenduge, et olete enne jätkamist lugenud kõigi nende väljalaskemärkmeid.", + "yes": "Jah" + }, + "dialog_about": { + "frontend_version": "Kasutajaliidese versioon", + "installed_repositories": "Paigaldatud hoidlad", + "integration_version": "Sidumise versioon", + "useful_links": "Kasulikud veebiviited" + }, + "dialog_add_repo": { + "limit": "Kuvatakse ainult esimesed 100 hoidlat. Vajaliku filtreerimiseks kasutage otsingut", + "no_match": "Filtrile vastavaid hoidlaid ei leitud", + "sort_by": "Sortimisalus", + "title": "Lisa hoidla" + }, + "dialog_custom_repositories": { + "category": "Kategooria", + "no_category": "Puuduv kategooria", + "no_repository": "Puuduv hoidla", + "title": "Kohandatud hoidlad", + "url_placeholder": "Lisa kohandatud hoidla URL" + }, + "dialog_info": { + "author": "Autor", + "downloads": "Allalaadimised", + "install": "Paigalda see hoidla HACS-i", + "loading": "Teabe laadimine ...", + "no_info": "Arendaja ei ole selle hoidla kohta rohkem teavet avaldanud", + "open_issues": "Teadaolevad tõrketeatised", + "open_repo": "Ava hoidla", + "stars": "Hinnang", + "version_installed": "Paigaldatud versioon" + }, + "dialog_install": { + "restart": "Pea meeles, et sidumiste (custom_components) muudatuste rakendamiseks pead Home Assistanti taaskäivitama.", + "select_version": "Vali versioon", + "show_beta": "Kuva beetaversioonid", + "type": "Liik", + "url": "URL" + }, + "dialog_removed": { + "link": "Väline link lisateabe saamiseks", + "name": "Hoidla nimi", + "reason": "Eemaldamise põhjus", + "type": "Eemaldamise tüüp" + }, + "dialog_update": { + "available_version": "Saadaolev versioon", + "changelog": "Muudatused", + "installed_version": "Paigaldatud versioon", + "releasenotes": "Väljalaske märkused väljaandele {release}", + "title": "Uuendus on ootel" + }, + "dialog": { + "reload": { + "confirm": "Kas teen seda kohe?", + "description": "Lovelace ressursside muutmisel pead brauseri vahemälu tühjendama." + } + }, + "entry": { + "information": "Teave", + "intro": "Siin kuvatakse saadaval värskendused ja olulised sõnumid kui neid juhtub olema", + "messages": { + "disabled": { + "content": "Lisateavet leiad oma logifailist", + "title": "HACS on keelatud" + }, + "has_pending_tasks": { + "content": "Mõnda hoidlat ei kuvata enne kui tegevus on lõpule viidud", + "title": "Taustal on ootel toiminguid" + }, + "removed": "Eemaldasin hoidla '{repository}'", + "resources": { + "content": "Teil on {number} Lovelace'i elementi mis pole Lovelace'is õigesti laaditud.", + "title": "Ei laaditud Lovelace'i" + }, + "restart": { + "content": "Teil on {number} sidumist mis nõuavad Home Assistanti taaskäivitamist. Saate seda teha Home Assistanti kasutajaliidese seadete alamjaotisest \"Serveri juhtimine\".", + "title": "Taaskäivitamise ootel" + }, + "setup": { + "content": "HACS on seadistub. Selle aja jooksul võib osa teavet puududa või olla vale", + "title": "HACS seadistub" + }, + "startup": { + "content": "HACS käivitub. Selle aja jooksul võib osa teavet puududa või olla vale", + "title": "HACS käivitub" + }, + "waiting": { + "content": "HACS ootab Home Assistanti käivitumist", + "title": "HACS on ootel" + }, + "wrong_frontend_installed": { + "content": "Teil on installitud HACS'i kasutajaliides {running} kuid eeldatakse versiooni {expected}. Kui näete seda teadet ei suutnud Home Assistant uut versiooni installida. Proovige Home Assistant taaskäivitada.", + "title": "Ootamatu kasutajaliidese versioon" + }, + "wrong_frontend_loaded": { + "content": "Paigaldatud on HACS'i kasutajaliides {running} kuid eeldatakse versiooni {expected}. Tühjenda oma veebilehitseja vahemälu.", + "title": "Ootamatu kasutajaliidese versioon" + } + }, + "pending_updates": "Ootel värskendused" + }, + "menu": { + "about": "HACS-i info", + "clear": "Tühjenda kõik uued", + "custom_repositories": "Kohandatud hoidlad", + "dismiss": "Peida kõik uued hoidlad", + "documentation": "Dokumentatsioon", + "open_issue": "Esita tõrketeade", + "reload": "Lae aken uuesti" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Luba AppDaemoni rakenduste otsimine ja jälgimine", + "country": "Filtreeri riigi koodi abil.", + "debug": "Luba silumine.", + "experimental": "Luba katselised funktsioonid", + "netdaemon": "Luba NetDaemoni rakenduste otsimine ja jälgimine", + "not_in_use": "YAML režiimi ei toetata", + "release_limit": "Mitu väljalaset kuvada.", + "sidepanel_icon": "Külgpaneeli ikoon", + "sidepanel_title": "Külgpaneeli pealkiri" + } + } + } + }, + "repository_banner": { + "config_flow": "See sidumine toetab config_flow'd. See tähendab, et selle seadistamiseks pead minema oma kasutajaliidese sidumiste sektsiooni.", + "config_flow_title": "Seadistamine kasutajaliidese abil", + "integration_not_loaded": "Seda sidumist ei laeta Home Assistanti.", + "no_restart_required": "Taaskäivitamine pole vajalik", + "not_loaded": "Pole laaditud", + "plugin_not_loaded": "Seda elementi ei lisata teie Lovelace'i ressurssidesse", + "restart": "Pead Home Assistanti taaskäivitama.", + "restart_pending": "Taaskäivitamine on ootel" + }, + "repository_card": { + "dismiss": "Peida", + "hide": "Peida", + "information": "Teave", + "new_repository": "Uus hoidla", + "not_loaded": "Pole laaditud", + "open_issue": "Esita tõrketeade", + "open_source": "Avatud lähtekoodiga", + "pending_restart": "Taaskäivitamise ootel", + "pending_update": "Värskendamise ootel", + "reinstall": "Paigalda uuesti", + "report": "Teavita eemaldamiseks", + "update_information": "Värskenda teavet" + }, + "repository": { + "add_to_lovelace": "Lisa kasutajaliidesesse", + "authors": "Autorid", + "available": "Saadaval", + "back_to": "Tagasi", + "changelog": "Muudatuste teave", + "downloads": "Allalaadimised", + "flag_this": "Märgi see ära", + "frontend_version": "Kasutajaliidese versioon", + "github_stars": "GitHubi hinnangud", + "goto_integrations": "Mine sidumiste juurde", + "hide": "Peida", + "hide_beta": "Peida eelversioon", + "install": "Paigalda", + "installed": "Paigaldatud", + "lovelace_copy_example": "Kopeeri näide lõikelauale", + "lovelace_instruction": "Kui lisad selle oma kasutajaliidese seadetesse kasuta", + "lovelace_no_js_type": "Selle elemendi tüüpi ei õnnestunud määratleda, kontrolli hoidlat.", + "newest": "uusim", + "note_appdaemon": "pead selle ikkagi lisama oma faili 'apps.yaml'", + "note_installed": "Kui see on paigaldatus siis asub see", + "note_integration": "pead selle ikkagi lisama oma faili 'configuration.yaml'", + "note_plugin": "lisaks pead selle lisama oma kasutajaliidese seadetesse ('ui-lovelace.yaml' või raw UI seadete redaktoris')", + "note_plugin_post_107": "pead selle lisama oma kasutajaliidese seadetesse ('configuration.yaml' või ressursiredaktoris '\/ config \/ lovelace \/ resources')", + "open_issue": "Esita tõrketeade", + "open_plugin": "Ava element", + "reinstall": "Paigalda uuesti", + "repository": "Hoidla", + "restart_home_assistant": "Taaskäivita Home Assistant", + "show_beta": "Kuva eelversioon", + "uninstall": "Desinstalli", + "update_information": "Värskenduse teave", + "upgrade": "Uuenda" + }, + "search": { + "installed": "Otsi paigaldatud hoidlaid", + "installed_new": "Otsi paigaldatud või uusi hoidlaid", + "placeholder": "Otsi hoidlat" + }, + "sections": { + "about": { + "description": "Kuva HACS-i teave", + "title": "Üldteave" + }, + "addon": { + "description": "HACS-is pole lisandmooduleid. Supervisorisse pääsemiseks klõpsa siin", + "title": "Lisandmoodulid" + }, + "automation": { + "description": "Siit leiad python_scripts, AppDaemoni ja NetDaemoni rakendused", + "title": "Automatiseeringud" + }, + "frontend": { + "description": "Siit leiad kasutajaliidese teemad, kohandatud kaardid ja muud elemendid", + "title": "Kasutajaliides" + }, + "integrations": { + "description": "Siit leiad kohandatud sidumised (custom_components)", + "title": "Sidumised" + }, + "pending_repository_upgrade": "Kasutad versiooni {installed}, saadaval on versioon {available}" + }, + "settings": { + "add_custom_repository": "LISA KOHANDATUD HOIDLA", + "adding_new_repo": "Lisan uue hoidla '{repo}'", + "adding_new_repo_category": "Kategoorias '{category}'.", + "bg_task_custom": "Kohandatud hoidlad on taustatoimingute töötamise ajal peidetud.", + "category": "Kategooria", + "compact_mode": "Kompaktne režiim", + "custom_repositories": "KOHANDATUD HOIDLAD", + "delete": "Kustuta", + "display": "Kuva", + "grid": "Võrgustik", + "hacs_repo": "HACSi teek", + "hidden_repositories": "peidetud hoidlad", + "missing_category": "Pead valima kategooria", + "open_repository": "Ava hoidla", + "reload_data": "Taaslae andmed", + "reload_window": "Lae aken uuesti", + "repository_configuration": "Hoidla seaded", + "save": "Salvesta", + "table": "Tabel", + "table_view": "Tabeli vaade", + "unhide": "too peidust välja", + "upgrade_all": "Uuenda kõik" + }, + "store": { + "ascending": "kasvav", + "clear_new": "Peida kõik uued hoidlad", + "descending": "kahanev", + "last_updated": "Viimati uuendatud", + "name": "Nimi", + "new_repositories": "Uued hoidlad", + "new_repositories_note": "Siin on kuvatud üle 10 uue hoidla. Kui soovid need kõik peita klõpsa paremas ülanurgas 3 punkti ja jätaneed kõik kõrvale.", + "no_repositories": "Hoidlaid pole", + "no_repositories_desc1": "Tundub, et sul pole veel ühtegi hoidlat mis on siia jaotisse paigaldatud.", + "no_repositories_desc2": "Esimese hoidla lisamiseks klõpsake alumises nurgas + märki!", + "no_repositories_found_desc1": "Selles jaotises ei leitud ühtegi paigaldatud hoidlat mis vastaks otsingule \" {searchInput} \".", + "no_repositories_found_desc2": "Proovi otsida midagi muud!", + "pending_upgrades": "Ootel värskendused", + "placeholder_search": "Palun sisesta otsingutermin...", + "sort": "sorteeri", + "stars": "Hinnang", + "status": "Olek" + }, + "time": { + "ago": "eest", + "day": "päev", + "days": "päeva", + "hour": "tund", + "hours": "tundi", + "minute": "minut", + "minutes": "minutit", + "month": "kuu", + "months": "kuud", + "one": "Üks", + "one_day_ago": "ühe aasta eest", + "one_hour_ago": "ühe tunni eest", + "one_minute_ago": "ühe minuti eest", + "one_month_ago": "ühe kuu esst", + "one_second_ago": "ühe sekundi eest", + "one_year_ago": "ühe aasta eest", + "second": "sekund", + "seconds": "sekundit", + "x_days_ago": "{x} päeva eest", + "x_hours_ago": "{x} tunni eest", + "x_minutes_ago": "{x} minuti eest", + "x_months_ago": "{x} kuu eest", + "x_seconds_ago": "{x} sekundi eest", + "x_years_ago": "{x} aasta eest", + "year": "aasta", + "years": "aastat" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/fi.json b/custom_components/hacs/translations/fi.json new file mode 100644 index 0000000..5529fe5 --- /dev/null +++ b/custom_components/hacs/translations/fi.json @@ -0,0 +1,242 @@ +{ + "common": { + "about": "Tietoja", + "add": "Lisää", + "appdaemon_plural": "AppDaemon-sovellukset", + "check_log_file": "Tarkista lokitiedostosi saadaksesi lisätietoja.", + "disabled": "Poistettu käytöstä", + "documentation": "Dokumentointi", + "element": "elementti", + "hacs_is_disabled": "HACS on poistettu käytöstä", + "ignore": "Ohita", + "install": "Asenna", + "installed": "asennettu", + "integration_plural": "Integraatiot", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace-elementti", + "lovelace_elements": "Lovelace-elementit", + "manage": "hallinnoi", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon-sovellukset", + "netdaemon_plural": "NetDaemon-sovellukset", + "plugin": "Lovelace", + "plugin_plural": "Lovelace-elementit", + "plugins": "Lovelace-elementit", + "python_script_plural": "Python-skriptit", + "repository": "Repo", + "theme": "Teema", + "theme_plural": "Teemat", + "themes": "Teemat", + "uninstall": "Poista", + "update": "Päivitä", + "version": "Versio" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "cancel": "Peruuta", + "generic": "Oletko varma?", + "no": "Ei", + "ok": "Okei", + "yes": "Kyllä" + }, + "dialog_about": { + "frontend_version": "Frontend-versio", + "installed_repositories": "Asennetut repot", + "useful_links": "Hyödyllisiä linkkejä" + }, + "dialog_add_repo": { + "sort_by": "Järjestä", + "title": "Lisää repo" + }, + "dialog_custom_repositories": { + "category": "Kategoria", + "no_category": "Puuttuva kategoria", + "no_repository": "Puuttuva repo" + }, + "dialog_info": { + "author": "Luoja", + "downloads": "Lataukset", + "install": "Asenna tämä HACS:iin", + "loading": "Tietoja ladataan...", + "open_issues": "Avoimet ongelmat", + "open_repo": "Avaa repo", + "stars": "Tähdet", + "version_installed": "Asennettu versio" + }, + "dialog_install": { + "select_version": "Valitse versio", + "show_beta": "Näytä beetaversiot", + "type": "Tyyppi", + "url": "URL" + }, + "dialog_removed": { + "link": "Linkki lisätietoihin", + "name": "Repon nimi", + "reason": "Poiston syy", + "type": "Poiston tyyppi" + }, + "dialog_update": { + "available_version": "Saatavilla oleva versio", + "changelog": "Muutosloki", + "installed_version": "Asennettu versio", + "title": "Päivitys odottaa" + }, + "entry": { + "information": "Tiedot", + "messages": { + "disabled": { + "content": "Tarkista lokitiedostosi saadaksesi lisätietoja", + "title": "HACS on poistettu käytöstä" + }, + "has_pending_tasks": { + "title": "Taustatehtävät vireillä" + }, + "removed": "Poistettu repo '{arkisto}'", + "resources": { + "title": "Ei ladattu Lovelaceen" + }, + "restart": { + "title": "Odottaa uudelleenkäynnistystä" + }, + "startup": { + "title": "HACS käynnistyy" + }, + "wrong_frontend_installed": { + "title": "Odottamaton käyttöliittymäversio" + }, + "wrong_frontend_loaded": { + "title": "Odottamaton käyttöliittymäversio" + } + }, + "pending_updates": "Päivityksiä saatavilla" + }, + "menu": { + "about": "Tietoja HACS:stä", + "clear": "Tyhjennä kaikki uudet", + "custom_repositories": "Mukautetut repot", + "dismiss": "Hylkää kaikki uudet repot", + "documentation": "Dokumentointi", + "open_issue": "Avoin ongelma", + "reload": "Lataa ikkuna uudelleen" + }, + "options": { + "step": { + "user": { + "data": { + "netdaemon": "Ota NetDaemon-sovellusten etsintä ja seuranta käyttöön", + "not_in_use": "Ei käytössä YAML:n kanssa", + "sidepanel_icon": "Sivupaneelin kuvake", + "sidepanel_title": "Sivupaneelin otsikko" + } + } + } + }, + "repository_banner": { + "config_flow_title": "Käyttöliittymän määrityksiä tuetaan" + }, + "repository_card": { + "dismiss": "Hylkää", + "hide": "Piilota", + "information": "Tiedot", + "new_repository": "Uusi repo", + "not_loaded": "Ei ladattu", + "open_issue": "Avoin ongelma", + "open_source": "Avoin lähdekoodi", + "pending_restart": "Odottaa uudelleenkäynnistystä", + "pending_update": "Odottaa päivittämistä", + "reinstall": "Asenna uudelleen", + "report": "Raportoi poistettavaksi", + "update_information": "Päivitä tiedot" + }, + "repository": { + "frontend_version": "Frontend-versio", + "github_stars": "GitHub-tähdet", + "goto_integrations": "Siirry integraatioihin" + }, + "search": { + "installed": "Etsi asennettuja repoja", + "installed_new": "Etsi asennettuja tai uusia repoja", + "placeholder": "Etsi repoa" + }, + "sections": { + "about": { + "title": "Tietoja" + }, + "automation": { + "title": "Automaatio" + }, + "frontend": { + "title": "Frontend" + }, + "integrations": { + "description": "Täältä löydät mukautetut integraatiot (custom_components)", + "title": "integraatiot" + } + }, + "settings": { + "delete": "Poista", + "reload_window": "Lataa ikkuna uudelleen", + "save": "Tallenna" + }, + "store": { + "name": "Nimi", + "no_repositories": "Ei repoja", + "no_repositories_found_desc2": "Kokeile etsiä jotain muuta!", + "pending_upgrades": "Odottaa päivityksiä", + "sort": "järjestä", + "stars": "Tähdet", + "status": "Tila" + }, + "time": { + "day": "päivä", + "days": "päivää", + "hour": "tunti", + "hours": "tuntia", + "minute": "minuutti", + "minutes": "minuuttia", + "month": "kuukausi", + "months": "kuukautta", + "one": "Yksi", + "one_day_ago": "yksi päivä sitten", + "one_hour_ago": "tunti sitten", + "one_minute_ago": "minuutti sitten", + "one_month_ago": "kuukausi sitten", + "one_second_ago": "sekunti sitten", + "one_year_ago": "vuosi sitten", + "second": "sekunti", + "seconds": "sekuntia", + "x_days_ago": "{x} päivää sitten", + "x_hours_ago": "{x} tuntia sitten", + "x_minutes_ago": "{x} minuuttia sitten", + "x_months_ago": "{x} kuukautta sitten", + "x_seconds_ago": "{x} sekuntia sitten", + "x_years_ago": "{x} vuotta sitten", + "year": "vuosi", + "years": "vuotta" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/fr.json b/custom_components/hacs/translations/fr.json new file mode 100644 index 0000000..1dc0197 --- /dev/null +++ b/custom_components/hacs/translations/fr.json @@ -0,0 +1,383 @@ +{ + "common": { + "about": "À propos de", + "add": "ajouter", + "appdaemon": "AppDaemon", + "appdaemon_apps": "Applications AppDaemon", + "appdaemon_plural": "AppDaemon Apps", + "background_task": "Tache de fond en cours, cette page se rechargera une fois terminée", + "cancel": "Annuler", + "check_log_file": "Consultez votre fichier journal pour plus de détails.", + "continue": "Continuer", + "disabled": "Désactivé", + "documentation": "Documentation", + "element": "élément", + "hacs_is_disabled": "HACS est désactivé", + "ignore": "Ignorer", + "install": "Installer", + "installed": "installés", + "integration": "Intégration", + "integration_plural": "Intégrations", + "integrations": "Intégrations", + "lovelace": "Lovelace", + "lovelace_element": "Élément Lovelace", + "lovelace_elements": "Éléments Lovelace", + "manage": "gérer", + "netdaemon": "NetDaemon", + "netdaemon_apps": "Applications NetDaemon", + "netdaemon_plural": "NetDaemon Apps", + "plugin": "Lovelace", + "plugin_plural": "Éléments Lovelace", + "plugins": "Éléments Lovelace", + "python_script": "Script Python", + "python_script_plural": "Scripts Python", + "python_scripts": "Scripts Python", + "reload": "Recharger", + "repositories": "Dépôts", + "repository": "Dépôt", + "settings": "paramètres", + "theme": "Thème", + "theme_plural": "Thèmes", + "themes": "Thèmes", + "uninstall": "Désinstaller", + "update": "Mettre à jour", + "version": "Version" + }, + "config": { + "abort": { + "single_instance_allowed": "Une seule configuration de HACS est autorisée." + }, + "error": { + "acc": "Vous devez accepter toutes les déclarations avant de continuer", + "auth": "Le jeton personnel d'accès est invalide." + }, + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Êtes-vous sûr de vouloir l'ajouter à vos ressources Lovelace ?", + "bg_task": "L'action est désactivée pendant l'exécution de tâches en arrière-plan.", + "cancel": "Annuler", + "continue": "Es-tu sur de vouloir continuer?", + "delete": "Êtes-vous sûr de vouloir supprimer '{item}'?", + "delete_installed": "'{item}' est installé, vous devez le désinstaller avant de pouvoir le supprimer.", + "exist": "{item} existe déjà", + "generic": "Êtes-vous sûr?", + "home_assistant_is_restarting": "Attendez, Home Assistant redémarre maintenant.", + "home_assistant_version_not_correct": "Vous utilisez la version '{haversion}' de Home Assistant, mais ce référentiel nécessite l'installation de la version minimale '{minversion}'.", + "no": "Non", + "no_upgrades": "Aucune mise à niveau en attente", + "ok": "OK", + "overwrite": "En faisant cela, cela l'écrasera.", + "reload_data": "Cela recharge les données de tous les dépôts dont HACS a connaissance, cela prendra un certain temps à terminer.", + "restart_home_assistant": "Voulez-vous vraiment redémarrer Home Assistant ?", + "uninstall": "Êtes-vous sûr de vouloir désinstaller '{item}'?", + "upgrade_all": "Cela mettra à niveau tous ces dépôts, assurez-vous d'avoir lu les notes de publication pour chacun d'entre eux avant de continuer.", + "yes": "Oui" + }, + "dialog_about": { + "frontend_version": "Version frontend", + "installed_repositories": "Dépôts installés", + "integration_version": "Version d'intégration", + "useful_links": "Liens utiles" + }, + "dialog_add_repo": { + "limit": "Seuls les 100 premiers dépôts sont affichés, utilisez la recherche pour filtrer ce dont vous avez besoin", + "no_match": "Aucun dépôt trouvé correspondant à votre filtre", + "sort_by": "Trier par", + "title": "Ajouter un dépôt" + }, + "dialog_custom_repositories": { + "category": "Catégorie", + "no_category": "Catégorie manquante", + "no_repository": "Dépôt manquant", + "title": "Dépôts personnalisés", + "url_placeholder": "Ajouter une URL de dépôt personnalisée" + }, + "dialog_info": { + "author": "Auteur", + "downloads": "Téléchargements", + "install": "Installer ce dépôt dans HACS", + "loading": "Chargement des informations ...", + "no_info": "Le développeur n'a pas fourni plus d'informations pour ce dépôt", + "open_issues": "Open issues", + "open_repo": "Ouvrir dépôt", + "stars": "Étoiles", + "version_installed": "Version installée" + }, + "dialog_install": { + "restart": "N'oubliez pas que vous devez redémarrer Home Assistant avant que les modifications aux intégrations (custom_components) soient appliquées.", + "select_version": "Sélectionnez une version", + "show_beta": "Afficher les versions bêta", + "type": "Type", + "url": "URL" + }, + "dialog_removed": { + "link": "Lien externe vers plus d'informations", + "name": "Nom du dépôt", + "reason": "Raison de la suppression", + "type": "Type de suppression" + }, + "dialog_update": { + "available_version": "Version disponible", + "changelog": "Changelog", + "installed_version": "Version installée", + "releasenotes": "Notes de version pour {release}", + "title": "Mise à jour en attente" + }, + "dialog": { + "reload": { + "confirm": "Voulez-vous faire cela maintenant ?", + "description": "Vous devez vider le cache de votre navigateur lors de la modification des ressources Lovelace." + } + }, + "entry": { + "information": "Information", + "intro": "Les mises à jour et les messages importants s'afficheront ici s'il y en a", + "messages": { + "disabled": { + "content": "Vérifiez votre fichier journal pour plus de détails", + "title": "HACS est désactivé" + }, + "has_pending_tasks": { + "content": "Certains dépôts peuvent ne pas apparaître tant que cette opération n'est pas terminée", + "title": "Tâches d’arrière-plan en attente" + }, + "removed": "Dépôt '{repository}' supprimé", + "resources": { + "content": "Vous avez {number} éléments Lovelace qui ne sont pas chargés correctement dans Lovelace.", + "title": "Non chargé dans Lovelace" + }, + "restart": { + "content": "Vous disposez de {number} intégrations qui nécessitent un redémarrage de Home Assistant, vous pouvez le faire à partir de la section \"Contrôle du serveur\" dans la partie configuration de l'interface utilisateur de Home Assistant.", + "title": "En attente de redémarrage" + }, + "setup": { + "content": "HACS est en cours de configuration, pendant ce temps, certaines informations peuvent être manquantes ou incorrectes", + "title": "HACS se met en place" + }, + "startup": { + "content": "HACS démarre, pendant ce temps, certaines informations peuvent être manquantes ou incorrectes", + "title": "HACS est en train de démarrer" + }, + "waiting": { + "content": "HACS attend que Home Assistant termine le démarrage avant de démarrer les tâches de démarrage", + "title": "HACS attend" + }, + "wrong_frontend_installed": { + "content": "Vous avez {running} du frontend HACS installé, mais la version {expected} était attendue. Si ce message s'affiche, Home Assistant n'a pas pu installer la nouvelle version, essayez de redémarrer Home Assistant.", + "title": "Version frontend inattendue" + }, + "wrong_frontend_loaded": { + "content": "Vous exécutez la version {running} de l'interface HACS, mais la version {expected} était attendue, vous devez vider le cache de votre navigateur.", + "title": "Version frontend inattendue" + } + }, + "pending_updates": "Mises à jour en attente" + }, + "menu": { + "about": "À propos de HACS", + "clear": "Effacer tous les nouveaux", + "custom_repositories": "Dépôts personnalisés", + "dismiss": "Rejeter tous les nouveaux dépôts", + "documentation": "Documentation", + "open_issue": "Open issue", + "reload": "Recharger la fenêtre" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Activer la découverte et le suivi des applications AppDaemon", + "country": "Filtrer par code pays.", + "debug": "Activer le débogage.", + "experimental": "Activer les fonctionnalités expérimentales", + "netdaemon": "Activer la découverte et le suivi des applications NetDaemon", + "not_in_use": "Non utilisé avec YAML", + "release_limit": "Nombre de recensés à afficher.", + "sidepanel_icon": "Icône de la barre latérale", + "sidepanel_title": "Titre de la barre latérale" + } + } + } + }, + "repository_banner": { + "config_flow": "Cette intégration prend en charge config_flow, ce qui signifie que vous pouvez maintenant accéder à la section d'intégration de votre interface utilisateur pour le configurer.", + "config_flow_title": "Configuration de l'interface utilisateur prise en charge", + "integration_not_loaded": "Cette intégration n'est pas chargée dans Home Assistant.", + "no_restart_required": "Aucun redémarrage requis", + "not_loaded": "Non chargé", + "plugin_not_loaded": "Ce plugin n'est pas ajouté à vos ressources Lovelace.", + "restart": "Vous devez redémarrer Home Assistant.", + "restart_pending": "Redémarrage en attente" + }, + "repository_card": { + "dismiss": "rejeter", + "hide": "Cacher", + "information": "Information", + "new_repository": "Nouveau dépôt", + "not_loaded": "Non chargé", + "open_issue": "Ouvrir issue", + "open_source": "Open source", + "pending_restart": "Redémarrage en attente", + "pending_update": "Mise à jour en attente", + "reinstall": "Réinstaller", + "report": "Rapport de suppression", + "update_information": "Mettre à jour les informations" + }, + "repository": { + "add_to_lovelace": "Ajouter à Lovelace", + "authors": "Auteurs", + "available": "Disponible", + "back_to": "Retour", + "changelog": "Change log", + "downloads": "Téléchargements", + "flag_this": "Marquer", + "frontend_version": "Version de l'interface", + "github_stars": "Étoiles GitHub", + "goto_integrations": "Aller aux intégrations", + "hide": "Masquer", + "hide_beta": "Masquer les bêta", + "install": "Installer", + "installed": "Installés", + "lovelace_copy_example": "Copier cet exemple dans le presse-papier", + "lovelace_instruction": "Quand vous l'ajoutez à votre configuration lovelace, utilisez", + "lovelace_no_js_type": "Impossible de déterminer le type de plugin, veuillez vérifier le dépôt", + "newest": "nouveau", + "note_appdaemon": "vous devez toujours l'ajouter à votre fichier 'apps.yaml'", + "note_installed": "Une fois installé, il se trouvera dans", + "note_integration": "Vous devez toujours l'ajouter à votre fichier 'configuration.yaml'", + "note_plugin": "Vous devez toujours l'ajouter à votre configuration lovelace ('ui-lovelace.yaml' ou l'éditeur de configuration de l'interface)", + "note_plugin_post_107": "Vous devez toujours l'ajouter à votre configuration lovelace ('configuration.yaml' ou l'éditeur de configuration de l'interface '\/config\/lovelace\/resources')", + "open_issue": "Ouvrir un ticket", + "open_plugin": "Ouvrir l'élément", + "reinstall": "Réinstaller", + "repository": "Dépôt", + "restart_home_assistant": "Redémarrer Home Assistant", + "show_beta": "Afficher les bêta", + "uninstall": "Désinstaller", + "update_information": "Mettre à jour les informations", + "upgrade": "Mettre à jour" + }, + "search": { + "installed": "Rechercher des dépôts installés", + "installed_new": "Rechercher des dépôts installés ou nouveaux", + "placeholder": "Rechercher un dépôt" + }, + "sections": { + "about": { + "description": "Afficher des informations sur le système HACS", + "title": "À propos de" + }, + "addon": { + "description": "Il n'y a pas de modules complémentaires dans HACS, mais vous pouvez cliquer ici pour aller au superviseur", + "title": "Modules complémentaires" + }, + "automation": { + "description": "C'est ici que vous trouverez les scripts python, les applications AppDaemon et NetDaemon", + "title": "Automatisation" + }, + "frontend": { + "description": "C'est ici que vous trouverez des thèmes, des cartes personnalisées et d'autres éléments pour lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "C'est ici que vous trouverez les intégrations personnalisées (custom_components)", + "title": "Intégrations" + }, + "pending_repository_upgrade": "Vous utilisez la version {installed} , la version {available} est disponible" + }, + "settings": { + "add_custom_repository": "AJOUTER UN DÉPÔT PERSONNALISÉ", + "adding_new_repo": "Ajout d'un nouveau dépôt '{repo}'", + "adding_new_repo_category": "Avec la catégorie '{category}'.", + "bg_task_custom": "Les dépôts personnalisés sont masqués pendant l'exécution de tâches en arrière-plan.", + "category": "Catégorie", + "compact_mode": "Mode compact", + "custom_repositories": "DÉPÔTS PERSONNALISÉS", + "delete": "Supprimer", + "display": "Affichage", + "grid": "Grille", + "hacs_repo": "Dépôt HACS", + "hidden_repositories": "dépôts cachés", + "missing_category": "Vous devez sélectionner une catégorie", + "open_repository": "Ouvrir dépôt", + "reload_data": "Recharger les données", + "reload_window": "Recharger la fenêtre", + "repository_configuration": "Configuration de dépôt", + "save": "Enregistrer", + "table": "Tableau", + "table_view": "Vue table", + "unhide": "Afficher", + "upgrade_all": "Tout mettre à jour" + }, + "store": { + "ascending": "ascendant", + "clear_new": "Effacer tous les nouveaux dépôts", + "descending": "descendant", + "last_updated": "Dernière mise à jour", + "name": "Nom", + "new_repositories": "Nouveaux dépôts", + "new_repositories_note": "Vous avez plus de 10 nouveaux dépôts qui apparaissent ici. Si vous voulez les effacer tous, cliquez sur les 3 points en haut à droite de l'écran et ignorez-les tous.", + "no_repositories": "Aucun dépôt", + "no_repositories_desc1": "Il semble que vous n’avez pas de dépôts installés dans cette section encore.", + "no_repositories_desc2": "Cliquez sur le + dans le coin inférieur pour ajouter votre premier !", + "no_repositories_found_desc1": "Aucun référentiel installé correspondant à \" {searchInput} \" n'a été trouvé dans cette section.", + "no_repositories_found_desc2": "Essayez de chercher autre chose !", + "pending_upgrades": "Mises à niveau en attente", + "placeholder_search": "Veuillez entrer un terme de recherche...", + "sort": "Trier", + "stars": "Étoiles", + "status": "Statut" + }, + "time": { + "ago": "il y a", + "day": "jour", + "days": "jours", + "hour": "heure", + "hours": "heures", + "minute": "minute", + "minutes": "minutes", + "month": "mois", + "months": "mois", + "one": "Un", + "one_day_ago": "il y a 1 jour", + "one_hour_ago": "il y a 1 heure", + "one_minute_ago": "il y a 1 minute", + "one_month_ago": "il y a 1 mois", + "one_second_ago": "il y a 1 seconde", + "one_year_ago": "il y a 1 an", + "second": "seconde", + "seconds": "secondes", + "x_days_ago": "il y a {x} jours", + "x_hours_ago": "il y a {x} heures", + "x_minutes_ago": "il y a {x} minutes", + "x_months_ago": "il y a {x} mois", + "x_seconds_ago": "il y a {x} secondes", + "x_years_ago": "il y a {x} ans", + "year": "an", + "years": "ans" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/hu.json b/custom_components/hacs/translations/hu.json new file mode 100644 index 0000000..013f0dd --- /dev/null +++ b/custom_components/hacs/translations/hu.json @@ -0,0 +1,378 @@ +{ + "common": { + "about": "Névjegy", + "add": "hozzáadás", + "appdaemon": "AppDaemon", + "appdaemon_apps": "AppDaemon Appok", + "appdaemon_plural": "AppDaemon appok", + "background_task": "Éppen háttérfeladat fut, ez az oldal frissülni fog, ha kész.", + "cancel": "Mégse", + "check_log_file": "További részletekért ellenőrizd a naplófájlt.", + "continue": "Folytatás", + "disabled": "Tiltva", + "documentation": "Dokumentáció", + "element": "bővítmény", + "hacs_is_disabled": "A HACS le van tiltva", + "ignore": "Mellőzés", + "install": "Telepítés", + "installed": "Telepített", + "integration": "Integráció", + "integration_plural": "Integrációk", + "integrations": "Integrációk", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace bővítmény", + "lovelace_elements": "Lovelace bővítmények", + "manage": "kezelés", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon Appok", + "netdaemon_plural": "NetDaemon appok", + "plugin": "Lovelace", + "plugin_plural": "Lovelace bővítmények", + "plugins": "Lovelace bővítmények", + "python_script": "Python Szkript", + "python_script_plural": "Python szkriptek", + "python_scripts": "Python Szkriptek", + "reload": "Újratöltés", + "repositories": "Tárolók", + "repository": "Tároló", + "settings": "beállítások", + "theme": "Téma", + "theme_plural": "Témák", + "themes": "Témák", + "uninstall": "Eltávolítás", + "update": "Frissítés", + "version": "Verzió" + }, + "config": { + "abort": { + "single_instance_allowed": "Csak egyetlen HACS konfiguráció megengedett." + }, + "error": { + "acc": "El kell fogadnod minden nyilatkozatot a folytatás előtt", + "auth": "A személyes hozzáférési token nem megfelelő" + }, + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Biztosan hozzá szeretnéd ezt adni a Lovelace erőforrásokhoz?", + "bg_task": "A művelet le van tiltva, amíg háttérfeladat fut.", + "cancel": "Mégse", + "continue": "Biztosan folytatni szeretnéd?", + "delete": "Biztosan törölni szeretnéd a(z) '{item}'-t?", + "delete_installed": "A(z) '{item}' telepítve van, törlés előtt el kell távolítanod.", + "exist": "{item} már létezik", + "generic": "Biztos vagy benne?", + "home_assistant_is_restarting": "Várj, a Home Assistant éppen újraindul.", + "home_assistant_version_not_correct": "A Home Assistant '{haversion}' verzióját használod, de ehhez a tárolóhoz legalább a(z) '{minversion}' verzióra van szükség.", + "no": "Nem", + "no_upgrades": "Nincsenek elérhető frissítések", + "ok": "OK", + "overwrite": "Ezzel felül fogod írni.", + "reload_data": "Ez újratölti a HACS által ismert összes tároló adatát, ami némi időbe telhet.", + "restart_home_assistant": "Biztosan újraindítod a Home Assistant programot?", + "uninstall": "Biztosan el szeretnéd távolítani a(z) '{item}'-t?", + "upgrade_all": "Ez frissíteni fogja az összes tárolót. Győzödj meg róla, hogy elolvastad az összes kiadási megjegyzést, mielőtt továbblépnél.", + "yes": "Igen" + }, + "dialog_about": { + "frontend_version": "Frontend verzió", + "installed_repositories": "Telepített tárolók", + "integration_version": "Integráció verzió", + "useful_links": "Hasznos linkek" + }, + "dialog_add_repo": { + "limit": "Csak az első 100 tároló jelenik meg, használd a keresőt a találatok szűkítéséhez", + "no_match": "Nincs a szűrésnek megfelelő tároló", + "sort_by": "Rendezés", + "title": "Tároló hozzáadása" + }, + "dialog_custom_repositories": { + "category": "Kategória", + "no_category": "Hiányzó kategória", + "no_repository": "Hiányzó tároló", + "title": "Egyéni tárolók", + "url_placeholder": "Egyéni tároló URL címének hozzáadása" + }, + "dialog_info": { + "author": "Szerző", + "downloads": "Letöltések", + "install": "Tároló telepítése HACS-ben", + "loading": "Információ betöltése...", + "no_info": "A fejlesztő nem adott meg több információt ehhez a tárolóhoz", + "open_issues": "Jelentett problémák", + "open_repo": "Tároló megnyitása", + "stars": "Csillagok", + "version_installed": "Telepített verzió" + }, + "dialog_install": { + "restart": "Ne feledd, hogy az egyéni integrációk (custom_components) módosításainak alkalmazásához újra kell indítanod a Home Assistant alkalmazást.", + "select_version": "Verzió kiválasztása", + "show_beta": "Béta verziók megjelenítése", + "type": "Típus", + "url": "URL" + }, + "dialog_removed": { + "link": "Külső link további információkhoz", + "name": "Tároló neve", + "reason": "Eltávolítás oka", + "type": "Eltávolítás típusa" + }, + "dialog_update": { + "available_version": "Elérhető verzió", + "changelog": "Változási napló", + "installed_version": "Telepített verzió", + "releasenotes": "{release} kiadási megjegyzései", + "title": "Frissítés érhető el" + }, + "dialog": { + "reload": { + "confirm": "Meg szeretnéd most ezt tenni?", + "description": "Törölnöd kell a böngésződ gyorsítótárát a Lovelace erőforrások módosításakor." + } + }, + "entry": { + "information": "Információ", + "intro": "A frissítések és a fontos üzenetek itt jelennek meg, ha vannak", + "messages": { + "disabled": { + "content": "További részletek a naplófájlban", + "title": "A HACS le van tiltva" + }, + "has_pending_tasks": { + "content": "Előfordulhat, hogy egyes tárolók nem jelennek meg, amíg ez be nem fejeződik", + "title": "Függőben lévő háttérfeladatok" + }, + "removed": "Eltávolított tároló '{repository}'", + "resources": { + "title": "Nincs betöltve a Lovelace-ben" + }, + "restart": { + "title": "Újraindításra vár" + }, + "setup": { + "content": "A HACS beállítása folyamatban van, ez idő alatt bizonyos információk hiányozhatnak vagy helytelenek lehetnek", + "title": "A HACS beállítása folyamatban van" + }, + "startup": { + "content": "A HACS éppen indul, ez idő alatt bizonyos információk hiányozhatnak vagy helytelenek lehetnek", + "title": "A HACS éppen indul" + }, + "waiting": { + "content": "A HACS az indítási feladatok megkezdése előtt arra vár, hogy a Home Assistant befejezze az indulást", + "title": "A HACS vár" + }, + "wrong_frontend_installed": { + "title": "Nem várt frontend verzió" + }, + "wrong_frontend_loaded": { + "title": "Nem várt frontend verzió" + } + }, + "pending_updates": "Frissítések érhetők el" + }, + "menu": { + "about": "HACS névjegye", + "clear": "Új jelölések törlése", + "custom_repositories": "Egyéni tárolók", + "dismiss": "Minden új tároló elvetése", + "documentation": "Dokumentáció", + "open_issue": "Probléma jelentése", + "reload": "Ablak újratöltése" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "AppDaemon appok felfedezésének és nyomon követésének engedélyezése", + "country": "Szűrés országkóddal.", + "debug": "Hibakeresés engedélyezése.", + "experimental": "Kísérleti funkciók engedélyezése", + "netdaemon": "NetDaemon appok felfedezésének és nyomon követésének engedélyezése", + "not_in_use": "YAML-lel nem használható", + "release_limit": "Megjelenítendő kiadások száma.", + "sidepanel_icon": "Oldalsó panel ikon", + "sidepanel_title": "Oldalsó panel cím" + } + } + } + }, + "repository_banner": { + "config_flow": "Ez az integráció támogatja a config_flow-t, vagyis a felhasználói felületen az integrációk menüben lehet konfigurálni.", + "config_flow_title": "A felhasználói felület konfigurációja támogatott", + "integration_not_loaded": "Ez az integráció nincs betöltve a Home Assistantban.", + "no_restart_required": "Nincs szükség újraindításra", + "not_loaded": "Nincs betöltve", + "plugin_not_loaded": "Ez a bővítmény nincs hozzáadva a Lovelace erőforrásaidhoz.", + "restart": "Indítsd újra a Home Assistant programot.", + "restart_pending": "Újraindítás függőben" + }, + "repository_card": { + "dismiss": "elvetés", + "hide": "Elrejtés", + "information": "Információ", + "new_repository": "Új tároló", + "not_loaded": "Nincs betöltve", + "open_issue": "Probléma jelentése", + "open_source": "Forrás megnyitása", + "pending_restart": "Újraindításra vár", + "pending_update": "Frissítés érhető el", + "reinstall": "Újratelepítés", + "report": "Jelentés eltávolításra", + "update_information": "Frissítési információ" + }, + "repository": { + "add_to_lovelace": "Hozzáadás a Lovelace-hez", + "authors": "Szerzők", + "available": "Elérhető", + "back_to": "Vissza -", + "changelog": "Változási napló", + "downloads": "Letöltések", + "flag_this": "Megjelölés", + "frontend_version": "Frontend verzió", + "github_stars": "GitHub csillagok", + "goto_integrations": "Ugrás az integrációkhoz", + "hide": "Elrejtés", + "hide_beta": "Béta elrejtése", + "install": "Telepítés", + "installed": "Telepített", + "lovelace_copy_example": "Példa másolása a vágólapra", + "lovelace_instruction": "Amikor hozzáadod ezt a lovelace konfigurációdhoz, használd ezt", + "lovelace_no_js_type": "Nem sikerült meghatározni a bővítmény típusát, ellenőrizd a tárolót.", + "newest": "legújabb", + "note_appdaemon": "de még hozzá kell adnod az 'apps.yaml' fájlhoz", + "note_installed": "Telepítéskor a következő helyre kerül:", + "note_integration": "de még hozzá kell adnod a 'configuration.yaml' fájlhoz", + "note_plugin": "de még hozzá kell adnod a lovelace konfigurációhoz (az 'ui-lovelace.yaml' fájlban vagy a Lovelace felületen a konfiguráció szerkesztőben)", + "note_plugin_post_107": "de még hozzá kell adnod a lovelace konfigurációhoz ('configuration.yaml' vagy az erőforrás szerkesztőben '\/config\/lovelace\/resources')", + "open_issue": "Probléma jelentése", + "open_plugin": "Bővítmény megnyitása", + "reinstall": "Újratelepítés", + "repository": "Tároló", + "restart_home_assistant": "Home Assistant újraindítása", + "show_beta": "Béta megjelenítése", + "uninstall": "Eltávolítás", + "update_information": "Frissítési információk", + "upgrade": "Frissítés" + }, + "search": { + "installed": "Telepített tárolók keresése", + "installed_new": "Telepített vagy új tárolók keresése", + "placeholder": "Tároló keresése" + }, + "sections": { + "about": { + "description": "Információk megjelenítése a HACS-ről", + "title": "Névjegy" + }, + "addon": { + "description": "A HACS-ban nincsenek kiegészítők, de ide kattintva eljuthatsz a supervisor-hoz", + "title": "Kiegészítők" + }, + "automation": { + "description": "Itt Python szkripteket, AppDaemon és NetDaemon appokat találsz", + "title": "Automatizálás" + }, + "frontend": { + "description": "Itt témákat, egyéni kártyákat és más bővítményeket találsz a Lovelace-hez", + "title": "Frontend" + }, + "integrations": { + "description": "Itt találod az egyéni integrációkat (custom_components)", + "title": "Integrációk" + }, + "pending_repository_upgrade": "A(z) {installed} verziót futtatod, a(z) {available} verzió már elérhető" + }, + "settings": { + "add_custom_repository": "EGYÉNI TÁROLÓ HOZZÁADÁSA", + "adding_new_repo": "Új tároló hozzáadása '{repo}'", + "adding_new_repo_category": "A '{category}' kategóriával.", + "bg_task_custom": "Az egyéni tárolók rejtve vannak, amíg háttérfeladat fut.", + "category": "Kategória", + "compact_mode": "Kompakt mód", + "custom_repositories": "EGYÉNI TÁROLÓK", + "delete": "Törlés", + "display": "Megjelenítés", + "grid": "Rács", + "hacs_repo": "HACS tároló", + "hidden_repositories": "rejtett tárolók", + "missing_category": "Ki kell választanod egy kategóriát", + "open_repository": "Tároló megnyitása", + "reload_data": "Adatok újratöltése", + "reload_window": "Ablak újratöltése", + "repository_configuration": "Tároló konfiguráció", + "save": "Mentés", + "table": "Táblázat", + "table_view": "Táblázat nézet", + "unhide": "felfedés", + "upgrade_all": "Minden frissítése" + }, + "store": { + "ascending": "növekvő", + "clear_new": "Új tárolók megjelölése látottként", + "descending": "csökkenő", + "last_updated": "Utolsó frissítés", + "name": "Név", + "new_repositories": "Új tárolók", + "new_repositories_note": "Több mint 10 új tároló látható. Ha törölni szeretnéd őket, akkor kattints a jobb felső sarokban lévő 3 pontra, és válaszd ki a 'Minden új tároló elvetése' menüpontot.", + "no_repositories": "Nincsenek tárolók", + "no_repositories_desc1": "Úgy tűnik, még nincsenek telepítve tárolók ebben a szekcióban.", + "no_repositories_desc2": "Kattints az alsó sarokban található + jelre az első hozzáadásához!", + "no_repositories_found_desc2": "Próbálj valami mást keresni!", + "pending_upgrades": "Frissítések érhetők el", + "placeholder_search": "Kérlek adj meg egy keresési kifejezést...", + "sort": "rendezés", + "stars": "Csillag", + "status": "Állapot" + }, + "time": { + "ago": "ezelőtt", + "day": "nap", + "days": "nap", + "hour": "óra", + "hours": "óra", + "minute": "perc", + "minutes": "perc", + "month": "hónap", + "months": "hónap", + "one": "Egy", + "one_day_ago": "egy nappal ezelőtt", + "one_hour_ago": "egy órával ezelőtt", + "one_minute_ago": "egy perccel ezelőtt", + "one_month_ago": "egy hónappal ezelőtt", + "one_second_ago": "egy másodperccel ezelőtt", + "one_year_ago": "egy évvel ezelőtt", + "second": "másodperc", + "seconds": "másodperc", + "x_days_ago": "{x} nappal ezelőtt", + "x_hours_ago": "{x} órával ezelőtt", + "x_minutes_ago": "{x} perccel ezelőtt", + "x_months_ago": "{x} hónappal ezelőtt", + "x_seconds_ago": "{x} másodperccel ezelőtt", + "x_years_ago": "{x} évvel ezelőtt", + "year": "év", + "years": "év" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/it.json b/custom_components/hacs/translations/it.json new file mode 100644 index 0000000..dc376fc --- /dev/null +++ b/custom_components/hacs/translations/it.json @@ -0,0 +1,366 @@ +{ + "common": { + "about": "Informazioni su", + "add": "aggiungi", + "appdaemon": "AppDaemon", + "appdaemon_apps": "Applicazioni AppDaemon", + "appdaemon_plural": "Applicazioni AppDaemon", + "background_task": "Attività in esecuzione, questa pagina sarà ricaricata al termine.", + "cancel": "Annulla", + "check_log_file": "Controlla il tuo file di registro per maggiori dettagli.", + "continue": "Continua", + "disabled": "Disabilitato", + "documentation": "Documentazione", + "element": "elemento", + "hacs_is_disabled": "HACS è disabilitato", + "install": "Installa", + "installed": "Installati", + "integration": "Integrazione", + "integration_plural": "Integrazioni", + "integrations": "Integrazioni", + "lovelace": "Lovelace", + "lovelace_element": "Elemento Lovelace", + "lovelace_elements": "Elementi di Lovelace", + "manage": "gestione", + "netdaemon": "NetDaemon", + "netdaemon_apps": "Applicazioni NetDaemon", + "netdaemon_plural": "Applicazioni NetDaemon", + "plugin": "Lovelace", + "plugin_plural": "Elementi di Lovelace", + "plugins": "Elementi di Lovelace", + "python_script": "Script python", + "python_script_plural": "Script in Python", + "python_scripts": "Script python", + "reload": "Ricarica", + "repositories": "Repository", + "repository": "Repository", + "settings": "Impostazioni", + "theme": "Tema", + "theme_plural": "Temi", + "themes": "Temi", + "uninstall": "Disinstallare", + "update": "Aggiorna", + "version": "Versione" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + } + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Sei sicuro di voler aggiungerlo alle tue risorse Lovelace?", + "bg_task": "L'azione è disabilitata mentre sono in esecuzione attività in background.", + "cancel": "Annulla", + "continue": "Sei sicuro di voler continuare?", + "delete": "Sei sicuro di voler disinstallare '{item}'?", + "delete_installed": "'{item}' è installato, è necessario disinstallarlo prima di poterlo eliminare.", + "exist": "{item} esiste già", + "generic": "Sei sicuro?", + "home_assistant_is_restarting": "Aspetta, Home Assistant si sta riavviando.", + "home_assistant_version_not_correct": "Stai eseguendo la versione Home Assistant '{haversion}', ma questo repository richiede l'installazione della versione minima '{minversion}'.", + "no": "No", + "no_upgrades": "Nessun aggiornamento in sospeso", + "ok": "OK", + "overwrite": "In questo modo lo sovrascriverà.", + "reload_data": "Questo ricarica i dati di tutte le repository di cui HACS è a conoscenza, ci vorrà del tempo per finire.", + "restart_home_assistant": "Sei sicuro di voler riavviare Home Assistant?", + "uninstall": "Sei sicuro di voler disinstallare '{item}'?", + "upgrade_all": "Questa azione aggiornerà tutti i repository, assicurati di aver letto le note di rilascio prima di procedere.", + "yes": "Sì" + }, + "dialog_about": { + "frontend_version": "Versione frontend", + "installed_repositories": "Repository installati", + "integration_version": "Versione di integrazione", + "useful_links": "Link utili" + }, + "dialog_add_repo": { + "limit": "Vengono visualizzati solo i primi 100 repository, utilizza la ricerca per filtrare ciò di cui hai bisogno", + "no_match": "Nessun repository trovato corrispondente al filtro", + "sort_by": "Ordina per", + "title": "Aggiungi repository" + }, + "dialog_custom_repositories": { + "category": "Categoria", + "no_category": "Categoria mancante", + "no_repository": "Repository mancante", + "title": "Repository personalizzati", + "url_placeholder": "Aggiungi l'URL del repository personalizzato" + }, + "dialog_info": { + "author": "Autore", + "downloads": "Download", + "install": "Installa questo repository in HACS", + "loading": "Caricamento informazioni in corso ...", + "no_info": "Lo sviluppatore non ha fornito ulteriori informazioni per questo repository", + "open_issues": "Problemi aperti", + "open_repo": "Apri il repository", + "stars": "Stelle", + "version_installed": "Versione installata" + }, + "dialog_install": { + "restart": "Tenere presente che è necessario riavviare Home Assistant prima di applicare le modifiche alle integrazioni (custom_components).", + "select_version": "Seleziona la versione", + "show_beta": "Mostra le versioni beta", + "type": "Tipo", + "url": "URL" + }, + "dialog_update": { + "available_version": "Versione disponibile", + "changelog": "Registro dei cambiamenti", + "installed_version": "Versione installata", + "releasenotes": "Note di rilascio per {release}", + "title": "Aggiornamento in sospeso" + }, + "dialog": { + "reload": { + "confirm": "Vuoi farlo adesso?", + "description": "È necessario cancellare la cache del browser quando si modificano le risorse di Lovelace." + } + }, + "entry": { + "information": "Informazioni", + "intro": "Gli aggiornamenti e i messaggi importanti verranno visualizzati qui se presenti", + "messages": { + "disabled": { + "content": "Controlla il tuo file di registro per maggiori dettagli", + "title": "HACS è disabilitato" + }, + "has_pending_tasks": { + "content": "Alcuni repository potrebbero non essere visualizzati fino al completamento", + "title": "Attività in background in sospeso" + }, + "resources": { + "content": "Hai {number} elementi di Lovelace che non sono stati caricati correttamente in Lovelace.", + "title": "Non caricato in Lovelace" + }, + "restart": { + "content": "Hai {number} integrazioni che richiedono il riavvio di Home Assistant, puoi farlo dalla sezione \"Controlli del server\" nella sezione Impostazioni dell'interfaccia utente di Home Assistant.", + "title": "In attesa di riavvio" + }, + "setup": { + "content": "HACS è in fase di configurazione, durante questo periodo alcune informazioni potrebbero essere mancanti o errate", + "title": "HACS si sta installando" + }, + "startup": { + "content": "HACS si sta avviando, durante questo periodo alcune informazioni potrebbero essere mancanti o errate", + "title": "HACS si sta avviando" + }, + "waiting": { + "content": "HACS è in attesa che Home Assistant finisca l'avvio prima di iniziare le attività di avvio", + "title": "HACS è in attesa" + }, + "wrong_frontend_installed": { + "content": "Hai installato {running} del frontend HACS, ma era attesa la versione {expected}, se viene visualizzato questo messaggio Home Assistant non è stato in grado di installare la nuova versione, prova a riavviare Home Assistant.", + "title": "Versione frontend inaspettata" + }, + "wrong_frontend_loaded": { + "content": "Stai eseguendo la versione {running} del frontend HACS, ma la versione {expected} era prevista, è necessario svuotare la cache del browser.", + "title": "Versione frontend inaspettata" + } + }, + "pending_updates": "Aggiornamenti in sospeso" + }, + "menu": { + "about": "Informazioni su HACS", + "clear": "Cancella tutte le novità", + "custom_repositories": "Repository personalizzati", + "dismiss": "Elimina tutti i nuovi repository", + "documentation": "Documentazione", + "open_issue": "Segnala anomalia", + "reload": "Ricarica la finestra" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni AppDaemon", + "country": "Filtra con prefisso internazionale.", + "debug": "Abilita debug.", + "experimental": "Abilita funzionalità sperimentali", + "netdaemon": "Abilita il rilevamento e il monitoraggio delle applicazioni NetDaemon", + "not_in_use": "Non in uso con YAML", + "release_limit": "Numero di versioni da mostrare.", + "sidepanel_icon": "Icona nel pannello laterale", + "sidepanel_title": "Titolo nel pannello laterale" + } + } + } + }, + "repository_banner": { + "config_flow": "Questa integrazione supporta config_flow, questo significa che è ora possibile passare alla sezione \"IntegrazionI\" dell'interfaccia utente per la configurazione.", + "config_flow_title": "Configurazione dell'interfaccia utente supportata", + "integration_not_loaded": "Questa integrazione non è caricata in Home Assistant.", + "no_restart_required": "Non è necessario riavviare", + "not_loaded": "Non caricato", + "plugin_not_loaded": "Questo elemento non è aggiunto alle tue risorse Lovelace.", + "restart": "È necessario riavviare Home Assistant.", + "restart_pending": "Riavvio in attesa" + }, + "repository_card": { + "dismiss": "elimina", + "hide": "Nascondi", + "information": "Informazioni", + "new_repository": "Nuovo repository", + "not_loaded": "Non caricato", + "open_issue": "Segnala anomalia", + "open_source": "Open source", + "pending_restart": "In attesa di riavvio", + "pending_update": "Aggiornamento in sospeso", + "reinstall": "Reinstallare", + "report": "Segnala per la rimozione", + "update_information": "Aggiorna informazioni" + }, + "repository": { + "add_to_lovelace": "Aggiungi a Lovelace", + "authors": "Autori", + "available": "Disponibile", + "back_to": "Torna a", + "changelog": "Change log", + "downloads": "Downloads", + "flag_this": "Spunta questo", + "frontend_version": "Frontend versione", + "github_stars": "GitHub stelle", + "goto_integrations": "Vai alle Integrazioni", + "hide": "Nascondi", + "hide_beta": "Nascondi beta", + "install": "Installa", + "installed": "Installato", + "lovelace_copy_example": "Copia l'esempio negli appunti", + "lovelace_instruction": "Quando lo aggiungi nella configurazione di Lovelace, usa questo", + "lovelace_no_js_type": "Impossibile determinare il tipo di questo elemento, controllare il repository.", + "newest": "Più recente", + "note_appdaemon": "dovrai aggiungerlo nel file 'apps.yaml'", + "note_installed": "Una volta installato, si troverà in", + "note_integration": "dovrai aggiungerlo nel file 'configuration.yaml'", + "note_plugin": "è comunque necessario aggiungerlo alla configurazione Lovelace ('ui-lovelace.yaml' o nell'editor grezzo di configurazione dell'Interfaccia Utente)", + "note_plugin_post_107": "è comunque necessario aggiungerlo alla configurazione di Lovelace ('configuration.yaml' o nell'editor di risorse '\/config\/lovelace\/resources')", + "open_issue": "Segnala anomalia", + "open_plugin": "Apri elemento", + "reinstall": "Reinstalla", + "repository": "Repository", + "restart_home_assistant": "Riavvia Home Assistant", + "show_beta": "Visualizza beta", + "uninstall": "Rimuovi", + "update_information": "Aggiorna informazioni", + "upgrade": "Aggiorna" + }, + "search": { + "installed": "Cerca i repository installati", + "installed_new": "Cercare repository installati o nuovi", + "placeholder": "Cerca repository" + }, + "sections": { + "about": { + "description": "Mostra informazioni su HACS", + "title": "Informazioni su" + }, + "addon": { + "description": "Non ci sono componenti aggiuntivi in HACS, ma puoi fare clic qui per accedere al supervisore", + "title": "Componenti aggiuntivi" + }, + "automation": { + "description": "Qui trovi python_scripts, app AppDaemon e app NetDaemon", + "title": "Automazione" + }, + "frontend": { + "description": "Qui troverai temi, schede personalizzate e altri elementi per Lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Qui si trovano le integrazioni personalizzate (custom_components)", + "title": "Integrazioni" + }, + "pending_repository_upgrade": "Stai eseguendo la versione {installed}, la versione {available}è disponibile" + }, + "settings": { + "add_custom_repository": "AGGIUNGI REPOSITORY PERSONALIZZATA", + "adding_new_repo": "Aggiunta di un nuovo repository '{repo}'", + "adding_new_repo_category": "Con la categoria '{category}'.", + "bg_task_custom": "I repository personalizzati sono nascosti mentre sono in esecuzione attività in background.", + "category": "Categoria", + "compact_mode": "Modalità compatta", + "custom_repositories": "REPOSITORY PERSONALIZZATE", + "delete": "Cancella", + "display": "Visualizza", + "grid": "Griglia", + "hacs_repo": "HACS repo", + "hidden_repositories": "repository nascosti", + "missing_category": "Devi selezionare una categoria", + "open_repository": "Apri il repository", + "reload_data": "Ricarica i dati", + "reload_window": "Ricarica la finestra", + "repository_configuration": "Configurazione del repository", + "save": "Salva", + "table": "Tabella", + "table_view": "Vista tabella", + "unhide": "Mostra", + "upgrade_all": "Aggiorna tutto" + }, + "store": { + "ascending": "ascendente", + "clear_new": "Ripulisci i nuovi repository", + "descending": "discendente", + "last_updated": "Ultimo aggiornamento", + "name": "Nome", + "new_repositories": "Nuovi repository", + "new_repositories_note": "Hai più di 10 nuovi repository visualizzati qui, se vuoi cancellarli tutti fai clic sui 3 punti nell'angolo in alto a destra e li eliminerai tutti.", + "no_repositories": "Nessun repository", + "no_repositories_desc1": "Sembra che non ci siano ancora repository installati in questa sezione.", + "no_repositories_desc2": "Fai clic sul + nell'angolo in basso per aggiungere il tuo primo!", + "no_repositories_found_desc1": "Nessun repository installato corrispondente a \"{searchInput}\" trovato in questa sezione.", + "no_repositories_found_desc2": "Prova a cercare qualcos'altro!", + "pending_upgrades": "Aggiornamenti in sospeso", + "placeholder_search": "Inserire un termine di ricerca", + "sort": "Ordinare", + "stars": "Stelle", + "status": "Stato" + }, + "time": { + "ago": "fa", + "day": "giorno", + "days": "giorni", + "hour": "ora", + "hours": "ore", + "minute": "minuto", + "minutes": "minuti", + "month": "mese", + "months": "mesi", + "one": "Un", + "one_day_ago": "un giorno fa", + "one_hour_ago": "un'ora fa", + "one_minute_ago": "un minuto fa", + "one_month_ago": "un mese fa", + "one_second_ago": "un secondo fa", + "one_year_ago": "un anno fa", + "second": "secondo", + "seconds": "secondi", + "x_days_ago": "{x} giorni fa", + "x_hours_ago": "{x} ore fa", + "x_minutes_ago": "{x} minuti fa", + "x_months_ago": "{x} mesi fa", + "x_seconds_ago": "{x} secondi fa", + "x_years_ago": "{x} anni fa", + "year": "anno", + "years": "anni" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/nb.json b/custom_components/hacs/translations/nb.json new file mode 100644 index 0000000..5701d05 --- /dev/null +++ b/custom_components/hacs/translations/nb.json @@ -0,0 +1,383 @@ +{ + "common": { + "about": "Om", + "add": "legg til", + "appdaemon": "AppDaemon", + "appdaemon_apps": "AppDaemon Apper", + "appdaemon_plural": "AppDaemon-apper", + "background_task": "Bakgrunnsoppgaven kjører. Denne siden lastes inn på nytt når den er ferdig.", + "cancel": "Avbryt", + "check_log_file": "Sjekk loggfilen din for mer informasjon.", + "continue": "Fortsett", + "disabled": "Deaktivert", + "documentation": "dokumentasjon", + "element": "element", + "hacs_is_disabled": "HACS er deaktivert", + "ignore": "Ignorere", + "install": "Installer", + "installed": "Installert", + "integration": "Integrasjon", + "integration_plural": "Integrasjoner", + "integrations": "Integrasjoner", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace-element", + "lovelace_elements": "Lovelace-elementer", + "manage": "manage", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon Apper", + "netdaemon_plural": "NetDaemon-apper", + "plugin": "Lovelace", + "plugin_plural": "Lovelace-elementer", + "plugins": "Lovelace-elementer", + "python_script": "Python-skript", + "python_script_plural": "Python-skript", + "python_scripts": "Python-skript", + "reload": "Last inn på nytt", + "repositories": "Repositories", + "repository": "Repository", + "settings": "Innstillinger", + "theme": "Tema", + "theme_plural": "Temaer", + "themes": "Temaer", + "uninstall": "Avinstaller", + "update": "Oppdater", + "version": "Versjon" + }, + "config": { + "abort": { + "single_instance_allowed": "Bare en konfigurasjon av HACS er tillatt." + }, + "error": { + "acc": "Du må bekrefte alle uttalelsene før du fortsetter", + "auth": "Personlig tilgangstoken er ikke korrekt" + }, + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Er du sikker på at du vil legge dette til i dine Lovelace resources?", + "bg_task": "Handlingen er deaktivert mens bakgrunnsoppgaver kjører.", + "cancel": "Avbryt", + "continue": "Er du sikker på at du vil fortsette?", + "delete": "Er du sikker på at du vil fjerne '{item}'?", + "delete_installed": "'{item}' er installert, du må avinstallere det før du kan slette det.", + "exist": "{item} eksisterer allerede", + "generic": "Er du sikker?", + "home_assistant_is_restarting": "Vent, Home Assistant starter nå på nytt.", + "home_assistant_version_not_correct": "Du kjører Home Assistant '{haversion}', men denne krever minimum versjon '{minversion}' for å bli installert.", + "no": "Nei", + "no_upgrades": "Ingen oppgraderinger tilgjengelig", + "ok": "OK", + "overwrite": "Å gjøre dette vil overskrive det.", + "reload_data": "Dette laster inn dataene til alle repositories HACS vet om, dette vil ta litt tid å fullføre.", + "restart_home_assistant": "Er du sikker på at du vil starte Home Assistant på nytt?", + "uninstall": "Er du sikker på at du vil avinstallere '{item}'?", + "upgrade_all": "Dette vil oppgradere alle disse repositorene, sørg for at du har lest utgivelses notatene for dem alle før du fortsetter.", + "yes": "Ja" + }, + "dialog_about": { + "frontend_version": "Frontend versjon", + "installed_repositories": "Installerte repositories", + "integration_version": "Integrasjonsversjon", + "useful_links": "Nyttige lenker" + }, + "dialog_add_repo": { + "limit": "Bare de første 100 elementene vises, bruk søket til å filtrere det du trenger", + "no_match": "Ingen elementer funnet som samsvarer med filteret ditt", + "sort_by": "Sorter etter", + "title": "Legg til repository" + }, + "dialog_custom_repositories": { + "category": "Kategori", + "no_category": "Mangler kategori", + "no_repository": "Mangler repository", + "title": "Custom repositories", + "url_placeholder": "Legg til custom repository" + }, + "dialog_info": { + "author": "Utgiver", + "downloads": "Nedlastinger", + "install": "Installer dette elementet i HACS", + "loading": "Laster inn informasjon ...", + "no_info": "Utvikleren har ikke gitt mer informasjon for dette elementet", + "open_issues": "Åpne problemer", + "open_repo": "Åpne repository", + "stars": "Stjerner", + "version_installed": "Versjon installert" + }, + "dialog_install": { + "restart": "Husk at du må starte Home Assistant på nytt før endringer i integrasjoner (custom_components) brukes.", + "select_version": "Velg versjon", + "show_beta": "Vis betaversjoner", + "type": "Type", + "url": "URL" + }, + "dialog_removed": { + "link": "Ekstern lenke til mer informasjon", + "name": "Navn på repositorium", + "reason": "Årsaken til fjerning", + "type": "Fjerningstype" + }, + "dialog_update": { + "available_version": "Tilgjengelig versjon", + "changelog": "Endringslogg", + "installed_version": "Installert versjon", + "releasenotes": "Utgivelsesnotater for {release}", + "title": "Oppdatering venter" + }, + "dialog": { + "reload": { + "confirm": "Vil du gjøre det nå?", + "description": "Du må tømme nettleserbufferen når du endrer ressurser for Lovelace." + } + }, + "entry": { + "information": "Informasjon", + "intro": "Oppdateringer og viktige meldinger vises her hvis det er noen", + "messages": { + "disabled": { + "content": "Sjekk loggfilen din for mer informasjon", + "title": "HACS er deaktivert" + }, + "has_pending_tasks": { + "content": "Noen elementer vises kanskje ikke før dette er fullført", + "title": "Venter på bakgrunnsoppgaver" + }, + "removed": "Fjernet repositoriet {repository}", + "resources": { + "content": "Du har {number} Lovelace-elementer som ikke er riktig lastet inn i Lovelace.", + "title": "Ikke lastet i Lovelace" + }, + "restart": { + "content": "Du har {number} integrasjoner som krever en omstart av Home Assistant, du kan gjøre det fra delen Serverkontroller under konfigurasjonsdelen av Home Assistant UI.", + "title": "Venter på omstart" + }, + "setup": { + "content": "HACS starter opp, i løpet av denne tiden kan det hende at noe informasjon mangler eller er feil", + "title": "HACS settes opp" + }, + "startup": { + "content": "HACS starter opp, i løpet av denne tiden kan det hende at noe informasjon mangler eller er feil", + "title": "HACS starter opp" + }, + "waiting": { + "content": "HACS venter på at Home Assistant skal fullføre oppstart før oppstart av oppgaver", + "title": "HACS venter" + }, + "wrong_frontend_installed": { + "content": "Du har {running} av HACS frontend installert, men versjon {expected} var forventet, hvis dette du ser denne meldingen Home Assistant ikke kunne installere den nye versjonen, kan du prøve å starte Home Assistant på nytt.", + "title": "Uventet frontend versjon" + }, + "wrong_frontend_loaded": { + "content": "Du kjører versjon {running} av HACS frontend, men versjon {expected} var forventet, du bør tømme nettleserens hurtigbuffer.", + "title": "Uventet frontend versjon" + } + }, + "pending_updates": "Oppdateringer er klare" + }, + "menu": { + "about": "Om HACS", + "clear": "Fjern alt nytt", + "custom_repositories": "Custom repositories", + "dismiss": "Avvis alle nye repositorier", + "documentation": "Dokumentasjon", + "open_issue": "Meld et problem", + "reload": "Last inn vinduet på nytt" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Aktiver oppdagelse og sporing av AppDaemon-apper", + "country": "Filtrer med landskode.", + "debug": "Aktiver debug", + "experimental": "Aktiver eksperimentelle funksjoner", + "netdaemon": "Aktiver oppdagelse og sporing av NetDaemon-apper", + "not_in_use": "Ikke i bruk med YAML", + "release_limit": "Antall utgivelser som skal vises.", + "sidepanel_icon": "Sidepanel ikon", + "sidepanel_title": "Sidepanel tittel" + } + } + } + }, + "repository_banner": { + "config_flow": "Denne integrasjonen støtter config_flow, det betyr at du nå kan gå til integrasjoner i brukergrensesnittet for å konfigurere den.", + "config_flow_title": "UI konfigurasjon støttet", + "integration_not_loaded": "Integrasjonen er ikke lastet inn i Home Assistant.", + "no_restart_required": "Ingen omstart kreves", + "not_loaded": "Ikke lastet inn", + "plugin_not_loaded": "Dette elementet er ikke lagt til i lovelace under \"resource\" delen av konfigurasjonen.", + "restart": "Du må restart Home Assistant", + "restart_pending": "Restart er nødvendig" + }, + "repository_card": { + "dismiss": "Avvis", + "hide": "Skjul", + "information": "Informasjon", + "new_repository": "Ny", + "not_loaded": "Ikke lastet inn", + "open_issue": "Meld et problem", + "open_source": "Åpne kilde", + "pending_restart": "Venter på omstart", + "pending_update": "Oppdatering venter", + "reinstall": "Installer på nytt", + "report": "Rapporter for fjerning", + "update_information": "Oppdater informasjon" + }, + "repository": { + "add_to_lovelace": "Legg til i Lovelace", + "authors": "Laget av", + "available": "Tilgjengelig", + "back_to": "Tilbake til", + "changelog": "Endringslogg", + "downloads": "Nedlastinger", + "flag_this": "Flag dette", + "frontend_version": "Frontend versjon", + "github_stars": "GitHub-stjerner", + "goto_integrations": "Gå til integrasjoner", + "hide": "Skjul", + "hide_beta": "Skjul beta", + "install": "Installer", + "installed": "Installert", + "lovelace_copy_example": "Kopier eksemplet til utklippstavlen", + "lovelace_instruction": "Når du legger til dette i lovelace-konfigurasjonen din, bruk dette", + "lovelace_no_js_type": "Kunne ikke bestemme typen for dettte elementet, sjekk repository.", + "newest": "Nyeste", + "note_appdaemon": "du må fortsatt legge den til i 'apps.yaml' filen", + "note_installed": "Når det er installert, vil dette ligge i", + "note_integration": "du må fortsatt legge den til 'configuration.yaml' filen", + "note_plugin": "du må fortsatt legge den til i lovelace-konfigurasjonen ('ui-lovelace.yaml' eller den rå UI-konfigurasjonsredigereren)", + "note_plugin_post_107": "du må fortsatt legge den til i lovelace konfigurasjonen ('configuration.yaml' eller via resource behanleren i grensesnittet '\/config\/lovelace\/resources')", + "open_issue": "Meld et problem", + "open_plugin": "Åpne kilde", + "reinstall": "Installer på nytt", + "repository": "Repository", + "restart_home_assistant": "Start Home Assistant på nytt", + "show_beta": "Vis beta", + "uninstall": "Avinstaller", + "update_information": "Oppdater informasjon", + "upgrade": "Oppdater" + }, + "search": { + "installed": "Søke etter installerte repositorier", + "installed_new": "Søke etter installerte eller nye repositorier", + "placeholder": "Søk etter repository" + }, + "sections": { + "about": { + "description": "Vis informasjon om HACS", + "title": "Om" + }, + "addon": { + "description": "Det er ingen addons i HACS, men du kan klikke her for å komme til veilederen", + "title": "Add-ons" + }, + "automation": { + "description": "Det er her du finner python_scripts, AppDaemon-apper og NetDaemon-apper", + "title": "Automasjon" + }, + "frontend": { + "description": "Det er her du finner temaer, tilpassede kort og andre elementer for lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Det er her du finner tilpassede integrasjoner (custom_components)", + "title": "Integrasjoner" + }, + "pending_repository_upgrade": "Du kjører versjon {installed} , versjon {available} er tilgjengelig" + }, + "settings": { + "add_custom_repository": "LEGG TIL REPOSITORY", + "adding_new_repo": "Legger til ny repository '{repo}'", + "adding_new_repo_category": "Med kategori '{category}'.", + "bg_task_custom": "Custom repositories er skjult mens bakgrunnsoppgaver kjører.", + "category": "Kategori", + "compact_mode": "Kompakt modus", + "custom_repositories": "TILPASSEDE REPOSITORIER", + "delete": "Slett", + "display": "Vise", + "grid": "Nett", + "hacs_repo": "HACS repo", + "hidden_repositories": "Gjemte repositories", + "missing_category": "Du må velge en kategori", + "open_repository": "Åpne repository", + "reload_data": "Last inn data på nytt", + "reload_window": "Last inn vinduet på nytt", + "repository_configuration": "Repository konfigurasjon", + "save": "Lagre", + "table": "Tabell", + "table_view": "Tabellvisning", + "unhide": "Vis igjen", + "upgrade_all": "Oppgradere alle" + }, + "store": { + "ascending": "stigende", + "clear_new": "Tøm nye repositories", + "descending": "synkende", + "last_updated": "Sist oppdatert", + "name": "Navn", + "new_repositories": "Nye repositories", + "new_repositories_note": "Du har over 10 nye repositorier som viser her, hvis du vil fjerne dem alle, klikker du på de 3 prikkene øverst til høyre og avviser dem alle.", + "no_repositories": "Ingen repositories", + "no_repositories_desc1": "Det virker som om du ikke har noen elementer installert i denne delen ennå.", + "no_repositories_desc2": "Klikk på + i nederste hjørne for å legge til din første!", + "no_repositories_found_desc1": "Finner ingen installerte repositorier som samsvarer med {searchInput}i denne delen.", + "no_repositories_found_desc2": "Prøv å søke etter noe annet!", + "pending_upgrades": "Venter på oppgradering", + "placeholder_search": "Skriv inn et søkeord ...", + "sort": "sorter", + "stars": "Stjerner", + "status": "Status" + }, + "time": { + "ago": "siden", + "day": "dag", + "days": "dager", + "hour": "time", + "hours": "timer", + "minute": "minutt", + "minutes": "minutter", + "month": "måned", + "months": "måneder", + "one": "En", + "one_day_ago": "for en dag siden", + "one_hour_ago": "en time siden", + "one_minute_ago": "ett minutt siden", + "one_month_ago": "en måned siden", + "one_second_ago": "ett sekund siden", + "one_year_ago": "ett år siden", + "second": "sekund", + "seconds": "sekunder", + "x_days_ago": "{x} dager siden", + "x_hours_ago": "{x} timer siden", + "x_minutes_ago": "{x} minutter siden", + "x_months_ago": "{x} måneder siden", + "x_seconds_ago": "{x} sekunder siden", + "x_years_ago": "{x} år siden", + "year": "år", + "years": "år" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/nl.json b/custom_components/hacs/translations/nl.json new file mode 100644 index 0000000..eac51c0 --- /dev/null +++ b/custom_components/hacs/translations/nl.json @@ -0,0 +1,346 @@ +{ + "common": { + "about": "Over", + "add": "toevoegen", + "appdaemon": "AppDaemon", + "appdaemon_apps": "AppDaemon Apps", + "appdaemon_plural": "AppDaemon Apps", + "background_task": "Achtergrond taak is draaiende, de pagina herhaalt zichzelf wanneer dit klaar is.", + "check_log_file": "Controleer het logbestand voor meer details.", + "continue": "Verder", + "disabled": "Uitgeschakeld", + "documentation": "Documentatie", + "element": "element", + "hacs_is_disabled": "HACS is uitgeschakeld", + "install": "Installeer", + "installed": "geinstalleerd", + "integration": "Integratie", + "integration_plural": "Integraties", + "integrations": "Integraties", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace-element", + "lovelace_elements": "Lovelace-elementen", + "manage": "beheer", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon Apps", + "netdaemon_plural": "NetDaemon Apps", + "plugin": "Lovelace", + "plugin_plural": "Lovelace-elementen", + "plugins": "Plugins", + "python_script": "Python Script", + "python_script_plural": "Python Scripts", + "python_scripts": "Python Scripts", + "repositories": "Repositories", + "repository": "Repository", + "settings": "instellingen", + "theme": "Thema", + "theme_plural": "Themas", + "themes": "Themas", + "uninstall": "Verwijderen", + "update": "Update", + "version": "Versie" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Weet u zeker dat u dit wilt toevoegen aan uw Lovelace bronnen?", + "bg_task": "Actie is geblokkeerd terwijl achtergrondtaken actief zijn.", + "cancel": "Annuleer", + "continue": "Weet je zeker dat je door wilt gaan?", + "delete": "Weet u zeker dat u '{item}' wilt verwijderen?", + "delete_installed": "'{item}' is geïnstalleerd, je dient het eerst te deïnstalleren voordat je het kan verwijderen.", + "exist": "{item} bestaat al.", + "generic": "Weet je het zeker?", + "home_assistant_is_restarting": "Een moment alstublieft, Home Assistant is aan het herstarten.", + "home_assistant_version_not_correct": "Je gebruikt Home Assistant versie '{haversion}', echter deze repository vereist dat minimaal versie '{minversion}' is geïnstalleerd.", + "no": "Nee", + "no_upgrades": "Geen upgrades in afwachting.", + "ok": "Oké", + "overwrite": "Door dit te doen, wordt het overschreven.", + "reload_data": "Dit zal alle bekende data herladen van alle repositories van HACS. Dit kan even duren", + "restart_home_assistant": "Weet u zeker dat u Home Assistant opnieuw wilt starten?", + "uninstall": "Weet u zeker dat u '{item}' wilt verwijderen?", + "upgrade_all": "Hiermee worden al deze repositories geüpgraded. Zorg ervoor dat u de release-opmerkingen van allen heeft gelezen voordat u doorgaat.", + "yes": "Ja" + }, + "dialog_about": { + "frontend_version": "Frontend versie", + "installed_repositories": "Geïnstalleerde repositories", + "integration_version": "Integratieversie", + "useful_links": "Nuttige links" + }, + "dialog_add_repo": { + "limit": "Alleen de eerste 100 repositories worden getoond, gebruik de zoekopdracht om te filteren wat je nodig hebt", + "no_match": "Er zijn geen repositories gevonden die overeenkomen met uw filter", + "sort_by": "Sorteren op", + "title": "Repository toevoegen" + }, + "dialog_custom_repositories": { + "category": "Categorie", + "no_category": "Ontbrekende categorie", + "no_repository": "Ontbrekende repository", + "title": "Aangepaste repositories", + "url_placeholder": "Voeg een aangepaste repository-URL toevoegen" + }, + "dialog_info": { + "author": "Auteur", + "downloads": "Downloads", + "install": "Installeer deze repository in HACS", + "loading": "Informatie laden ...", + "no_info": "De ontwikkelaar heeft geen verdere informatie verstrekt voor deze repository", + "open_issues": "Openstaande problemen", + "open_repo": "Open repository", + "stars": "Sterren", + "version_installed": "Versie geïnstalleerd" + }, + "dialog_install": { + "restart": "Onthoud dat u Home Assistant opnieuw moet opstarten voordat wijzigingen aan integraties (custom_components) worden toegepast.", + "select_version": "Selecteer versie", + "show_beta": "Bètaversies weergeven", + "type": "Type", + "url": "URL" + }, + "dialog_update": { + "available_version": "Beschikbare versie", + "changelog": "Changelog", + "installed_version": "Geïnstalleerde versie", + "releasenotes": "Releasenotes voor {release}", + "title": "Update in behandeling" + }, + "entry": { + "information": "Informatie", + "intro": "Updates en belangrijke berichten worden hier weergegeven als die er zijn", + "messages": { + "disabled": { + "content": "Controleer uw logbestand voor meer details", + "title": "HACS is uitgeschakeld" + }, + "has_pending_tasks": { + "content": "Sommige repositories worden mogelijk pas weergegeven als dit is voltooid", + "title": "Achtergrondtaken in behandeling" + }, + "resources": { + "content": "Je hebt {number} Lovelace-elementen die niet correct zijn geladen in Lovelace.", + "title": "Niet geladen in Lovelace" + }, + "restart": { + "content": "Je hebt {number} integraties waarvoor de Home Assistant opnieuw moet worden opgestart, je kunt dat doen via het gedeelte 'Serverbeheer' onder het configuratiegedeelte van de Home Assistant UI.", + "title": "In afwachting van herstart" + }, + "startup": { + "content": "HACS is aan het opstarten, gedurende deze tijd kan er informatie ontbreken of onjuist zijn", + "title": "HACS is aan het opstarten" + }, + "wrong_frontend_installed": { + "content": "Je hebt {running} van de HACS-frontend geïnstalleerd, maar versie {expected} werd verwacht. Als je dit bericht ziet, kon Home Assistant de nieuwe versie niet installeren, probeer dan Home Assistant opnieuw op te starten.", + "title": "Onverwachte frontend-versie" + }, + "wrong_frontend_loaded": { + "content": "U gebruikt versie {running} van de HACS-frontend, maar versie {expected} werd verwacht, u moet uw browsercache wissen.", + "title": "Onverwachte frontend-versie" + } + }, + "pending_updates": "In afwachting van updates" + }, + "menu": { + "about": "Over HACS", + "clear": "Wis alle nieuwe", + "custom_repositories": "Aangepaste repositories", + "dismiss": "Sluit alle nieuwe repositories af", + "documentation": "Documentatie", + "open_issue": "Meld probleem", + "reload": "Herlaad venster" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Zet AppDaemon apps ontdekken & traceren aan", + "country": "Filter met land code.", + "debug": "Schakel debug in.", + "experimental": "Zet experimentele functies aan", + "netdaemon": "Zet NetDaemon apps ontdekken & traceren aan", + "not_in_use": "Niet in gebruik met YAML", + "release_limit": "Aantal releases om te laten zien.", + "sidepanel_icon": "Zijpaneel icoon", + "sidepanel_title": "Zijpaneel titel" + } + } + } + }, + "repository_banner": { + "config_flow": "Deze integratie ondersteunt config_flow, wat betekent dat u via uw \"Instellingen\" naar \"Integraties\" kunt gaan om het te configureren.", + "config_flow_title": "UI-configuratie ondersteund", + "integration_not_loaded": "Deze integratie wordt niet geladen in Home Assistant.", + "no_restart_required": "Geen herstart vereist", + "not_loaded": "Niet geladen", + "plugin_not_loaded": "Deze plugin wordt niet toegevoegd aan je Lovelace resources.", + "restart": "U moet Home Assistant opnieuw starten.", + "restart_pending": "Wachten op herstart" + }, + "repository_card": { + "dismiss": "afwijzen", + "hide": "Verbergen", + "information": "Informatie", + "new_repository": "Nieuwe repository", + "not_loaded": "Niet geladen", + "open_issue": "Meld probleem", + "open_source": "Open source", + "pending_restart": "In afwachting van herstart", + "pending_update": "In afwachting van update", + "reinstall": "Herinstalleer", + "report": "Rapport voor verwijdering", + "update_information": "Update informatie" + }, + "repository": { + "add_to_lovelace": "Toevoegen aan Lovelace", + "authors": "Auteurs", + "available": "Beschikbaar", + "back_to": "Terug naar", + "changelog": "Changelog", + "downloads": "Downloads", + "flag_this": "Vlag dit", + "frontend_version": "Frontend versie", + "github_stars": "GitHub-sterren", + "goto_integrations": "Ga naar integraties", + "hide": "Verberg", + "hide_beta": "Verberg beta", + "install": "Installeer", + "installed": "Geinstalleerd", + "lovelace_copy_example": "Kopier het voorbeeld naar je klembord", + "lovelace_instruction": "Wanneer je dit gaat toevoegen aan je lovelace configuratie gebruik dit", + "lovelace_no_js_type": "Kon niet achterhalen welk type plugin dit is, check de repository van de plugin.", + "newest": "nieuwste", + "note_appdaemon": "je moet het nog steeds toevoegen aan je 'apps.yaml' bestand", + "note_installed": "Wanneer geïnstalleerd, staat het in", + "note_integration": "je moet het nog steeds toevoegen aan je 'configuration.yaml' bestand", + "note_plugin": "je moet het nog steeds toevoegen aan je lovelace configuratie ('ui-lovelace.yaml') of raw UI config editor.", + "note_plugin_post_107": "je moet het nog steeds toevoegen aan je lovelace configuratie ('configuration.yaml' of de resource editor '\/config\/lovelace\/resources')", + "open_issue": "Meld probleem", + "open_plugin": "Open plugin", + "reinstall": "Herinstalleer", + "repository": "Repository", + "restart_home_assistant": "Start Home Assistant opnieuw", + "show_beta": "Laat beta zien", + "uninstall": "Verwijder", + "update_information": "Update informatie", + "upgrade": "Update" + }, + "search": { + "placeholder": "Zoek naar repository" + }, + "sections": { + "about": { + "description": "Toon informatie over HACS", + "title": "Over" + }, + "automation": { + "description": "Hier vind je python_scripts, AppDaemon-apps en NetDaemon-apps", + "title": "Automatisering" + }, + "frontend": { + "description": "Dit is waar je thema's, aangepaste kaarten en andere elementen voor lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Hier vindt u aangepaste integraties (custom_components)", + "title": "Integraties" + }, + "pending_repository_upgrade": "U gebruikt versie {installed} , versie {available} is beschikbaar" + }, + "settings": { + "add_custom_repository": "VOEG EIGEN REPOSITORY TOE", + "adding_new_repo": "Nieuwe repository '{repo}' toevoegen", + "adding_new_repo_category": "Met categorie '{category}'.", + "bg_task_custom": "Aangepaste repositories zijn verborgen terwijl de achtergrondtaken actief zijn.", + "category": "Categorie", + "compact_mode": "Compacte modus", + "custom_repositories": "EIGEN REPOSITORIES", + "delete": "Verwijder", + "display": "Weergave", + "grid": "Rooster", + "hacs_repo": "HACS repo", + "hidden_repositories": "verborgen repositories", + "missing_category": "Je moet een categorie selecteren.", + "open_repository": "Open repository", + "reload_data": "Herlaad data", + "reload_window": "Herlaad venster", + "repository_configuration": "Repository configuratie", + "save": "Opslaan", + "table": "Tabel", + "table_view": "Tabelweergave", + "unhide": "zichtbaar maken", + "upgrade_all": "Upgrade alles" + }, + "store": { + "ascending": "oplopend", + "clear_new": "Wissen van alle nieuwe repositories", + "descending": "Aflopend", + "last_updated": "Laatste update", + "name": "Naam", + "new_repositories": "Nieuwe Repositories", + "new_repositories_note": "Je hebt meer dan 10 nieuwe repositories die hier worden weergegeven, als je ze allemaal wilt wissen, klik dan op de 3 puntjes in de rechterbovenhoek en wijs ze af.", + "no_repositories": "Geen repositories", + "no_repositories_desc1": "Het lijkt erop dat je nog geen repositories hebt geïnstalleerd in deze sectie.", + "no_repositories_desc2": "Klik op de + in de benedenhoek om je eerste toe te voegen!", + "no_repositories_found_desc1": "Er zijn geen geïnstalleerde repositories die overeenkomen met {searchInput}\" in deze sectie.", + "no_repositories_found_desc2": "Probeer iets anders te zoeken!", + "pending_upgrades": "Upgrades in afwachting", + "placeholder_search": "Typ iets om te zoeken...", + "sort": "sorteer", + "stars": "Sterren", + "status": "Status" + }, + "time": { + "ago": "geleden", + "day": "dag", + "days": "dagen", + "hour": "uur", + "hours": "uren", + "minute": "minuut", + "minutes": "minuten", + "month": "maand", + "months": "maanden", + "one": "Eén", + "one_day_ago": "een dag geleden", + "one_hour_ago": "een uur geleden", + "one_minute_ago": "een minuut geleden", + "one_month_ago": "een maand geleden", + "one_second_ago": "een seconde geleden", + "one_year_ago": "een jaar geleden", + "second": "seconde", + "seconds": "seconden", + "x_days_ago": "{x} dagen geleden", + "x_hours_ago": "{x} uur geleden", + "x_minutes_ago": "{x} minuten geleden", + "x_months_ago": "{x} maanden geleden", + "x_seconds_ago": "{x} seconden geleden", + "x_years_ago": "{x} jaar geleden", + "year": "jaar", + "years": "jaren" + } +} \ No newline at end of file diff --git a/custom_components/hacs/.translations/nn.json b/custom_components/hacs/translations/nn.json similarity index 74% rename from custom_components/hacs/.translations/nn.json rename to custom_components/hacs/translations/nn.json index 5c1fc0c..0a1666d 100644 --- a/custom_components/hacs/.translations/nn.json +++ b/custom_components/hacs/translations/nn.json @@ -3,48 +3,60 @@ "about": "Om", "appdaemon": "AppDaemon", "appdaemon_apps": "AppDeamon-appar", + "appdaemon_plural": "AppDaemon-appar", "background_task": "Bakgrunnsoppgåve køyrer. Denne sida kjem til å laste seg omatt når ho er ferdig.", "check_log_file": "Sjå i loggfila di for meir detaljar.", "continue": "Hald fram", "disabled": "Deaktivert", "documentation": "Dokumentasjon", + "element": "element", "hacs_is_disabled": "HACS er deaktivert", "installed": "Installert", "integration": "Integrasjon", + "integration_plural": "Integrasjonar", "integrations": "Integrasjonar", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace-element", + "lovelace_elements": "Lovelace-element", "manage": "Handtere", "netdaemon": "NetDaemon", "netdaemon_apps": "NetDeamon-appar", - "plugin": "Tillegg", - "plugins": "Tillegg", + "netdaemon_plural": "NetDaemon-appar", + "plugin": "Lovelace", + "plugin_plural": "Lovelace-element", + "plugins": "Lovelace-element", "python_script": "Pythonskript", + "python_script_plural": "Pythonskript", "python_scripts": "Pythonskript", "repositories": "Repositories", "settings": "innstillingar", "theme": "Tema", + "theme_plural": "Tema", "themes": "Tema", "version": "Versjon" }, "config": { - "abort": { - "single_instance_allowed": "Berre éin enkelt konfigurasjon av HACS er tillete." - }, - "error": { - "auth": "Personleg tilgangsnøkkel er ikkje korrekt." - }, "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, "user": { "data": { - "appdaemon": "Aktiver AppDeamon-appar-oppdaging og sporing", - "netdaemon": "Aktiver NetDeamon-appar-oppdaging og sporing", - "python_script": "Aktiver pythonscript-oppdaging og sporing", - "sidepanel_icon": "Sidepanelikon", - "sidepanel_title": "Sidepaneltittel", - "theme": "Aktiver tema-oppdaging og sporing", - "token": "Personleg GitHub tilgangsnøkkel" + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" }, - "description": "Dersom du treng hjelp med konfigurasjonen, ta ein kik her: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" } }, "title": "HACS (Home Assistant Community Store)" @@ -59,7 +71,7 @@ "exist": "{item} eksisterer allereie", "generic": "Er du sikker?", "home_assistant_is_restarting": "Vent... Home Assistant starter på nytt no.", - "home_assistant_version_not_correct": "Du køyrer Home Assistant-versjonen '{haversion}', men dette kodedepoet krev minimum versjon '{minversion}' for å bli installert.", + "home_assistant_version_not_correct": "Du køyrer Home Assistant-versjonen '{haversion}', men dette kodedepoet krev minst versjon '{minversion}' for å bli installert.", "no": "Nei", "no_upgrades": "Ingen ventande oppgradringer", "ok": "OK", @@ -120,6 +132,7 @@ "note_installed": "Når dette er installert, kjem den til å vere plassert i", "note_integration": "du må framleis legge dette til i \"configuration.yaml\"-fila di", "note_plugin": "du må framleis dette til i Lovelace-konfigurasjonen (\"ui-lovelace.yaml\" eller i rå-brukargrensesnittredigeraren", + "note_plugin_post_107": "du må framleis legge dette til i lovelace-konfigurasjonen ('configuration.yaml' eller i kjelderedigeraren ''\/config\/lovelace\/resources')", "open_issue": "Opne problem", "open_plugin": "Opne tillegg", "reinstall": "Installer på nytt", @@ -130,6 +143,25 @@ "update_information": "Oppdater informasjonen", "upgrade": "Oppdater" }, + "sections": { + "about": { + "description": "Vis informasjon om HACS", + "title": "Om" + }, + "automation": { + "description": "Her finn du python_scripts, AppDaemon-appar og NetDaemon-appar", + "title": "Automasjon" + }, + "frontend": { + "description": "Her finn du tema, eigendefinerte kort og andre element for lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Her finn du eigendefinerte ingtegrasjonar (custom_components)", + "title": "Integrasjonar" + }, + "pending_repository_upgrade": "Du køyrer versjon {installed}, og versjon {available} er tilgjengeleg" + }, "settings": { "add_custom_repository": "LEGG TIL EIN ANNAN REPOSITORY", "adding_new_repo": "Legger til ny repository '{repo}'", diff --git a/custom_components/hacs/translations/pl.json b/custom_components/hacs/translations/pl.json new file mode 100644 index 0000000..7458e26 --- /dev/null +++ b/custom_components/hacs/translations/pl.json @@ -0,0 +1,383 @@ +{ + "common": { + "about": "O", + "add": "dodaj", + "appdaemon": "AppDaemon", + "appdaemon_apps": "Aplikacje AppDaemon", + "appdaemon_plural": "Aplikacje AppDaemon", + "background_task": "Wykonywanie zadania w tle, ta strona zostanie odświeżona, gdy zadanie zostanie ukończone.", + "cancel": "Anuluj", + "check_log_file": "Sprawdź plik dziennika, aby uzyskać więcej informacji.", + "continue": "Kontynuuj", + "disabled": "Wyłączony", + "documentation": "Dokumentacja", + "element": "element", + "hacs_is_disabled": "HACS jest wyłączony", + "ignore": "Ignoruj", + "install": "Zainstaluj", + "installed": "zainstalowane", + "integration": "Integracja", + "integration_plural": "Integracje", + "integrations": "Integracje", + "lovelace": "Lovelace", + "lovelace_element": "Element Lovelace", + "lovelace_elements": "Elementy Lovelace", + "manage": "zarządzaj", + "netdaemon": "NetDaemon", + "netdaemon_apps": "Aplikacje NetDaemon", + "netdaemon_plural": "Aplikacje NetDaemon", + "plugin": "Lovelace", + "plugin_plural": "Elementy Lovelace", + "plugins": "Elementy Lovelace", + "python_script": "Skrypt Python", + "python_script_plural": "Skrypty języka Python", + "python_scripts": "Skrypty Python", + "reload": "Wczytaj ponownie", + "repositories": "Repozytoria", + "repository": "Repozytorium", + "settings": "ustawienia", + "theme": "Motyw", + "theme_plural": "Motywy", + "themes": "Motywy", + "uninstall": "Odinstaluj", + "update": "Uaktualnij", + "version": "Wersja" + }, + "config": { + "abort": { + "single_instance_allowed": "Dozwolona jest tylko jedna konfiguracja HACS." + }, + "error": { + "acc": "Musisz potwierdzić wszystkie oświadczenia przed kontynuowaniem", + "auth": "Osobisty token dostępu jest nieprawidłowy" + }, + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Na pewno chcesz dodać to do swoich zasobów Lovelace?", + "bg_task": "Akcja jest wyłączona podczas wykonywania zadań w tle.", + "cancel": "Anuluj", + "continue": "Na pewno chcesz kontynuować?", + "delete": "Na pewno chcesz usunąć '{item}'?", + "delete_installed": "'{item}' jest zainstalowany, musisz go odinstalować zanim będziesz mógł go usunąć.", + "exist": "{item} już istnieje", + "generic": "Jesteś pewny?", + "home_assistant_is_restarting": "Poczekaj, Home Assistant jest teraz ponownie uruchamiany.", + "home_assistant_version_not_correct": "Używasz Home Assistant'a w wersji '{haversion}', a to repozytorium wymaga wersji minimum '{minversion}'.", + "no": "Nie", + "no_upgrades": "Brak oczekujących aktualizacji", + "ok": "Ok", + "overwrite": "Spowoduje to zastąpienie istniejącej kopii.", + "reload_data": "To przeładowuje dane wszystkich repozytoriów, o których wie HACS, może to trochę potrwać.", + "restart_home_assistant": "Na pewno chcesz ponownie uruchomić Home Assistant'a?", + "uninstall": "Na pewno chcesz odinstalować '{item}'?", + "upgrade_all": "To uaktualni wszystkie te repozytoria, upewnij się, że przeczytałeś uwagi do wydania dla wszystkich z nich przed kontynuacją.", + "yes": "Tak" + }, + "dialog_about": { + "frontend_version": "Wersja interfejsu użytkownika", + "installed_repositories": "Zainstalowane repozytoria", + "integration_version": "Wersja integracji", + "useful_links": "Przydatne linki" + }, + "dialog_add_repo": { + "limit": "Wyświetlanych jest tylko pierwszych 100 repozytoriów, użyj wyszukiwania, aby przefiltrować potrzebne informacje", + "no_match": "Nie znaleziono repozytoriów pasujących do filtra", + "sort_by": "Sortuj według", + "title": "Dodawanie repozytorium" + }, + "dialog_custom_repositories": { + "category": "Kategoria", + "no_category": "Brak kategorii", + "no_repository": "Brak repozytorium", + "title": "Niestandardowe repozytoria", + "url_placeholder": "Adres URL niestandardowego repozytorium" + }, + "dialog_info": { + "author": "Autor", + "downloads": "Ilość pobrań", + "install": "Zainstaluj to repozytorium w HACS", + "loading": "Pobieranie informacji...", + "no_info": "Deweloper nie dostarczył więcej informacji na temat tego repozytorium", + "open_issues": "Problemy", + "open_repo": "Otwórz repozytorium", + "stars": "Gwiazdki", + "version_installed": "Wersja zainstalowana" + }, + "dialog_install": { + "restart": "Pamiętaj, że musisz ponownie uruchomić Home Assistanta by zastosować zmiany w integracjach (custom_components).", + "select_version": "Wybierz wersję", + "show_beta": "Wyświetl wydania beta", + "type": "Typ", + "url": "URL" + }, + "dialog_removed": { + "link": "Link zewnętrzny do dodatkowych informacji", + "name": "Nazwa repozytorium", + "reason": "Powód usunięcia", + "type": "Rodzaj usunięcia" + }, + "dialog_update": { + "available_version": "Dostępna wersja", + "changelog": "Lista zmian", + "installed_version": "Zainstalowana wersja", + "releasenotes": "Informacje o {release}", + "title": "Dostępna aktualizacja" + }, + "dialog": { + "reload": { + "confirm": "Czy chcesz to zrobić teraz?", + "description": "Musisz wyczyścić pamięć podręczną przeglądarki po zmianie zasobów Lovelace." + } + }, + "entry": { + "information": "Informacje", + "intro": "Aktualizacje i ważne komunikaty będą wyświetlane w tym miejscu", + "messages": { + "disabled": { + "content": "Sprawdź log, aby uzyskać więcej informacji", + "title": "HACS jest wyłączony" + }, + "has_pending_tasks": { + "content": "Dopóki nie zostaną zakończone, niektóre repozytoria mogą nie być wyświetlane", + "title": "Wykonywane są zadania w tle" + }, + "removed": "Usunięto repozytorium '{repository}'", + "resources": { + "content": "Elementy Lovelace, które nie zostały poprawnie załadowane: {number}", + "title": "Nie załadowano w Lovelace" + }, + "restart": { + "content": "Integracje, które wymagają ponownego uruchomienia Home Assistanta: {number}\\nMożesz to zrobić w sekcji 'Kontrola serwera' konfiguracji Home Assistanta.", + "title": "Oczekiwanie na restart" + }, + "setup": { + "content": "HACS jest konfigurowany, w tym czasie może brakować niektórych informacji lub są one nieprawidłowe", + "title": "HACS jest konfigurowany" + }, + "startup": { + "content": "HACS uruchamia się, w tym czasie może brakować pewnych informacji lub mogą one być nieprawidłowe.", + "title": "HACS uruchamia się" + }, + "waiting": { + "content": "HACS czeka na zakończenie uruchamiania Home Assistanta przed rozpoczęciem własnych zadań", + "title": "HACS czeka" + }, + "wrong_frontend_installed": { + "content": "Masz zainstalowany interfejs HACS w wersji {running}, a wersja {expected} była oczekiwana. Komunikat ten oznacza, że Home Assistant nie mógł zainstalować nowej wersji interfejsu HACS, spróbuj ponownie uruchomić Home Assistanta.", + "title": "Nieoczekiwana wersja interfejsu" + }, + "wrong_frontend_loaded": { + "content": "Używasz wersji {running} interfejsu HACS, a wersja {expected} była oczekiwana, powinieneś wyczyścić pamięć podręczną przeglądarki.", + "title": "Nieoczekiwana wersja interfejsu" + } + }, + "pending_updates": "Oczekujące aktualizacje" + }, + "menu": { + "about": "O HACS", + "clear": "Wyczyść oznaczenia nowych", + "custom_repositories": "Niestandardowe repozytoria", + "dismiss": "Odrzuć wszystkie nowe repozytoria", + "documentation": "Dokumentacja", + "open_issue": "Powiadom o problemie", + "reload": "Załaduj ponownie okno" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Włącz wykrywanie i śledzenie aplikacji AppDaemon", + "country": "Filtruj według kodu kraju", + "debug": "Włącz debugowanie.", + "experimental": "Włącz funkcje eksperymentalne", + "netdaemon": "Włącz wykrywanie i śledzenie aplikacji NetDaemon", + "not_in_use": "Nieużywany z YAML", + "release_limit": "Liczba wydań do wyświetlenia", + "sidepanel_icon": "Ikona w panelu bocznym", + "sidepanel_title": "Tytuł w panelu bocznym" + } + } + } + }, + "repository_banner": { + "config_flow": "Ta integracja obsługuje config_flow, co oznacza, że możesz teraz przejść do sekcji integracji w interfejsie użytkownika, aby ją skonfigurować.", + "config_flow_title": "Obsługiwana konfiguracja poprzez interfejs użytkownika", + "integration_not_loaded": "Ta integracja nie jest załadowana do Home Assistant'a.", + "no_restart_required": "Ponowne uruchomienie nie jest wymagane", + "not_loaded": "Nie załadowano", + "plugin_not_loaded": "Ta wtyczka nie jest dodana do zasobów Lovelace.", + "restart": "Musisz ponownie uruchomić Home Assistant'a.", + "restart_pending": "Oczekiwanie na ponowne uruchomienie" + }, + "repository_card": { + "dismiss": "odrzuć", + "hide": "Ukryj", + "information": "Informacje", + "new_repository": "Nowe repozytorium", + "not_loaded": "Nie załadowano", + "open_issue": "Powiadom o problemie", + "open_source": "Otwórz kod źródłowy", + "pending_restart": "Oczekiwanie na restart", + "pending_update": "Oczekująca aktualizacja", + "reinstall": "Przeinstaluj", + "report": "Zgłoś do usunięcia", + "update_information": "Uaktualnij dane" + }, + "repository": { + "add_to_lovelace": "Dodaj do Lovelace", + "authors": "Autorzy", + "available": "Dostępna", + "back_to": "Wróć do", + "changelog": "Lista zmian", + "downloads": "Ilość pobrań", + "flag_this": "Oflaguj", + "frontend_version": "Wersja frontendu", + "github_stars": "Gwiazdki GitHub", + "goto_integrations": "Przejdź do integracji", + "hide": "Ukryj", + "hide_beta": "Ukryj wydania beta", + "install": "Zainstaluj", + "installed": "Zainstalowano", + "lovelace_copy_example": "Skopiuj przykład do schowka", + "lovelace_instruction": "Interfejs użytkownika użyje tej wtyczki po dodaniu konfiguracji", + "lovelace_no_js_type": "Nie można określić typu tej wtyczki, sprawdź repozytorium.", + "newest": "najnowsza", + "note_appdaemon": "musisz jeszcze dodać aplikację do pliku 'apps.yaml'", + "note_installed": "Po zainstalowaniu dodatek będzie znajdować się w", + "note_integration": "musisz jeszcze dodać integrację do pliku 'configuration.yaml'", + "note_plugin": "musisz jeszcze dodać wtyczkę do konfiguracji interfejsu użytkownika (plik 'ui-lovelace.yaml' lub edytor interfejsu użytkownika)", + "note_plugin_post_107": "nadal musisz dodać go do konfiguracji Lovelace ('configuration.yaml' lub edytora zasobów '\/config\/lovelace\/resources')", + "open_issue": "Powiadom o problemie", + "open_plugin": "Otwórz element", + "reinstall": "Przeinstaluj", + "repository": "Repozytorium", + "restart_home_assistant": "Uruchom ponownie Home Assistant'a", + "show_beta": "Wyświetl wydania beta", + "uninstall": "Odinstaluj", + "update_information": "Uaktualnij dane", + "upgrade": "Uaktualnij" + }, + "search": { + "installed": "Wyszukaj zainstalowane repozytoria", + "installed_new": "Wyszukaj zainstalowane lub nowe repozytoria", + "placeholder": "Wyszukaj repozytorium" + }, + "sections": { + "about": { + "description": "Informacje o HACS", + "title": "O HACS" + }, + "addon": { + "description": "W HACS nie ma dodatków, ale możesz kliknąć tutaj, aby przejść do Supervisora", + "title": "Dodatki" + }, + "automation": { + "description": "Skrypty Pythona, aplikacje AppDaemon i NetDaemon", + "title": "Automatyzacje" + }, + "frontend": { + "description": "Motywy, niestandardowe karty i inne elementy interfejsu użytkownika", + "title": "Interfejs użytkownika" + }, + "integrations": { + "description": "Niestandardowe integracje (custom_components)", + "title": "Integracje" + }, + "pending_repository_upgrade": "Używasz wersji {installed}, wersja {available} jest dostępna" + }, + "settings": { + "add_custom_repository": "DODAJ REPOZYTORIUM NIESTANDARDOWE", + "adding_new_repo": "Dodawanie nowego repozytorium '{repo}'", + "adding_new_repo_category": "Z kategorią '{category}'.", + "bg_task_custom": "Niestandardowe repozytoria są ukryte podczas wykonywania zadań w tle.", + "category": "Kategoria", + "compact_mode": "Tryb kompaktowy", + "custom_repositories": "REPOZYTORIA NIESTANDARDOWE", + "delete": "Usuń", + "display": "Sposób wyświetlania", + "grid": "kratka", + "hacs_repo": "Repozytorium HACS", + "hidden_repositories": "ukryte repozytoria", + "missing_category": "Musisz wybrać kategorię", + "open_repository": "Otwórz repozytorium", + "reload_data": "Wczytaj ponownie dane", + "reload_window": "Załaduj ponownie okno", + "repository_configuration": "Konfiguracja repozytorium", + "save": "Zapisz", + "table": "tabela", + "table_view": "Widok tabeli", + "unhide": "pokaż", + "upgrade_all": "Uaktualnij wszystkie" + }, + "store": { + "ascending": "rosnąco", + "clear_new": "Wyczyść wszystkie nowe repozytoria", + "descending": "malejąco", + "last_updated": "Ostatnia aktualizacja", + "name": "Nazwa", + "new_repositories": "Nowe repozytoria", + "new_repositories_note": "Jest ponad 10 nowych repozytoriów, jeśli chcesz je wyczyścić, kliknij menu z trzema kropkami w prawym górnym rogu i odrzuć je wszystkie.", + "no_repositories": "Brak repozytoriów", + "no_repositories_desc1": "Wygląda na to, że nie masz jeszcze zainstalowanych repozytoriów w tej sekcji.", + "no_repositories_desc2": "Kliknij + w dolnym rogu, aby dodać pierwsze!", + "no_repositories_found_desc1": "W tej sekcji nie znaleziono zainstalowanych repozytoriów pasujących do \"{searchInput}\".", + "no_repositories_found_desc2": "Spróbuj wyszukać czegoś innego!", + "pending_upgrades": "Oczekujące aktualizacje", + "placeholder_search": "Wprowadź wyszukiwane hasło...", + "sort": "sortowanie", + "stars": "Gwiazdki", + "status": "Status" + }, + "time": { + "ago": "temu", + "day": "dzień", + "days": "dni", + "hour": "godzina", + "hours": "godziny", + "minute": "minuta", + "minutes": "minuty", + "month": "miesiąc", + "months": "miesięcy", + "one": "Jeden", + "one_day_ago": "jeden dzień temu", + "one_hour_ago": "jedna godzina temu", + "one_minute_ago": "jedna minuta temu", + "one_month_ago": "jeden miesiąc temu", + "one_second_ago": "jedna sekunda temu", + "one_year_ago": "jeden rok temu", + "second": "sekunda", + "seconds": "sekundy", + "x_days_ago": "{x} dni temu", + "x_hours_ago": "{x} godzin(y) temu", + "x_minutes_ago": "{x} minut(y) temu", + "x_months_ago": "{x} miesi(ące\/ęcy) temu", + "x_seconds_ago": "{x} sekund(y) temu", + "x_years_ago": "{x} lat(a) temu", + "year": "rok", + "years": "lata" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/pt-BR.json b/custom_components/hacs/translations/pt-BR.json new file mode 100644 index 0000000..d97bec8 --- /dev/null +++ b/custom_components/hacs/translations/pt-BR.json @@ -0,0 +1,356 @@ +{ + "common": { + "about": "Sobre", + "add": "adicionar", + "appdaemon": "AppDaemon", + "appdaemon_apps": "Aplicativos AppDaemon", + "appdaemon_plural": "Aplicativos AppDaemon", + "background_task": "Tarefa em segundo plano em execução, esta página será recarregada quando terminar.", + "check_log_file": "Verifique seu arquivo de log para obter mais detalhes.", + "continue": "Continuar", + "disabled": "Desativado", + "documentation": "Documentação", + "element": "elemento", + "hacs_is_disabled": "HACS está desativado", + "ignore": "Ignorar", + "install": "Instalar", + "installed": "instalado", + "integration": "Integração", + "integration_plural": "Integrações", + "integrations": "Integrações", + "lovelace": "Lovelace", + "lovelace_element": "Elemento do Lovelace", + "lovelace_elements": "Elementos do lovelace", + "manage": "gerenciar", + "netdaemon": "NetDaemon", + "netdaemon_apps": "NetDaemon Apps", + "netdaemon_plural": "Aplicativos NetDaemon", + "plugin": "Lovelace", + "plugin_plural": "Elementos do Lovelace", + "plugins": "Elementos do Lovelace", + "python_script": "Script Python", + "python_script_plural": "Scripts python", + "python_scripts": "Scripts python", + "repositories": "Repositórios", + "repository": "Repositório", + "settings": "configurações", + "theme": "Tema", + "theme_plural": "Temas", + "themes": "Temas", + "uninstall": "Desinstalar", + "update": "Atualizar", + "version": "Versão" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Tem certeza de que deseja adicionar isso aos seus recursos do Lovelace?", + "bg_task": "A ação é desativada enquanto as tarefas de fundo estão sendo executadas.", + "cancel": "Cancelar", + "continue": "Tem certeza que quer continuar?", + "delete": "Tem certeza de que deseja excluir '{item}'?", + "delete_installed": "'{item}' está instalado, é necessário desinstalá-lo para poder excluí-lo.", + "exist": "{item} já existe", + "generic": "Tem certeza?", + "home_assistant_is_restarting": "Espere, o Home Assistant está agora a reiniciar.", + "home_assistant_version_not_correct": "Você está executando a versão Home Assistant '{haversion}', mas este repositório requer que a versão mínima '{minversion}' esteja instalada.", + "no": "Não", + "no_upgrades": "Não há atualizações pendentes", + "ok": "OK", + "overwrite": "Fazer isso irá substituí-lo.", + "reload_data": "Isso recarrega os dados de todos os repositórios que o HACS conhece e levará algum tempo para concluir.", + "restart_home_assistant": "Tem certeza de que deseja reiniciar o Home Assistant?", + "uninstall": "Tem certeza de que deseja desinstalar '{item}'?", + "upgrade_all": "Isso atualizará todos esses repositórios, verifique se você leu as notas de versão de todos eles antes de continuar.", + "yes": "Sim" + }, + "dialog_about": { + "frontend_version": "Versão do frontend", + "installed_repositories": "Repositórios instalados", + "integration_version": "Versão da integração", + "useful_links": "Links úteis" + }, + "dialog_add_repo": { + "limit": "Apenas os 100 primeiros repositórios são mostrados, use a pesquisa para filtrar o que você precisa", + "no_match": "Nenhum repositório encontrado correspondente ao seu filtro", + "sort_by": "Ordenar por", + "title": "Novo repositório" + }, + "dialog_custom_repositories": { + "category": "Categoria", + "no_category": "Categoria ausente", + "no_repository": "Repositório ausente", + "title": "Repositórios personalizados", + "url_placeholder": "Adicionar URL de repositório personalizado" + }, + "dialog_info": { + "author": "Autor", + "downloads": "Downloads", + "install": "Instalar esse repositório no HACS", + "loading": "Carregando informações...", + "no_info": "O desenvolvedor não forneceu mais informações para este repositório", + "open_issues": "Problemas em aberto", + "open_repo": "Abrir repositório", + "stars": "Estrelas", + "version_installed": "Versão instalada" + }, + "dialog_install": { + "restart": "Lembre-se de que você precisa reiniciar o Home Assistant para que as alterações nas integrações (custom_components) sejam aplicadas.", + "select_version": "Selecionar versão", + "show_beta": "Mostrar versões beta", + "type": "Tipo", + "url": "URL" + }, + "dialog_removed": { + "link": "Link externo para mais informações", + "name": "Nome do Repositório", + "reason": "Motivo da remoção", + "type": "Tipo da Remoção" + }, + "dialog_update": { + "available_version": "Versão disponível", + "changelog": "Registro de mudanças", + "installed_version": "Versão instalada", + "releasenotes": "Notas de lançamento para {release}", + "title": "Atualização pendente" + }, + "entry": { + "information": "Informações", + "intro": "Atualizações e mensagens importantes serão mostradas aqui, quando necessário", + "messages": { + "disabled": { + "content": "Verifique seu arquivo de log para mais detalhes.", + "title": "O HACS está desativado" + }, + "has_pending_tasks": { + "content": "Alguns repositórios podem não aparecer até que isso seja concluído", + "title": "Tarefas em segundo plano pendentes" + }, + "removed": "Repositório {repository} removido", + "resources": { + "content": "Existem {number} elementos do Lovelace que não estão carregados corretamente no Lovelace.", + "title": "Não carregado no Lovelace" + }, + "restart": { + "content": "Existem {number} integrações que requerem um reinício do Home Assistant. Você pode fazer isso na seção 'Controles do Servidor' na parte de configuração do Home Assistant UI.", + "title": "Reinicialização pendente" + }, + "startup": { + "content": "O HACS está sendo iniciado, durante esse período algumas informações podem estar ausentes ou incorretas", + "title": "O HACS está iniciando" + }, + "wrong_frontend_installed": { + "content": "Você instalou a versão {running} do frontend HACS, mas a versão {expected} era esperada. Se você vir esta mensagem, o Home Assistant não conseguiu instalar a nova versão. Por favor tente reiniciar o Home Assistant.", + "title": "Versão frontend inesperada" + }, + "wrong_frontend_loaded": { + "content": "Você está executando a versão {running} do frontend HACS, mas a versão {expected} era esperada. Por favor limpe o cache do seu navegador.", + "title": "Versão frontend inesperada" + } + }, + "pending_updates": "Atualizações pendentes" + }, + "menu": { + "about": "Sobre o HACS", + "clear": "Limpar todos os novos", + "custom_repositories": "Repositórios personalizados", + "dismiss": "Limpar todos os novos repositórios", + "documentation": "Documentação", + "open_issue": "Relatar problema", + "reload": "Recarregar janela" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Habilitar AppDaemon apps descoberta & rastreamento", + "country": "Filtrar pelo código do país.", + "debug": "Ative a depuração.", + "experimental": "Ativar recursos experimentais", + "netdaemon": "Habilitar NetDaemon apps descoberta & rastreamento", + "not_in_use": "Não está em uso com o YAML", + "release_limit": "Número de lançamentos a serem exibidos.", + "sidepanel_icon": "Icone do painel lateral", + "sidepanel_title": "Titulo do painel lateral" + } + } + } + }, + "repository_banner": { + "config_flow": "Essa integração oferece suporte ao config_flow, o que significa que agora você pode acessar a seção de integração da sua interface do usuário para configurá-lo.", + "config_flow_title": "Configuração de interface do usuário suportada", + "integration_not_loaded": "Esta integração não está carregada no Home Assistant.", + "no_restart_required": "Não é necessário reiniciar", + "not_loaded": "Não carregado", + "plugin_not_loaded": "Este elemento não é adicionado aos seus recursos do Lovelace.", + "restart": "Você precisa reiniciar o Home Assistant.", + "restart_pending": "Reinicialização pendente" + }, + "repository_card": { + "dismiss": "Dispensar", + "hide": "Esconder", + "information": "Informações", + "new_repository": "Novo repositório", + "not_loaded": "Não carregado", + "open_issue": "Relatar problema", + "open_source": "Código aberto", + "pending_restart": "Reinicialização pendente", + "pending_update": "Atualização pendente", + "reinstall": "Reinstalar", + "report": "Denunciar para remoção", + "update_information": "Atualizar informações" + }, + "repository": { + "add_to_lovelace": "Adicionar a Lovelace", + "authors": "Autores", + "available": "Disponível", + "back_to": "Voltar para", + "changelog": "Changelog", + "downloads": "Downloads", + "flag_this": "Sinalizar isso", + "frontend_version": "Versão Frontend", + "github_stars": "Estrelas de GitHub", + "goto_integrations": "Ir para integrações", + "hide": "Esconder", + "hide_beta": "Esconder beta", + "install": "Instalar", + "installed": "Instalado", + "lovelace_copy_example": "Copie este exemplo para seu clipboard", + "lovelace_instruction": "Quando você adicionar isso à sua configuração do lovelace, use este", + "lovelace_no_js_type": "Não foi possível determinar o tipo desse elemento, verifique o repositório.", + "newest": "O mais novo", + "note_appdaemon": "Você ainda precisa adicioná-lo ao seu arquivo 'apps.yaml'", + "note_installed": "Quando instalado, ele estará localizado em", + "note_integration": "Você ainda precisa adicioná-lo ao seu arquivo 'configuration.yaml'", + "note_plugin": "você ainda precisará adicioná-lo à sua configuração do lovelace ('ui-lovelace.yaml' ou o editor de configuração da interface do usuário)", + "note_plugin_post_107": "você ainda precisará adicioná-lo à sua configuração do lovelace ('configuration.yaml' ou o editor de recursos '\/config \/lovelace\/resources')", + "open_issue": "Open issue", + "open_plugin": "Elemento aberto", + "reinstall": "Reinstalar", + "repository": "Repositório", + "restart_home_assistant": "Reiniciar Home Assistant", + "show_beta": "Mostrar beta", + "uninstall": "Desinstalar", + "update_information": "Atualizar informações", + "upgrade": "Atualizar" + }, + "search": { + "installed": "Buscar repositórios instalados", + "installed_new": "Buscar repositórios instalados ou novos", + "placeholder": "Procurar repositório" + }, + "sections": { + "about": { + "description": "Exibir informações sobre o HACS", + "title": "Sobre" + }, + "automation": { + "description": "É aqui que você encontra python_scripts, aplicativos AppDaemon e aplicativos NetDaemon", + "title": "Automação" + }, + "frontend": { + "description": "É aqui que você encontra temas, cartões personalizados e outros elementos para o lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "É aqui que você encontra integrações personalizadas (custom_components)", + "title": "Integrações" + }, + "pending_repository_upgrade": "Você está executando a versão {installed}, a versão {available} está disponível" + }, + "settings": { + "add_custom_repository": "ADICIONAR REPOSITÓRIO PERSONALIZADO", + "adding_new_repo": "Adicionando novo repositório '{repo}'", + "adding_new_repo_category": "Com a categoria '{category}'.", + "bg_task_custom": "Os repositórios personalizados ficam ocultos enquanto as tarefas de fundo estão em execução.", + "category": "Categoria", + "compact_mode": "Modo compacto", + "custom_repositories": "REPOSITÓRIOS PERSONALIZADOS", + "delete": "Deletar", + "display": "Display", + "grid": "Grade", + "hacs_repo": "HACS repo", + "hidden_repositories": "repositórios ocultos", + "missing_category": "Você precisa selecionar uma categoria", + "open_repository": "Repositório aberto", + "reload_data": "Recarregar dados", + "reload_window": "Recarregar janela", + "repository_configuration": "Configuração do Repositório", + "save": "Salvar", + "table": "Tabela", + "table_view": "Visualização em tabela", + "unhide": "reexibir", + "upgrade_all": "Atualizar tudo" + }, + "store": { + "ascending": "ascendente", + "clear_new": "Limpar todos os novos repositórios", + "descending": "descendente", + "last_updated": "Última atualização", + "name": "Nome", + "new_repositories": "Novos Repositórios", + "new_repositories_note": "Você tem mais de 10 novos repositórios sendo mostrados aqui, se quiser limpar todos eles clique nos 3 pontos no canto superior direito e dispense-os.", + "no_repositories": "Nenhum repositório", + "no_repositories_desc1": "Parece que você ainda não tem nenhum repositório instalado nesta seção.", + "no_repositories_desc2": "Clique no + no canto inferior para adicionar o seu primeiro repositório!", + "no_repositories_found_desc1": "Nenhum repositório instalado foi encontrado que corresponda a \"{searchInput}\" nesta seção.", + "no_repositories_found_desc2": "Tente procurar por outra coisa!", + "pending_upgrades": "Atualizações pendentes", + "placeholder_search": "Por favor insira um termo de pesquisa...", + "sort": "ordenar", + "stars": "Estrelas", + "status": "Status" + }, + "time": { + "ago": "atrás", + "day": "dia", + "days": "dias", + "hour": "hora", + "hours": "horas", + "minute": "minuto", + "minutes": "minutos", + "month": "mês", + "months": "meses", + "one": "Um", + "one_day_ago": "um dia atrás", + "one_hour_ago": "uma hora atrás", + "one_minute_ago": "um minuto atrás", + "one_month_ago": "um mês atrás", + "one_second_ago": "um segundo atrás", + "one_year_ago": "um ano atrás", + "second": "segundo", + "seconds": "segundos", + "x_days_ago": "{x} dias atrás", + "x_hours_ago": "{x} horas atrás", + "x_minutes_ago": "{x} minutos atrás", + "x_months_ago": "{x} meses atrás", + "x_seconds_ago": "{x} segundos atrás", + "x_years_ago": "{x} anos atrás", + "year": "ano", + "years": "anos" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/pt.json b/custom_components/hacs/translations/pt.json new file mode 100644 index 0000000..d40cf50 --- /dev/null +++ b/custom_components/hacs/translations/pt.json @@ -0,0 +1,355 @@ +{ + "common": { + "about": "Sobre", + "add": "adicionar", + "appdaemon_apps": "Aplicações AppDaemon", + "appdaemon_plural": "Aplicações AppDaemon", + "background_task": "Está a ser executada uma tarefa em segundo plano, esta página será recarregada quando terminar.", + "check_log_file": "Verifique o seu ficheiro de log para obter mais detalhes.", + "continue": "Continuar", + "disabled": "Desativado", + "documentation": "Documentação", + "element": "elemento", + "hacs_is_disabled": "HACS está desativado", + "ignore": "Ignorar", + "install": "Instalar", + "installed": "instalado", + "integration": "Integração", + "integration_plural": "Integrações", + "integrations": "Integrações", + "lovelace": "Lovelace", + "lovelace_element": "Elemento Lovelace", + "lovelace_elements": "Elementos Lovelace", + "manage": "gerir", + "netdaemon": "NetDaemon", + "netdaemon_apps": "Aplicações NetDaemon", + "netdaemon_plural": "Aplicações NetDaemon", + "plugin": "Lovelace", + "plugin_plural": "Elementos Lovelace", + "plugins": "Elementos Lovelace", + "python_script": "Script Python", + "python_script_plural": "Scripts Python", + "python_scripts": "Scripts Python", + "repositories": "Repositórios", + "repository": "Repositório", + "settings": "configurações", + "theme": "Tema", + "theme_plural": "Temas", + "themes": "Temas", + "uninstall": "Desinstalar", + "update": "Atualizar", + "version": "Versão" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Tem certeza que deseja adicionar aos recursos do Lovelace?", + "bg_task": "Esta ação é desativada enquanto as tarefas em segundo plano estão a ser executadas.", + "cancel": "Cancelar", + "continue": "Tem a certeza que deseja continuar?", + "delete": "Tem a certeza que deseja apagar '{item}'?", + "delete_installed": "'{item}' está instalado, é preciso desinstalá-lo antes de o poder apagar.", + "exist": "{item} já existe", + "generic": "Tem a certeza?", + "home_assistant_is_restarting": "Aguarde, o Home Assistant está a reiniciar.", + "home_assistant_version_not_correct": "Está a executar a versão '{haversion}' do Home Assistant, mas este repositório requer a versão mínima '{minversion}' para ser instalado.", + "no": "Não", + "no_upgrades": "Não há atualizações pendentes", + "ok": "OK", + "overwrite": "Ao fazer esta ação irá substituir o ficheiro atual.", + "reload_data": "Isto irá recarregar os dados de todos os repositórios que a HACS conhece, irá demorar algum tempo até terminar.", + "restart_home_assistant": "Tem a certeza que deseja reiniciar o Home Assistant?", + "uninstall": "Tem a certeza que deseja desinstalar '{item}'?", + "upgrade_all": "Isto irá actualizar todos estes repositórios, certifique-se de que leu as notas de lançamento de todos antes de continuar.", + "yes": "Sim" + }, + "dialog_about": { + "frontend_version": "Versão Frontend", + "installed_repositories": "Repositórios instalados", + "integration_version": "Versão de integração", + "useful_links": "Links úteis" + }, + "dialog_add_repo": { + "limit": "Apenas os 100 primeiros repositórios serão mostrados, use a pesquisa para filtrar o que precisa", + "no_match": "Não foram encontrados repositórios que correspondam ao filtro", + "sort_by": "Ordenar por", + "title": "Adicionar repositório" + }, + "dialog_custom_repositories": { + "category": "Categoria", + "no_category": "Categoria em falta", + "no_repository": "Repositório em falta", + "title": "Repositórios personalizados", + "url_placeholder": "Adicionar URL do repositório personalizado" + }, + "dialog_info": { + "author": "Autor", + "downloads": "Transferências", + "install": "Instalar este repositório no HACS", + "loading": "A carregar informações...", + "no_info": "O developer não forneceu mais informações sobre este repositório", + "open_issues": "Questões em aberto", + "open_repo": "Abrir Repositório", + "stars": "Estrelas", + "version_installed": "Versão instalada" + }, + "dialog_install": { + "restart": "Lembre-se de que é preciso reiniciar o Home Assistant para que as alterações das integrações (custom_components) sejam aplicadas.", + "select_version": "Selecionar versão", + "show_beta": "Mostrar versões beta", + "type": "Tipo", + "url": "URL" + }, + "dialog_removed": { + "link": "Link externo para mais informações", + "name": "Nome do repositório", + "reason": "Motivo de remoção", + "type": "Tipo de remoção" + }, + "dialog_update": { + "available_version": "Versão disponível", + "changelog": "Changelog", + "installed_version": "Versão instalada", + "releasenotes": "Notas de lançamento para {release}", + "title": "Atualização pendente" + }, + "entry": { + "information": "Informações", + "intro": "Atualizações e mensagens importantes serão mostradas aqui", + "messages": { + "disabled": { + "content": "Verifique o seu ficheiro de log para obter mais detalhes", + "title": "HACS está desativado" + }, + "has_pending_tasks": { + "content": "Alguns repositórios podem não aparecer até que isso seja concluído", + "title": "Tarefas em segundo plano pendentes" + }, + "removed": "Repositório '{repository}' removido", + "resources": { + "content": "Tem {number} elementos que não são carregados corretamente em Lovelace.", + "title": "Não carregado em Lovelace" + }, + "restart": { + "content": "Tem {number} integrações que exigem uma reinicialização do Home Assistant, pode fazer isso a partir da secção 'Controlo do Servidor' na parte de configuração do Home Assistant.", + "title": "Reinicialização pendente" + }, + "startup": { + "content": "O HACS está a iniciar. Durante este tempo, algumas informações podem estar ausentes ou incorretas", + "title": "O HACS está a iniciar" + }, + "wrong_frontend_installed": { + "content": "Tem a versão {running} do frontend HACS instalado, mas a versão {expected} é a mais atualizada, se está a ver esta mensagem e o Home Assistant não foi capaz de instalar a nova versão, tente reiniciar o Home Assistant.", + "title": "Versão do frontend inesperada" + }, + "wrong_frontend_loaded": { + "content": "Está a executar a versão {running} do frontend HACS, mas a versão {expected} é a mais atualizada, deve limpar a cache do seu browser.", + "title": "Versão do frontend inesperada" + } + }, + "pending_updates": "Atualizações pendentes" + }, + "menu": { + "about": "Sobre o HACS", + "clear": "Limpar todos os recentes", + "custom_repositories": "Repositórios personalizados", + "dismiss": "Dispensar todos os novos repositórios", + "documentation": "Documentação", + "open_issue": "Questão em aberto", + "reload": "Recarregar janela" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Ativar a localização e o seguimento de aplicações AppDaemon", + "country": "Filtrar com o código do país.", + "debug": "Ativar depuração.", + "experimental": "Ativar recursos experimentais", + "netdaemon": "Ativar a localização e o seguimento de aplicações NetDaemon", + "not_in_use": "Não está a ser usado com YAML", + "release_limit": "Número de lançamentos a mostrar.", + "sidepanel_icon": "Ícone no painel lateral", + "sidepanel_title": "Título no painel lateral" + } + } + } + }, + "repository_banner": { + "config_flow": "Esta integração suporta o config_flow, o que significa que agora pode ir para a seção de integração do IU para a configurar.", + "config_flow_title": "Configuração UI suportada", + "integration_not_loaded": "Esta integração não foi carregada no Home Assistant.", + "no_restart_required": "Não é necessário reiniciar", + "not_loaded": "Não carregado", + "plugin_not_loaded": "Este elemento não é adicionado aos recursos Lovelace.", + "restart": "É necessário reiniciar o Home Assistant.", + "restart_pending": "Reinício pendente" + }, + "repository_card": { + "dismiss": "dispensar", + "hide": "Esconder", + "information": "Informações", + "new_repository": "Novo repositório", + "not_loaded": "Não carregado", + "open_issue": "Questão em aberto", + "open_source": "Código aberto", + "pending_restart": "Reinicialização pendente", + "pending_update": "Atualização pendente", + "reinstall": "Reinstalar", + "report": "Motivo para remover", + "update_information": "Atualizar informações" + }, + "repository": { + "add_to_lovelace": "Adicionar ao Lovelace", + "authors": "Autores", + "available": "Disponível", + "back_to": "Voltar", + "changelog": "Histórico de alterações", + "downloads": "Transferências", + "flag_this": "Marcar isto", + "frontend_version": "Versão Frontend", + "github_stars": "Estrelas do GitHub", + "goto_integrations": "Ir para as integrações", + "hide": "Ocultar", + "hide_beta": "Ocultar beta", + "install": "Instalar", + "installed": "Instalado", + "lovelace_copy_example": "Copiar o exemplo para a área de transferência", + "lovelace_instruction": "Quando for adicionar à sua configuração do lovelace, use este", + "lovelace_no_js_type": "Não foi possível determinar o tipo do elemento, verifique o repositório.", + "newest": "mais recente", + "note_appdaemon": "ainda é preciso adicioná-lo ao ficheiro 'apps.yaml'", + "note_installed": "Quando estiver instalado, a localização será", + "note_integration": "ainda é preciso adicioná-lo ao ficheiro 'configuration.yaml'", + "note_plugin": "ainda é preciso adicioná-lo à configuração do lovelace ('ui-lovelace.yaml' ou o editor de configuração de UI)", + "note_plugin_post_107": "ainda é preciso adicioná-lo à configuração do lovelace ('configuration.yaml' ou no editor '\/config\/lovelace\/resources')", + "open_issue": "Questão em aberto", + "open_plugin": "Abrir elemento", + "reinstall": "Reinstalar", + "repository": "Repositório", + "restart_home_assistant": "Reiniciar o Home Assistant", + "show_beta": "Mostrar beta", + "uninstall": "Desinstalar", + "update_information": "Atualizar informações", + "upgrade": "Atualizar" + }, + "search": { + "installed": "Pesquisar repositórios instalados", + "installed_new": "Pesquisar novos repositórios ou instalados", + "placeholder": "Procurar repositório" + }, + "sections": { + "about": { + "description": "Mostrar informações sobre o HACS", + "title": "Sobre" + }, + "automation": { + "description": "Aqui encontra os python_scripts, aplicações AppDaemon e NetDaemon", + "title": "Automação" + }, + "frontend": { + "description": "Aqui encontra os temas, cartões personalizados e outros elementos para o lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Aqui encontra as integrações personalizadas (custom_components)", + "title": "Integrações" + }, + "pending_repository_upgrade": "Está a executar a versão {installed} , mas a versão {available} já está disponível" + }, + "settings": { + "add_custom_repository": "ADICIONAR REPOSITÓRIO PERSONALIZADO", + "adding_new_repo": "Adicionando novo repositório '{repo}'", + "adding_new_repo_category": "Com a categoria '{category}'.", + "bg_task_custom": "Repositórios personalizados ficam ocultos, enquanto as tarefas em segundo plano estão a ser executadas.", + "category": "Categoria", + "compact_mode": "Modo compacto", + "custom_repositories": "REPOSITÓRIOS PERSONALIZADOS", + "delete": "Eliminar", + "display": "Mostrar", + "grid": "Grade", + "hacs_repo": "Repositório HACS", + "hidden_repositories": "repositórios ocultos", + "missing_category": "É preciso selecionar uma categoria", + "open_repository": "Abrir Repositório", + "reload_data": "Recarregar dados", + "reload_window": "Recarregar janela", + "repository_configuration": "Configuração do Repositório", + "save": "Guardar", + "table": "Tabela", + "table_view": "Vista de tabela", + "unhide": "Mostrar", + "upgrade_all": "Atualizar tudo" + }, + "store": { + "ascending": "ascendente", + "clear_new": "Limpar todos os novos repositórios", + "descending": "descendente", + "last_updated": "Última atualização", + "name": "Nome", + "new_repositories": "Novos Repositórios", + "new_repositories_note": "Tem mais de 10 novos repositórios visiveis aqui, se quiser limpar todos clique nos 3 pontos no canto superior direito e dispense todos.", + "no_repositories": "Sem repositórios", + "no_repositories_desc1": "Parece que ainda não possui nenhum repositório instalado nesta seção.", + "no_repositories_desc2": "Clique no \"+\", no canto inferior para adicionar o seu primeiro!", + "no_repositories_found_desc1": "Nenhum repositório instalado, correspondente a \"{searchInput}\" foi encontrado nesta secção.", + "no_repositories_found_desc2": "Tente procurar outra coisa!", + "pending_upgrades": "Atualizações pendentes", + "placeholder_search": "Por favor, introduza um termo de pesquisa...", + "sort": "ordenar", + "stars": "Estrelas", + "status": "Estado" + }, + "time": { + "ago": "atrás", + "day": "dia", + "days": "dias", + "hour": "hora", + "hours": "horas", + "minute": "minuto", + "minutes": "minutos", + "month": "mês", + "months": "meses", + "one": "Um", + "one_day_ago": "há um dia", + "one_hour_ago": "há uma hora", + "one_minute_ago": "há um minuto", + "one_month_ago": "há um mês", + "one_second_ago": "há um segundo", + "one_year_ago": "há um ano", + "second": "segundo", + "seconds": "segundos", + "x_days_ago": "{x} dias atrás", + "x_hours_ago": "{x} horas atrás", + "x_minutes_ago": "{x} minutos atrás", + "x_months_ago": "{x} meses atrás", + "x_seconds_ago": "{x} segundos atrás", + "x_years_ago": "{x} anos atrás", + "year": "ano", + "years": "anos" + } +} \ No newline at end of file diff --git a/custom_components/hacs/.translations/ro.json b/custom_components/hacs/translations/ro.json similarity index 74% rename from custom_components/hacs/.translations/ro.json rename to custom_components/hacs/translations/ro.json index d6884aa..d577abf 100644 --- a/custom_components/hacs/.translations/ro.json +++ b/custom_components/hacs/translations/ro.json @@ -4,6 +4,7 @@ "appdaemon": "AppDaemon", "appdaemon_apps": "Aplicații AppDaemon", "background_task": "Activitatea de fundal se execută, această pagină se va reîncărca atunci când este gata.", + "check_log_file": "Verificați log-ul pentru mai multe detalii.", "continue": "Continua", "disabled": "Dezactivat", "documentation": "Documentație", @@ -11,6 +12,7 @@ "installed": "instalat", "integration": "Integrare", "integrations": "Integrări", + "manage": "administra", "netdaemon": "NetDaemon", "netdaemon_apps": "Aplicații NetDaemon", "plugin": "Plugin", @@ -24,36 +26,45 @@ "version": "Versiune" }, "config": { - "abort": { - "single_instance_allowed": "Doar o singură configurație pentru HACS este permisă." - }, - "error": { - "auth": "Token-ul de acces personal nu este corect." - }, "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, "user": { "data": { - "appdaemon": "Activați descoperirea și urmărirea aplicațiilor AppDaemon", - "netdaemon": "Activați descoperirea și urmărirea aplicațiilor NetDaemon", - "python_script": "Activați descoperirea și urmărirea python_scripts", - "sidepanel_icon": "Pictogramă Panou lateral", - "sidepanel_title": "Titlu panou lateral", - "theme": "Activați descoperirea și urmărirea temelor", - "token": "Token de acces personal GitHub" + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" }, - "description": "Dacă aveți nevoie de ajutor pentru configurare, uitați-vă aici: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" } }, "title": "HACS (Home Assistant Community Store)" }, "confirm": { + "add_to_lovelace": "Ești sigur că vrei să adaugi asta la resursele tale Lovelace?", "bg_task": "Acțiunea este dezactivată în timp ce activitățile de fundal se execută.", "cancel": "Anulare", + "continue": "Esti sigur ca vrei sa continui?", "delete": "Sigur doriți să ștergeți '{item}'?", "exist": "{item} există deja", + "generic": "Ești sigur?", + "home_assistant_is_restarting": "Asteptati, Home Assistant repornește.", "no": "Nu", + "no_upgrades": "Nu există actualizări în curs", "ok": "OK", + "overwrite": "Făcând acest lucru, îl va suprascrie.", + "restart_home_assistant": "Sigur doriți să reporniți Home Assistant?", "uninstall": "Sigur doriți să dezinstalați '{item}'?", "yes": "Da" }, @@ -63,6 +74,7 @@ "data": { "appdaemon": "Activați descoperirea și urmărirea aplicațiilor AppDaemon", "country": "Filtrează cu codul țării.", + "debug": "Activează depanarea.", "experimental": "Activați funcțiile experimentale", "netdaemon": "Activați descoperirea și urmărirea aplicațiilor NetDaemon", "not_in_use": "Nu este utilizat cu YAML", @@ -76,6 +88,8 @@ "repository_banner": { "integration_not_loaded": "Această integrare nu este încărcată în Home Assistant.", "no_restart_required": "Nu este necesară repornirea", + "not_loaded": "Neîncărcat", + "plugin_not_loaded": "Acest plugin nu este adăugat la resursele Lovelace.", "restart": "Trebuie să reporniți Home Assistant.", "restart_pending": "Reporniți în așteptare" }, @@ -89,6 +103,7 @@ "flag_this": "Semnalizează", "frontend_version": "Versiune frontend", "github_stars": "Stele GitHub", + "goto_integrations": "Mergi la integrări", "hide": "Ascunde", "hide_beta": "Ascundere beta", "install": "Instalează", @@ -136,10 +151,12 @@ }, "store": { "ascending": "ascendent", + "clear_new": "Ștergeți toate depozitele noi", "descending": "descendent", "last_updated": "Ultima actualizare", "name": "Nume", "new_repositories": "Noi depozite", + "pending_upgrades": "Actualizări în așteptare", "placeholder_search": "Vă rugăm să introduceți un termen de căutare ...", "sort": "fel", "stars": "Stele", diff --git a/custom_components/hacs/translations/ru.json b/custom_components/hacs/translations/ru.json new file mode 100644 index 0000000..c017ae5 --- /dev/null +++ b/custom_components/hacs/translations/ru.json @@ -0,0 +1,356 @@ +{ + "common": { + "about": "О проекте", + "add": "добавить", + "appdaemon": "AppDaemon", + "appdaemon_apps": "Приложения AppDaemon", + "appdaemon_plural": "Приложения AppDaemon", + "background_task": "Выполняется фоновая задача, страница перезагрузится по готовности.", + "check_log_file": "Проверьте логи для получения более подробной информации.", + "continue": "Продолжить", + "disabled": "Отключено", + "documentation": "Документация", + "element": "элемент", + "hacs_is_disabled": "HACS отключен", + "ignore": "Игнорировать", + "install": "Установить", + "installed": "установлено", + "integration": "Интеграция", + "integration_plural": "Интеграции", + "integrations": "Интеграции", + "lovelace": "Lovelace", + "lovelace_element": "Элемент Lovelace", + "lovelace_elements": "Элементы Lovelace", + "manage": "управлять", + "netdaemon": "NetDaemon", + "netdaemon_apps": "Приложения NetDaemon", + "netdaemon_plural": "Приложения NetDaemon", + "plugin": "Lovelace", + "plugin_plural": "Элементы Lovelace", + "plugins": "Плагины", + "python_script": "Скрипт Python", + "python_script_plural": "Скрипты Python", + "python_scripts": "Скрипты Python", + "repositories": "Репозитории", + "repository": "Репозиторий", + "settings": "настройки", + "theme": "Тема", + "theme_plural": "Темы", + "themes": "Темы", + "uninstall": "Удалить", + "update": "Обновить", + "version": "Версия" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Вы уверены, что хотите добавить это в ресурсы Lovelace?", + "bg_task": "Действие отключено во время выполнения фоновых задач.", + "cancel": "Отмена", + "continue": "Вы уверены, что хотите продолжить?", + "delete": "Вы уверены, что хотите удалить '{item}'?", + "delete_installed": "'{item}' установлен, вам нужно нажать 'Удалить', чтобы удалить его.", + "exist": "{item} уже существует.", + "generic": "Вы уверены?", + "home_assistant_is_restarting": "Подожди, теперь Home Assistant перезагружается.", + "home_assistant_version_not_correct": "Вы используете Home Assistant версии '{haversion}', но данный репозиторий требует минимальную установленную версию '{minversion}'.", + "no": "Нет", + "no_upgrades": "Нет обновлений", + "ok": "ОК", + "overwrite": "После подтверждения файлы будут перезаписаны.", + "reload_data": "Выполняется перезагрузка данных всех репозиториев в HACS, это займет некоторое время.", + "restart_home_assistant": "Вы уверены, что хотите перезапустить Home Assistant?", + "uninstall": "Вы уверены, что хотите удалить '{item}'?", + "upgrade_all": "Это произведёт обновление всех этих репозиториев, убедитесь, что вы прочитали примечания к выпуску для каждого из них, прежде чем продолжить.", + "yes": "Да" + }, + "dialog_about": { + "frontend_version": "Версия интерфейса", + "installed_repositories": "Установленные репозитории", + "integration_version": "Версия интеграции", + "useful_links": "Полезные ссылки" + }, + "dialog_add_repo": { + "limit": "Показаны только первые 100 репозиториев, используйте поиск для фильтрации того, что вам нужно", + "no_match": "Не найдено репозиторий, соответствующих фильтру", + "sort_by": "Сортировать по", + "title": "Новый репозиторий" + }, + "dialog_custom_repositories": { + "category": "Категория", + "no_category": "Категория не указана", + "no_repository": "Репозиторий не указан", + "title": "Пользовательские репозитории", + "url_placeholder": "Добавить пользовательский URL-адрес репозитория" + }, + "dialog_info": { + "author": "Автор", + "downloads": "Загрузки", + "install": "Установить этот репозиторий в HACS", + "loading": "Загрузка информации...", + "no_info": "Разработчик не предоставил никакой дополнительной информации для этого репозитория", + "open_issues": "Открытые вопросы", + "open_repo": "Открыть репозиторий", + "stars": "Звёзды", + "version_installed": "Установлена версия" + }, + "dialog_install": { + "restart": "Помните, что вам нужно перезапустить Home Assistant, прежде чем будут применены изменения в интеграциях (custom_components).", + "select_version": "Выберите версию", + "show_beta": "Показывать бета-версии", + "type": "Тип", + "url": "Ссылка" + }, + "dialog_removed": { + "link": "Внешняя ссылка для получения дополнительной информации", + "name": "Имя репозитория", + "reason": "Причина удаления", + "type": "Тип удаления" + }, + "dialog_update": { + "available_version": "Доступная версия", + "changelog": "Изменения", + "installed_version": "Установленная версия", + "releasenotes": "Примечания к выпуску для {release}", + "title": "Обновление в ожидании" + }, + "entry": { + "information": "Информация", + "intro": "Обновления и важные сообщения будут отображаться здесь, если таковые имеются", + "messages": { + "disabled": { + "content": "Проверьте логи для получения более подробной информации.", + "title": "HACS отключен" + }, + "has_pending_tasks": { + "content": "Некоторые репозитории могут не отображаться до тех пор, пока это не будет завершено", + "title": "Выполняются фоновые задачи" + }, + "removed": "Репозиторий удален '{repository}'", + "resources": { + "content": "У вас есть {number} элементов Lovelace, которые не загружаются должным образом.", + "title": "Не загружено в Lovelace" + }, + "restart": { + "content": "У вас есть {number} интеграций, которые требуют перезагрузки Home Assistant, Вы можете сделать это из раздела 'Сервер' в разделе конфигурации пользовательского интерфейса Home Assistant.", + "title": "В ожидании перезапуска" + }, + "startup": { + "content": "HACS запускается, в течение этого времени некоторые сведения могут отсутствовать или быть неверными", + "title": "HACS запускается" + }, + "wrong_frontend_installed": { + "content": "Вы используете версию {running} интерфейса HACS, однако ожидаемая версия — {expected}; если Вы видите это сообщение, то Home Assistant не смог установить новую версию интерфейса; попробуйте перезапустить Home Assistant", + "title": "Неожиданная версия интерфейса" + }, + "wrong_frontend_loaded": { + "content": "Вы используете версию {running} интерфейса HACS, однако ожидаемая версия — {expected}, попробуйте очистить кеш браузера.", + "title": "Неожиданная версия интерфейса" + } + }, + "pending_updates": "Обновления в ожидании" + }, + "menu": { + "about": "О HACS", + "clear": "Очистить все новые репозитории", + "custom_repositories": "Пользовательские репозитории", + "dismiss": "Убрать все новые репозитории", + "documentation": "Документация", + "open_issue": "Сообщить о проблеме", + "reload": "Перезагрузить окно" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Включить поиск и отслеживание приложений AppDaemon", + "country": "Фильтр по стране.", + "debug": "Включить отладку.", + "experimental": "Вкл. экспериментальные функции", + "netdaemon": "Включить поиск и отслеживание приложений NetDaemon", + "not_in_use": "Не используется с YAML", + "release_limit": "Число доступных версий.", + "sidepanel_icon": "Иконка в боковом меню", + "sidepanel_title": "Название в боковом меню" + } + } + } + }, + "repository_banner": { + "config_flow": "Эта интеграция поддерживает config_flow, это означает, что теперь вы можете перейти в раздел интеграций вашего пользовательского интерфейса, чтобы настроить её.", + "config_flow_title": "Поддерживается настройка из пользовательского интерфейса", + "integration_not_loaded": "Эта интеграция не загружена в Home Assistant.", + "no_restart_required": "Перезагрузка не требуется", + "not_loaded": "Не загружено", + "plugin_not_loaded": "Этот плагин не добавлен к ресурсам Lovelace.", + "restart": "Вам нужно перезапустить Home Assistant.", + "restart_pending": "Ожидается перезапуск" + }, + "repository_card": { + "dismiss": "убрать", + "hide": "Скрыть", + "information": "Информация", + "new_repository": "Новый репозиторий", + "not_loaded": "Не загружено", + "open_issue": "Сообщить о проблеме", + "open_source": "Открыть источник", + "pending_restart": "В ожидании перезапуска", + "pending_update": "Ожидается обновление", + "reinstall": "Переустановить", + "report": "Сообщить о нарушении", + "update_information": "Обновить информацию" + }, + "repository": { + "add_to_lovelace": "Добавить в Lovelace", + "authors": "Авторы", + "available": "Доступно", + "back_to": "Назад к", + "changelog": "Изменения", + "downloads": "Загрузки", + "flag_this": "Пожаловаться", + "frontend_version": "Версия", + "github_stars": "Звезды GitHub", + "goto_integrations": "Перейти к интеграции", + "hide": "Скрыть", + "hide_beta": "Скрыть бета", + "install": "Установить", + "installed": "Установлено", + "lovelace_copy_example": "Скопируйте пример в буфер обмена", + "lovelace_instruction": "Для добавления в конфигурацию Lovelace, используйте", + "lovelace_no_js_type": "Не удалось определить тип этого плагина, проверьте репозиторий.", + "newest": "новейшая", + "note_appdaemon": "вам всё ещё нужно добавить код для настройки приложения в файл 'apps.yaml'", + "note_installed": "После установки, файлы будут расположены в", + "note_integration": "вам всё ещё нужно добавить код для настройки интеграции в файл 'configuration.yaml'", + "note_plugin": "вам всё ещё нужно добавить код для настройки плагина в конфигурацию Lovelace ('ui-lovelace.yaml')", + "note_plugin_post_107": "вам всё равно нужно добавить его в конфигурацию Lovelace ('configuration.yaml' или редактор ресурсов по пути '\/config\/lovelace\/resources').", + "open_issue": "Сообщить о проблеме", + "open_plugin": "Открыть плагин", + "reinstall": "Переустановить", + "repository": "Репозиторий", + "restart_home_assistant": "Перезагрузка Home Assistant", + "show_beta": "Показать бета", + "uninstall": "Удалить", + "update_information": "Обновить информацию", + "upgrade": "Обновить" + }, + "search": { + "installed": "Поиск установленных репозиторий", + "installed_new": "Поиск установленных или новых репозиторий", + "placeholder": "Поиск репозитория" + }, + "sections": { + "about": { + "description": "Показать информацию о HACS", + "title": "О проекте" + }, + "automation": { + "description": "Здесь вы найдете python_scripts, приложения AppDaemon и NetDaemon.", + "title": "Автоматизация" + }, + "frontend": { + "description": "Здесь вы найдете темы, пользовательские карточки и другие элементы для Lovelace", + "title": "Пользовательский интерфейс" + }, + "integrations": { + "description": "Здесь вы найдете пользовательские интеграции (custom_components)", + "title": "Интеграции" + }, + "pending_repository_upgrade": "Вы используете версию {installed}, доступна версия {available}" + }, + "settings": { + "add_custom_repository": "ДОБАВИТЬ СВОЙ РЕПОЗИТОРИЙ", + "adding_new_repo": "Добавление нового репозитория '{repo}'", + "adding_new_repo_category": "С категорией '{category}'.", + "bg_task_custom": "Свои репозитории скрыты во время выполнения фоновых задач.", + "category": "Категория", + "compact_mode": "Компактный режим", + "custom_repositories": "СВОИ РЕПОЗИТОРИИ", + "delete": "Удалить", + "display": "Вид", + "grid": "Сетка", + "hacs_repo": "Репозиторий HACS", + "hidden_repositories": "Скрытые репозитории", + "missing_category": "Вы должны выбрать категорию", + "open_repository": "Открыть репозиторий", + "reload_data": "Перезагрузить", + "reload_window": "Перезагрузить окно", + "repository_configuration": "Конфигурация репозитория", + "save": "Сохранить", + "table": "Таблица", + "table_view": "Таблица", + "unhide": "Показать", + "upgrade_all": "Обновить всё" + }, + "store": { + "ascending": "по возрастанию", + "clear_new": "Очистить все новые репозитории", + "descending": "по убыванию", + "last_updated": "Последнее обновление", + "name": "Название", + "new_repositories": "Новые репозитории", + "new_repositories_note": "У Вас есть более 10 новых репозиториев, показанных здесь; если Вы хотите очистить их все, нажмите 3 точки в верхнем правом углу и уберите их разом.", + "no_repositories": "Нет репозиториев", + "no_repositories_desc1": "Похоже, у вас еще нет репозиториев, установленных в этом разделе.", + "no_repositories_desc2": "Нажмите на + в нижнем углу, чтобы добавить первый!", + "no_repositories_found_desc1": "В этом разделе не найдено установленных репозиторий, соответствующих \"{searchinput}\".", + "no_repositories_found_desc2": "Попробуйте искать что-нибудь другое!", + "pending_upgrades": "Ожидается обновление", + "placeholder_search": "Пожалуйста, введите условие для поиска...", + "sort": "Сортировка", + "stars": "Звезды", + "status": "Статус" + }, + "time": { + "ago": "назад", + "day": "день", + "days": "дней", + "hour": "час", + "hours": "часов", + "minute": "минута", + "minutes": "минут", + "month": "месяц", + "months": "месяца", + "one": "Одна", + "one_day_ago": "один день назад", + "one_hour_ago": "час назад", + "one_minute_ago": "минуту назад", + "one_month_ago": "месяц назад", + "one_second_ago": "одну секунду назад", + "one_year_ago": "один год назад", + "second": "секунда", + "seconds": "секунд", + "x_days_ago": "{x} дней назад", + "x_hours_ago": "{x} часов назад", + "x_minutes_ago": "{x} минут назад", + "x_months_ago": "{x} месяцев назад", + "x_seconds_ago": "{x} секунд назад", + "x_years_ago": "{x} лет назад", + "year": "год", + "years": "лет" + } +} \ No newline at end of file diff --git a/custom_components/hacs/.translations/sl.json b/custom_components/hacs/translations/sl.json similarity index 85% rename from custom_components/hacs/.translations/sl.json rename to custom_components/hacs/translations/sl.json index ada106e..4b1181b 100644 --- a/custom_components/hacs/.translations/sl.json +++ b/custom_components/hacs/translations/sl.json @@ -15,7 +15,7 @@ "manage": "upravljanje", "netdaemon": "NetDaemon", "netdaemon_apps": "NetDaemon Aplikacije", - "plugin": "vtičnik", + "plugin": "Lovelace", "plugins": "Vtičniki", "python_script": "Python skripta", "python_scripts": "Python skripte", @@ -26,25 +26,27 @@ "version": "Različica" }, "config": { - "abort": { - "single_instance_allowed": "Dovoljena je samo ena konfiguracija HACS." - }, - "error": { - "auth": "Osebni dostopni žeton ni pravilen." - }, "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, "user": { "data": { - "appdaemon": "Omogoči odkrivanje in sledenje aplikacij AppDaemon", - "netdaemon": "Omogoči odkrivanje in sledenje aplikacij NetDaemon", - "python_script": "Omogoči odkrivanje in sledenje python_scripts", - "sidepanel_icon": "Ikona stranske plošče", - "sidepanel_title": "Naslov stranske plošče", - "theme": "Omogoči odkrivanje in sledenje tem", - "token": "GitHub žeton za osebni dostop" + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" }, - "description": "Če potrebujete pomoč pri konfiguraciji, poglejte tukaj: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" } }, "title": "HACS (Home Assistant Community Store)" @@ -120,6 +122,7 @@ "note_installed": "Ko bo nameščen, se bo nahajal v", "note_integration": "Še vedno ga morate dodati v svojo 'configuration.yaml' datoteko", "note_plugin": "vendar ga še vedno morate dodati v svojo lovelace konfiguracijo ('ui-lovelace.yaml' ali \"raw\" UI config urejevalnik)", + "note_plugin_post_107": "še vedno ga morate dodati v svojo konfiguracijo lovelace ('config.yaml' ali urejevalnik virov '\/config\/lovelace\/resources')", "open_issue": "Odprite težavo", "open_plugin": "Odprite vtičnik", "reinstall": "Znova namestite", diff --git a/custom_components/hacs/.translations/sv.json b/custom_components/hacs/translations/sv.json similarity index 75% rename from custom_components/hacs/.translations/sv.json rename to custom_components/hacs/translations/sv.json index e08e9a7..220c902 100644 --- a/custom_components/hacs/.translations/sv.json +++ b/custom_components/hacs/translations/sv.json @@ -4,6 +4,7 @@ "appdaemon": "AppDaemon", "appdaemon_apps": "Appdaemon Applikationer", "background_task": "Bakgrundsjobb körs, denna sida kommer att laddas igen när det är klart.", + "check_log_file": "Kontrollera din loggfil för mer information.", "continue": "Fortsätta", "disabled": "Inaktiverad", "documentation": "Dokumentation", @@ -11,9 +12,10 @@ "installed": "installerad", "integration": "Integration", "integrations": "Integrationer", + "manage": "hantera", "netdaemon": "NetDaemon", "netdaemon_apps": "NetDaemon Applikationer", - "plugin": "Plugin", + "plugin": "Lovelace", "plugins": "Plugins", "python_script": "Python skript", "python_scripts": "Python skript", @@ -24,25 +26,27 @@ "version": "Version" }, "config": { - "abort": { - "single_instance_allowed": "Endast en konfiguration kan användas i HACS." - }, - "error": { - "auth": "Personal Access Token är inte korrekt." - }, "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, "user": { "data": { - "appdaemon": "Upptäck och följ Appdaemon applikationer", - "netdaemon": "Upptäck och följ NetDaemon applikationer", - "python_script": "Upptäck och följ python_scripts", - "sidepanel_icon": "Ikon för sidpanel", - "sidepanel_title": "Rubrik för sidpanel", - "theme": "Upptäck och följ teman", + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", "token": "GitHub Personal Access Token" }, - "description": "Om du behöver hjälp med konfigurationen, se här: https:\/\/hacs.xyz\/docs\/configuration\/start", - "title": "HACS (Home Assistant Community Store)" + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" } }, "title": "HACS (Home Assistant Community Store)" @@ -56,10 +60,13 @@ "delete_installed": "'{item}' är installerat, du måste avinstallera det innan du kan ta bort det.", "exist": "{item} existerar redan", "generic": "Är du säker?", + "home_assistant_is_restarting": "Vänta, Home Assistant startar nu om.", + "home_assistant_version_not_correct": "Du kör Home Assistant-versionen '{haversion}', men detta repository kräver att lägsta versionen '{minversion}' måste installeras.", "no": "Nej", "no_upgrades": "Inga uppgraderingar väntar", "ok": "OK", "overwrite": "Detta kommer att skriva över den.", + "reload_data": "Detta laddar om data för alla repositories HACS vet om, kommer detta att ta lite tid att slutföra.", "restart_home_assistant": "Är du säker på att du vill starta om Home Assistant?", "uninstall": "Är du säker på att du vill avinstallera '{item}'?", "upgrade_all": "Detta kommer uppgradera alla dessa repositories, säkerhetsställ att du läst release anteckningarna för dem alla innan du fortsätter", @@ -74,6 +81,7 @@ "debug": "Aktivera felsökning", "experimental": "Använd experimentella funktioner", "netdaemon": "Upptäck och följ NetDaemon applikationer", + "not_in_use": "Används inte med YAML", "release_limit": "Antalet releaser som visas.", "sidepanel_icon": "Ikon för sidpanel", "sidepanel_title": "Rubrik för sidpanel" @@ -82,6 +90,8 @@ } }, "repository_banner": { + "config_flow": "Den här integreringen stöder config_flow, det innebär att du nu kan gå till integrationsdelen i användargränssnittet för att konfigurera det.", + "config_flow_title": "UI-konfiguration stöds", "integration_not_loaded": "Denna integration inte laddats i Hem Assistent.", "no_restart_required": "Ingen omstart krävs", "not_loaded": "Ej laddad", @@ -106,12 +116,13 @@ "installed": "Installerad", "lovelace_copy_example": "Kopiera exemplet till urklipp", "lovelace_instruction": "När du lägger till denna till din lovelace konfiguration, använd", - "lovelace_no_js_type": "Kan inte avgöra villken typ av plugin, kontrollera i GIT \nrepository", + "lovelace_no_js_type": "Kan inte avgöra villken typ av plugin, kontrollera i GIT \\nrepository", "newest": "nyaste", "note_appdaemon": "du behöver fortfarande lägga till den till filen 'apps.yaml'", "note_installed": "När den är installerad kommer den finnas i", "note_integration": "du behöver fortfarande lägga den till filen 'configuration.yaml'", "note_plugin": "du behöver fortfarande lägga till den till din lovelace konfiguration ('ui-lovelace.yaml' eller raw UI config redigerare)", + "note_plugin_post_107": "du behöver fortfarande lägga till den i din lovelace-konfiguration ('configuration.yaml' eller resursredigeraren \/config\/lovelace\/resources')", "open_issue": "Rapportera problem", "open_plugin": "Öppna plugin", "reinstall": "Ominstallera", @@ -122,6 +133,11 @@ "update_information": "Uppdatera information", "upgrade": "Uppdatera" }, + "sections": { + "addon": { + "title": "Tillägg" + } + }, "settings": { "add_custom_repository": "LÄGG TILL ETT REPOSITORY", "adding_new_repo": "Lägger till nytt repository '{repo}'", @@ -139,6 +155,7 @@ "open_repository": "Öppna Repository", "reload_data": "Ladda om data", "reload_window": "Ladda om fönstret", + "repository_configuration": "Konfiguration av repository", "save": "Spara", "table": "Tabell", "table_view": "Tabellvy", @@ -148,6 +165,7 @@ "store": { "ascending": "stigande", "clear_new": "Rensa alla nya förvar", + "descending": "fallande", "last_updated": "Senast uppdaterad", "name": "Namn", "new_repositories": "Nya förvar", diff --git a/custom_components/hacs/translations/vi.json b/custom_components/hacs/translations/vi.json new file mode 100644 index 0000000..db9f3cc --- /dev/null +++ b/custom_components/hacs/translations/vi.json @@ -0,0 +1,345 @@ +{ + "common": { + "about": "Thông tin", + "add": "thêm", + "appdaemon_apps": "Ứng dụng AppDaemon", + "appdaemon_plural": "Ứng dụng AppDaemon", + "background_task": "Tác vụ nền đang chạy, trang này sẽ được tải lại khi hoàn thành.", + "check_log_file": "Kiểm tra tệp nhật ký để biết thêm chi tiết.", + "continue": "Tiếp tục", + "disabled": "Bị vô hiệu hoá", + "documentation": "Tài liệu", + "element": "thành phần", + "hacs_is_disabled": "HACS đã bị vô hiệu hoá", + "install": "Cài đặt", + "installed": "đã cài đặt", + "integration": "Tích Hợp", + "integration_plural": "Tích Hợp", + "integrations": "Tích Hợp", + "lovelace": "Lovelace", + "lovelace_element": "Thành phần Lovelace", + "lovelace_elements": "Các thành phần Lovelace", + "manage": "quản lý", + "netdaemon": "NetDaemon", + "netdaemon_apps": "Ứng dụng NetDaemon", + "netdaemon_plural": "Ứng dụng NetDaemon", + "plugin": "Bổ Sung", + "plugin_plural": "Các thành phần Lovelace", + "plugins": "Bổ Sung", + "python_script": "Chương trình Python", + "python_script_plural": "Chương trình Python", + "python_scripts": "Chương trình Python", + "repositories": "Các kho ứng dụng", + "repository": "Kho lưu trữ", + "settings": "thiết lập", + "theme": "Chủ đề", + "theme_plural": "Chủ đề", + "themes": "Chủ đề", + "uninstall": "Gỡ cài đặt", + "update": "Cập nhật", + "version": "Phiên bản" + }, + "config": { + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "Bạn có chắc muốn thêm vào tài nguyên Lovelace của mình không?", + "bg_task": "Hành động bị vô hiệu hoá trong khi các tác vụ nền đang chạy.", + "cancel": "Huỷ bỏ", + "continue": "Bạn có chắc chắn muốn tiếp tục?", + "delete": "Bạn có chắc muốn xoá '{item}'?", + "delete_installed": "'{item}' đã được cài đặt, bạn cần gỡ cài đặt nó trước khi có thể xoá.", + "exist": "{item} đang tồn tại", + "generic": "Bạn có chắc không?", + "home_assistant_is_restarting": "Khoan đã, Home Assistant đang khởi động lại.", + "home_assistant_version_not_correct": "Bạn đang chạy phiên bản Home Assistant '{haversion}' nhưng kho ứng dụng này yêu cầu phiên bản thấp nhất '{minversion}' để cài đặt.", + "no": "Không", + "no_upgrades": "Không có cập nhật nào đang xử lý", + "ok": "OK", + "overwrite": "Tiếp tục sẽ khiến card này bị ghi đè lên.", + "reload_data": "Điều này tải lại dữ liệu của tất cả các kho ứng dụng trong HACS, sẽ mất một lúc để hoàn tất.", + "restart_home_assistant": "Bạn có muốn khởi động lại Home Assistant?", + "uninstall": "Bạn có chắc muốn gỡ cài đặt '{item}'?", + "upgrade_all": "Điều này sẽ nâng cấp tất cả các kho ứng dụng này, đảm bảo rằng bạn đã đọc tất cả các lưu ý phát hành trước khi tiếp tục.", + "yes": "Có" + }, + "dialog_about": { + "frontend_version": "Phiên bản Frontend", + "installed_repositories": "Kho lưu trữ đã cài đặt", + "integration_version": "Phiên bản tích hợp", + "useful_links": "Liên kết hữu ích" + }, + "dialog_add_repo": { + "limit": "Chỉ 100 kho lưu trữ đầu tiên được hiển thị, sử dụng mục tìm kiếm để lọc những gì bạn cần", + "no_match": "Không tìm thấy kho lưu trữ phù hợp với bộ lọc của bạn", + "sort_by": "Sắp xếp theo", + "title": "Thêm kho lưu trữ" + }, + "dialog_custom_repositories": { + "category": "Danh mục", + "no_category": "Thiếu danh mục", + "no_repository": "Kho lưu trữ bị thiếu", + "title": "Các kho lưu trữ tuỳ chỉnh", + "url_placeholder": "Thêm URL của kho lưu trữ tùy chỉnh" + }, + "dialog_info": { + "author": "Tác giả", + "downloads": "Tải xuống", + "install": "Cài đặt kho lưu trữ này trong HACS", + "loading": "Đang tải thông tin...", + "no_info": "Nhà phát triển đã không cung cấp thêm thông tin nào cho kho lưu trữ này", + "open_issues": "Báo cáo vấn đề", + "open_repo": "Mở Kho ứng dụng", + "stars": "Số sao", + "version_installed": "Phiên bản đã cài đặt" + }, + "dialog_install": { + "restart": "Hãy nhớ rằng bạn cần khởi động lại Home Assistant trước khi các thay đổi với tích hợp (custom_components) được áp dụng.", + "select_version": "Chọn phiên bản", + "show_beta": "Hiển thị phiên bản beta", + "type": "Loại", + "url": "URL" + }, + "dialog_update": { + "available_version": "Phiên bản hiện có", + "changelog": "Thay đổi", + "installed_version": "Phiên bản đã cài đặt", + "releasenotes": "Thông tin phiên bản {Release}", + "title": "Cập nhật đang chờ" + }, + "entry": { + "information": "Thông tin", + "intro": "Các cập nhật và thông điệp quan trọng sẽ hiển thị ở đây nếu có", + "messages": { + "disabled": { + "content": "Kiểm tra tệp nhật ký của bạn để biết thêm chi tiết", + "title": "HACS đã bị vô hiệu hoá" + }, + "has_pending_tasks": { + "content": "Một số kho có thể không thấy cho đến khi điều này hoàn tất", + "title": "Tác vụ nền đang chờ" + }, + "resources": { + "content": "Bạn có {number} thành phần Lovelace không được tải chính xác.", + "title": "Không được tải trong Lovelace" + }, + "restart": { + "content": "Bạn có {number} tích hợp yêu cầu khởi động lại Home Assistant, bạn có thể làm điều này từ mục 'Điều khiển máy chủ' bên trong tab Cấu hình trên giao diện Home Assistant.", + "title": "Đang chờ khởi động lại" + }, + "startup": { + "content": "HACS đang khởi động, suốt quá trình này có thể một số thông tin sẽ bị thiếu hoặc không chính xác", + "title": "HACS đang khởi động" + }, + "wrong_frontend_installed": { + "content": "Bạn có HACS frontend phiên bản {running} đã được cài đặt, nhưng phiên bản yêu cầu là {version}, khi bạn nhìn thấy thông điệp này có nghĩa là Home Assistant không thể cài đặt phiên bản mới, thử khởi động lại Home Assistant.", + "title": "Phiên bản frontend không đúng" + }, + "wrong_frontend_loaded": { + "content": "Bạn đang chạy phiên bản {running} của HACS frontend, nhưng phiên bản được yêu cầu là {expected}, bạn nên xoá bộ đệm của trình duyệt web.", + "title": "Phiên bản frontend không đúng" + } + }, + "pending_updates": "Đang chờ cập nhật" + }, + "menu": { + "about": "Giới thiệu HACS", + "clear": "Ẩn thông báo mục mới", + "custom_repositories": "Các kho ứng dụng tuỳ chỉnh", + "dismiss": "Bỏ qua tất cả kho chứa mới", + "documentation": "Tài liệu", + "open_issue": "Báo cáo vấn đề", + "reload": "Tải lại cửa sổ" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "Kích hoạt phát hiện & theo dõi các ứng dụng AppDaemon", + "country": "Lọc với mã quốc gia.", + "debug": "Kích hoạt Trình gỡ lỗi", + "experimental": "Kích hoạt các tính năng thử nghiệm", + "netdaemon": "Kích hoạt phát hiện & theo dõi các ứng dụng NetDaemon", + "not_in_use": "Không sử dụng với YAML", + "release_limit": "Số lượng phiên bản hiển thị.", + "sidepanel_icon": "Biểu tượng bảng điều khiển cạnh bên", + "sidepanel_title": "Tiêu đề bảng điều khiển cạnh bên" + } + } + } + }, + "repository_banner": { + "config_flow": "Tích hợp này hỗ trợ config_flow, điều này có nghĩa là bây giờ bạn có thể chuyển đến khu vực Tích hợp trên giao diện để cấu hình nó.", + "config_flow_title": "Hỗ trợ cấu hình qua giao diện người dùng", + "integration_not_loaded": "Tích hợp này chưa được tải trong Home Assistant.", + "no_restart_required": "Không cần khởi động lại", + "not_loaded": "Chưa được tải", + "plugin_not_loaded": "Bổ sung này chưa được thêm vào tài nguyên Lovelace của bạn.", + "restart": "Bạn cần khởi động lại Home Assistant.", + "restart_pending": "Đang chờ khởi động lại" + }, + "repository_card": { + "dismiss": "bỏ qua", + "hide": "Ẩn", + "information": "Thông tin", + "new_repository": "Kho lưu trữ mới", + "not_loaded": "Không được tải", + "open_issue": "Báo cáo vấn đề", + "open_source": "Mã nguồn mở", + "pending_restart": "Đang chờ khởi động lại", + "pending_update": "Cập nhật đang chờ", + "reinstall": "Cài đặt lại", + "report": "Báo cáo để loại bỏ", + "update_information": "Cập nhật thông tin" + }, + "repository": { + "add_to_lovelace": "Thêm vào Lovelace", + "authors": "Tác giả", + "available": "Có sẵn", + "back_to": "Quay lại", + "changelog": "Nhật ký thay đổi", + "downloads": "Tải xuống", + "flag_this": "Đánh dấu mục này", + "frontend_version": "Phiên bản Frontend", + "github_stars": "Số Sao trên GitHub", + "goto_integrations": "Đi đến Tích hợp", + "hide": "Ẩn", + "hide_beta": "Ẩn phiên bản beta", + "install": "Cài đặt", + "installed": "Đã cài đặt", + "lovelace_copy_example": "Sao chép ví dụ vào bộ nhớ tạm", + "lovelace_instruction": "Khi bạn thêm mục này vào cấu hình lovelace, sử dụng", + "lovelace_no_js_type": "Không thể xác định loại Bổ sung này, kiểm tra lại kho ứng dụng.", + "newest": "mới nhất", + "note_appdaemon": "bạn vẫn cần thêm vào tập tin 'apps.yaml'", + "note_installed": "Một khi được cài đặt, mục này sẽ nằm ở", + "note_integration": "bạn vẫn cần thêm vào tập tin 'configuration.yaml'", + "note_plugin": "bạn vẫn cần thêm vào cấu hình lovelace ('ui-lovelace.yaml' hoặc trình soạn thảo giao diện)", + "note_plugin_post_107": "bạn vẫn cần phải thêm nó vào cấu hình Lovelace ('configuration.yaml' hoặc trình biên tập tài nguyên '\/config\/Lovelace\/Resources ')", + "open_issue": "Báo cáo vấn đề", + "open_plugin": "Mở Bổ sung", + "reinstall": "Cài đặt lại", + "repository": "Kho ứng dụng", + "restart_home_assistant": "Khởi động lại Home Assistant", + "show_beta": "Hiển thị phiên bản beta", + "uninstall": "Gỡ cài đặt", + "update_information": "Cập nhật thông tin", + "upgrade": "Cập nhật" + }, + "search": { + "placeholder": "Tìm kiếm kho lưu trữ" + }, + "sections": { + "about": { + "description": "Hiển thị thông tin về HACS", + "title": "Thông tin" + }, + "automation": { + "description": "Đây là nơi bạn tìm thấy python_scripts, ứng dụng AppDaemon và ứng dụng NetDaemon", + "title": "Tự động hóa" + }, + "frontend": { + "description": "Đây là nơi bạn tìm thấy chủ đề, thẻ tùy chỉnh và các thành phần khác cho lovelace", + "title": "Frontend" + }, + "integrations": { + "description": "Đây là nơi bạn tìm thấy tích hợp tùy chỉnh (custom_components)", + "title": "Tích Hợp" + }, + "pending_repository_upgrade": "Bạn đang chạy phiên bản {installed}, phiên bản {available} có sẵn" + }, + "settings": { + "add_custom_repository": "THÊM KHO ỨNG DỤNG TUỲ CHỈNH", + "adding_new_repo": "Thêm kho ứng dụng mới '{repo}'", + "adding_new_repo_category": "Với danh mục '{category}'", + "bg_task_custom": "Các kho ứng dụng tuỳ chỉnh bị ẩn khi tác vụ nền đang chạy.", + "category": "Danh mục", + "compact_mode": "Chế độ thu gọn", + "custom_repositories": "KHO ỨNG DỤNG TUỲ CHỈNH", + "delete": "Xoá", + "display": "Hiển thị", + "grid": "Lưới", + "hacs_repo": "Kho ứng dụng HACS", + "hidden_repositories": "các kho ứng dụng bị ẩn", + "missing_category": "Bạn cần chọn một danh mục", + "open_repository": "Mở Kho ứng dụng", + "reload_data": "Tải lại dữ liệu", + "reload_window": "Tải lại cửa sổ", + "repository_configuration": "Cấu hình Kho ứng dụng", + "save": "Lưu", + "table": "Bảng", + "table_view": "Xem dạng bảng", + "unhide": "thôi ẩn", + "upgrade_all": "Nâng cấp tất cả" + }, + "store": { + "ascending": "tăng dần", + "clear_new": "Xoá tất cả kho ứng dụng mới", + "descending": "giảm dần", + "last_updated": "Cập nhật lần cuối", + "name": "Tên", + "new_repositories": "Kho ứng dụng Mới", + "new_repositories_note": "Bạn có hơn 10 kho chứa mới được hiển thị, nếu bạn muốn xoá tất cả thông báo này, bấm vào dấu 3 chấm ở góc trên bên phải rồi chọn Bỏ qua tất cả kho chứa mới.", + "no_repositories": "Không có kho lưu trữ", + "no_repositories_desc1": "Có vẻ như bạn chưa có bất kỳ kho lưu trữ nào được cài đặt trong phần này.", + "no_repositories_desc2": "Nhấp vào biểu tượng + ở góc dưới cùng để thêm mục mới đầu tiên của bạn!", + "no_repositories_found_desc1": "Không tìm thấy kho lưu trữ được cài đặt phù hợp với \"{searchInput}\" trong mục này.", + "no_repositories_found_desc2": "Thử tìm kiếm với từ khoá khác!", + "pending_upgrades": "Nâng cấp chờ xử lý", + "placeholder_search": "Vui lòng nhập cụm từ cần tìm", + "sort": "sắp xếp", + "stars": "Sao", + "status": "Trạng thái" + }, + "time": { + "ago": "trước", + "day": "ngày", + "days": "ngày", + "hour": "giờ", + "hours": "giờ", + "minute": "phút", + "minutes": "phút", + "month": "tháng", + "months": "tháng", + "one": "Một", + "one_day_ago": "một ngày trước", + "one_hour_ago": "một giờ trước", + "one_minute_ago": "một phút trước", + "one_month_ago": "một tháng trước", + "one_second_ago": "một giây trước", + "one_year_ago": "một năm trước", + "second": "giây", + "seconds": "giây", + "x_days_ago": "{x} ngày trước", + "x_hours_ago": "{x} giờ trước", + "x_minutes_ago": "{x} phút trước", + "x_months_ago": "{x} tháng trước", + "x_seconds_ago": "{x} giây trước", + "x_years_ago": "{x} năm trước", + "year": "năm", + "years": "năm" + } +} \ No newline at end of file diff --git a/custom_components/hacs/translations/zh-Hans.json b/custom_components/hacs/translations/zh-Hans.json new file mode 100644 index 0000000..404ffa3 --- /dev/null +++ b/custom_components/hacs/translations/zh-Hans.json @@ -0,0 +1,383 @@ +{ + "common": { + "about": "关于", + "add": "添加", + "appdaemon": "AppDaemon", + "appdaemon_apps": "AppDaemon应用", + "appdaemon_plural": "AppDaemon应用", + "background_task": "后台任务正在运行,完成后将重新加载此页面。", + "cancel": "取消", + "check_log_file": "请查看日志文件以了解更多信息。", + "continue": "继续", + "disabled": "禁用", + "documentation": "文档", + "element": "元素", + "hacs_is_disabled": "HACS已禁用", + "ignore": "忽略", + "install": "安装", + "installed": "已安装", + "integration": "集成", + "integration_plural": "集成", + "integrations": "集成", + "lovelace": "Lovelace", + "lovelace_element": "Lovelace元素", + "lovelace_elements": "Lovelace元素", + "manage": "管理", + "netdaemon": "NetDaemon应用", + "netdaemon_apps": "NetDaemon应用", + "netdaemon_plural": "NetDaemon应用", + "plugin": "Lovelace", + "plugin_plural": "Lovelace元素", + "plugins": "Lovelace元素", + "python_script": "Python脚本", + "python_script_plural": "Python脚本", + "python_scripts": "Python脚本", + "reload": "重新加载", + "repositories": "储存库数量", + "repository": "存储库", + "settings": "设置", + "theme": "主题", + "theme_plural": "主题", + "themes": "主题", + "uninstall": "卸载", + "update": "升级", + "version": "版本" + }, + "config": { + "abort": { + "single_instance_allowed": "仅允许单个HACS配置。" + }, + "error": { + "acc": "您需要先确认所有声明,然后才能继续", + "auth": "个人访问令牌不正确。" + }, + "step": { + "device": { + "description": "Open {url} and paste this key `{code}` to authorize HACS, when you have done that click 'submit'", + "title": "HACS" + }, + "user": { + "data": { + "acc_addons": "I know that there are no add-ons in HACS", + "acc_disable": "I know that if I get issues with Home Assistant I should disable all my custom_components", + "acc_logs": "I know how to access Home Assistant logs", + "acc_untested": "I know that everything inside HACS is custom and untested by Home Assistant", + "appdaemon": "Enable AppDaemon apps discovery & tracking", + "netdaemon": "Enable NetDaemon apps discovery & tracking", + "python_script": "Enable python_scripts discovery & tracking", + "sidepanel_icon": "Side panel icon", + "sidepanel_title": "Side panel title", + "theme": "Enable Themes discovery & tracking", + "token": "GitHub Personal Access Token" + }, + "description": "Before you can setup HACS you need to acknowledge the following", + "title": "HACS" + } + }, + "title": "HACS (Home Assistant Community Store)" + }, + "confirm": { + "add_to_lovelace": "您确定要将此添加到Lovelace资源中吗?", + "bg_task": "后台任务正在运行时,操作被禁用。", + "cancel": "取消", + "continue": "你确定你要继续吗?", + "delete": "是否确实要删除\"{item}\"?", + "delete_installed": "已安装 '{item}',需要先将其卸载,然后才能将其删除。", + "exist": "{item}已经存在", + "generic": "你确定吗?", + "home_assistant_is_restarting": "请等待,Home Assistant现在正在重新启动。", + "home_assistant_version_not_correct": "您正在运行Home Assistant的版本为'{haversion}',但是这个需要安装最低版本是'{minversion}'。", + "no": "取消", + "no_upgrades": "暂无升级", + "ok": "确定", + "overwrite": "这样做会覆盖它。", + "reload_data": "这将重新加载HACS知道的所有存储库的数据,这需要一些时间才能完成。", + "restart_home_assistant": "您确定要重新启动Home Assistant吗?", + "uninstall": "您确定要卸载 '{item}' 吗?", + "upgrade_all": "这将升级所有这些存储库,请确保在继续之前已阅读所有存储库的发行说明。", + "yes": "确定" + }, + "dialog_about": { + "frontend_version": "前端版本", + "installed_repositories": "已安装的存储库", + "integration_version": "集成版本", + "useful_links": "有用的链接" + }, + "dialog_add_repo": { + "limit": "仅显示前100个存储库,使用搜索过滤所需的内容", + "no_match": "找不到与您的过滤器匹配的存储库", + "sort_by": "排序方式", + "title": "添加存储库" + }, + "dialog_custom_repositories": { + "category": "类别", + "no_category": "没有选择类别", + "no_repository": "存储库地址不能为空", + "title": "自定义存储库", + "url_placeholder": "添加自定义存储库URL" + }, + "dialog_info": { + "author": "作者", + "downloads": "下载", + "install": "在HACS中安装此存储库", + "loading": "正在加载详细信息...", + "no_info": "开发人员尚未为此存储库提供任何更多信息", + "open_issues": "提交问题", + "open_repo": "打开存储库", + "stars": "星级", + "version_installed": "已安装版本" + }, + "dialog_install": { + "restart": "请记住,在应用对集成(custom_components)所做的更改之前,您需要重新启动Home Assistant。", + "select_version": "选择版本", + "show_beta": "显示测试版", + "type": "Type", + "url": "URL" + }, + "dialog_removed": { + "link": "外部链接以获取更多信息", + "name": "存储库名称", + "reason": "删除原因", + "type": "删除类型" + }, + "dialog_update": { + "available_version": "可用版本", + "changelog": "更改日志", + "installed_version": "已安装版本", + "releasenotes": "更新日志{release}", + "title": "待更新" + }, + "dialog": { + "reload": { + "confirm": "您现在要这样做吗?", + "description": "更改Lovelace资源时,您需要清除浏览器缓存。" + } + }, + "entry": { + "information": "详情", + "intro": "如果有更新和重要消息,将在此处显示", + "messages": { + "disabled": { + "content": "请查看日志文件以了解更多信息", + "title": "HACS已禁用" + }, + "has_pending_tasks": { + "content": "在完成此操作之前,某些存储库可能不会显示", + "title": "待处理的后台任务" + }, + "removed": "删除了存储库“ {repository}”", + "resources": { + "content": "您有{number} 个Lovelace元素未正确加载到Lovelace中。", + "title": "未载入Lovelace" + }, + "restart": { + "content": "您有{number}集成,需要重新启动Home Assistant,您可以从Home Assistant 的配置下的“服务器控制”部分执行此操作。", + "title": "待重启" + }, + "setup": { + "content": "HACS正在配置,在此期间某些信息可能丢失或不正确", + "title": "HACS正在配置" + }, + "startup": { + "content": "HACS正在启动,在此期间某些信息可能丢失或不正确", + "title": "HACS正在启动" + }, + "waiting": { + "content": "HACS在开始启动任务之前正在等待Home Assistant完成启动", + "title": "HACS正在等待" + }, + "wrong_frontend_installed": { + "content": "您已经安装了{running}的HACS前端,但是{expected}版本是{expected} ,如果看到此消息,则Home Assistant无法安装新版本,请尝试重新启动Home Assistant。", + "title": "额外的前端版本" + }, + "wrong_frontend_loaded": { + "content": "您正在运行HACS前端的{running}版本,但是{expected}版本,您应该清除浏览器缓存。", + "title": "额外的前端版本" + } + }, + "pending_updates": "待升级" + }, + "menu": { + "about": "关于HACS", + "clear": "清除所有新标记", + "custom_repositories": "自定义存储库", + "dismiss": "忽略所有新的存储库", + "documentation": "文档", + "open_issue": "提交问题", + "reload": "重新加载窗口" + }, + "options": { + "step": { + "user": { + "data": { + "appdaemon": "启用AppDaemon应用发现和跟踪", + "country": "用国家代码过滤。", + "debug": "启用调试。", + "experimental": "启用实验功能", + "netdaemon": "启用NetDaemon应用发现和跟踪", + "not_in_use": "不适用于 YAML", + "release_limit": "要显示的发行数量。", + "sidepanel_icon": "侧面板图标", + "sidepanel_title": "侧面板标题" + } + } + } + }, + "repository_banner": { + "config_flow": "此组件支持config_flow,这意味着您现在可以转到UI的“集成”部分进行配置。", + "config_flow_title": "支持UI配置", + "integration_not_loaded": "此集成未加载到Home Assistant中。", + "no_restart_required": "无需重启", + "not_loaded": "未加载", + "plugin_not_loaded": "该元件未添加到您的Lovelace资源中。", + "restart": "您需要重新启动Home Assistant。", + "restart_pending": "重新启动待处理" + }, + "repository_card": { + "dismiss": "忽略", + "hide": "隐藏", + "information": "详情", + "new_repository": "新存储库", + "not_loaded": "未加载", + "open_issue": "提交问题", + "open_source": "开源", + "pending_restart": "待重启", + "pending_update": "待升级", + "reinstall": "重新安装", + "report": "报告删除", + "update_information": "更新信息" + }, + "repository": { + "add_to_lovelace": "添加到Lovelace", + "authors": "作者", + "available": "版本号", + "back_to": "返回", + "changelog": "更新日志", + "downloads": "下载", + "flag_this": "标记", + "frontend_version": "前端版本", + "github_stars": "GitHub星级", + "goto_integrations": "转到集成", + "hide": "隐藏", + "hide_beta": "隐藏测试版", + "install": "安装", + "installed": "已安装版本", + "lovelace_copy_example": "复制样例代码到你的剪贴板", + "lovelace_instruction": "您仍然需要将下列代码添加到lovelace配置中(“ ui-lovelace.yaml”或原始配置编辑器):", + "lovelace_no_js_type": "无法确定此元素的类型,请检查存储库。", + "newest": "最新", + "note_appdaemon": "您仍然需要将其添加到“ apps.yaml”文件中", + "note_installed": "安装后,它将位于", + "note_integration": "您仍然需要将其添加到“ configuration.yaml”文件中", + "note_plugin": "您仍然需要将其添加到lovelace配置中(“ ui-lovelace.yaml”或原始配置编辑器)ui-lovelace.yaml", + "note_plugin_post_107": "您仍然需要将其添加到lovelace配置中(“ configuration.yaml”或资源编辑器“\/config\/lovelace\/resources”)", + "open_issue": "提交问题", + "open_plugin": "打开元素", + "reinstall": "重新安装", + "repository": "存储库", + "restart_home_assistant": "重新启动Home Assistant", + "show_beta": "显示测试版", + "uninstall": "卸载", + "update_information": "更新信息", + "upgrade": "升级" + }, + "search": { + "installed": "搜索已安装的存储库", + "installed_new": "搜索已安装或新的存储库", + "placeholder": "搜索存储库" + }, + "sections": { + "about": { + "description": "显示有关HACS的信息", + "title": "关于" + }, + "addon": { + "description": "HACS 中没有附加组件,但您可以单击此处联系管理员", + "title": "附加组件" + }, + "automation": { + "description": "在这里可以找到python_scripts,AppDaemon应用程序和NetDaemon应用程序", + "title": "自动化" + }, + "frontend": { + "description": "在这里,您可以找到主题,自定义卡片和其他用于lovelace的元素", + "title": "前端" + }, + "integrations": { + "description": "在这里您可以找到自定义集成(custom_components)", + "title": "集成" + }, + "pending_repository_upgrade": "您正在运行版本{installed} ,有新版 ({available}) 可用" + }, + "settings": { + "add_custom_repository": "添加自定义存储库", + "adding_new_repo": "添加新的储存库 '{repo}'", + "adding_new_repo_category": "类别为 '{category}'。", + "bg_task_custom": "自定义存储库在后台任务运行时被隐藏。", + "category": "类别", + "compact_mode": "紧凑模式", + "custom_repositories": "自定义存储库", + "delete": "删除", + "display": "视图方式", + "grid": "网格", + "hacs_repo": "HACS存储库", + "hidden_repositories": "隐藏的存储库", + "missing_category": "您需要选择一个类别", + "open_repository": "打开存储库", + "reload_data": "重载数据", + "reload_window": "重新加载窗口", + "repository_configuration": "存储库配置", + "save": "保存", + "table": "列表", + "table_view": "表视图", + "unhide": "取消隐藏", + "upgrade_all": "升级全部" + }, + "store": { + "ascending": "升序", + "clear_new": "清除所有新存储库标记", + "descending": "降序", + "last_updated": "最近更新时间", + "name": "名称", + "new_repositories": "新存储库", + "new_repositories_note": "这里有多于10个新存储库,如果要清除它们,请单击右上角的3点按钮,然后将其全部忽略。", + "no_repositories": "没有储存库", + "no_repositories_desc1": "在本节中您似乎尚未安装任何存储库。", + "no_repositories_desc2": "单击右下角的+,添加第一个!", + "no_repositories_found_desc1": "在这里找不到与“ {searchInput} ”匹配的已安装存储库。", + "no_repositories_found_desc2": "尝试寻找其他东西!", + "pending_upgrades": "待升级", + "placeholder_search": "搜索项目...", + "sort": "排序", + "stars": "星级", + "status": "状态" + }, + "time": { + "ago": "过去", + "day": "天", + "days": "天", + "hour": "小时", + "hours": "小时", + "minute": "分", + "minutes": "分", + "month": "月", + "months": "月", + "one": "一", + "one_day_ago": "一天前", + "one_hour_ago": "一个小时之前", + "one_minute_ago": "一分钟前", + "one_month_ago": "一个月前", + "one_second_ago": "一秒钟前", + "one_year_ago": "一年前", + "second": "秒", + "seconds": "秒", + "x_days_ago": "{x}天前", + "x_hours_ago": "{x}小时前", + "x_minutes_ago": "{x}分钟前", + "x_months_ago": "{x}个月前", + "x_seconds_ago": "{x}秒前", + "x_years_ago": "{x}年前", + "year": "年", + "years": "年" + } +} \ No newline at end of file diff --git a/custom_components/hacs/validate/README.md b/custom_components/hacs/validate/README.md new file mode 100644 index 0000000..e0a7fd3 --- /dev/null +++ b/custom_components/hacs/validate/README.md @@ -0,0 +1,38 @@ +# Repository validation + +This is where the validation rules that run against the various repository categories live. + +## Structure + +- All validation rules are in the directory for their category. +- Validation rules that aplies to all categories are in the `common` directory. +- There is one file pr. rule. +- All rule needs tests to verify every possible outcome for the rule. +- It's better with multiple files than a big rule. +- All rules uses `ValidationBase` or `ActionValidationBase` as the base class. +- The `ActionValidationBase` are for checks that will breaks compatibility with with existing repositories (default), so these are only run in github actions. +- The class name should describe what the check does. +- Only use `validate` or `async_validate` methods to define validation rules. +- If a rule should fail, raise `ValidationException` with the failure message. + + +## Example + +```python +from custom_components.hacs.validate.base import ( + ActionValidationBase, + ValidationBase, + ValidationException, +) + + +class AwesomeRepository(ValidationBase): + def validate(self): + if self.repository != "awesome": + raise ValidationException("The repository is not awesome") + +class SuperAwesomeRepository(ActionValidationBase, category="integration"): + async def async_validate(self): + if self.repository != "super-awesome": + raise ValidationException("The repository is not super-awesome") +``` \ No newline at end of file diff --git a/custom_components/hacs/validate/__init__.py b/custom_components/hacs/validate/__init__.py new file mode 100644 index 0000000..42ec85f --- /dev/null +++ b/custom_components/hacs/validate/__init__.py @@ -0,0 +1,51 @@ +import asyncio +import glob +import importlib +from os.path import dirname, join, sep + +from custom_components.hacs.share import SHARE, get_hacs + + +def _initialize_rules(): + rules = glob.glob(join(dirname(__file__), "**/*.py")) + for rule in rules: + rule = rule.replace(sep, "/") + rule = rule.split("custom_components/hacs")[-1] + rule = f"custom_components/hacs{rule}".replace("/", ".")[:-3] + importlib.import_module(rule) + + +async def async_initialize_rules(): + hass = get_hacs().hass + await hass.async_add_executor_job(_initialize_rules) + + +async def async_run_repository_checks(repository): + hacs = get_hacs() + if not SHARE["rules"]: + await async_initialize_rules() + if not hacs.system.running: + return + checks = [] + for check in SHARE["rules"].get("common", []): + checks.append(check(repository)) + for check in SHARE["rules"].get(repository.data.category, []): + checks.append(check(repository)) + + await asyncio.gather( + *[ + check._async_run_check() + for check in checks or [] + if hacs.system.action or not check.action_only + ] + ) + + total = len([x for x in checks if hacs.system.action or not x.action_only]) + failed = len([x for x in checks if x.failed]) + + if failed != 0: + repository.logger.error("%s %s/%s checks failed", repository, failed, total) + if hacs.system.action: + exit(1) + else: + repository.logger.debug("%s All (%s) checks passed", repository, total) diff --git a/custom_components/hacs/validate/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/validate/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f805c8021d19cafb52fb989dc8bb7e33067b3815 GIT binary patch literal 1954 zcmaJ>-ESL35Z~F`JD;78rXiR%El?0aI3ODd@q(&~@*$O%AQ2%UOVx3?yre9H0L1 zThs~|`<)hN2gk+xIQ17OB$H6EVWVLPZq0`rH7~re7>cn!^vA(40J8Cl);Ju73Y@uF~p=_gGmaxluom2GAc6y#|$y}B3IJII_<)!s>I_Yui@7}on*~d1_ zQWG7-iB8CTF(^bI=Z84;mnaOIF$0DhZweBod=4k9W*@PeADlo9Ghuwm=iGe<(wl=6 z7ug9&uZEx5FMK8>m;RiR3cluaVp_AXhB*MceL*q|x3zQJ+TI>)_a{g9VQ}0{4z;Pq zQBsX3RhgDX4=Bjfhd3^O)t>S&Kt&Ums01v=mcrSdCIeHn#eQ#Nuy&k*=I ze1Z(P!H5S480_?fLV7VP4q~lsH`YgG62($R>76t=G?j`*2o7D-1Jy<4l4`^T{qfOX zDD<=8uH%^|*cb+@E}%{*w8bD^(7jWf8l!PxeuwOkaFOXv!>4Q!=(lX-jf8%k0?w%F z&Sr*O$=IADd)7}lvL~o{&87_--dotzY}6Ws|7o{p+M~`lY#%u$#a-mmZXtzv|848L#XTTa zB>GITJ6DTb8+uRIUZYq-6anWDvf#n-;E6MQHRM1r_}kE<6^S-7KKraf||h^6Wk6mQ~eB4(Un#7{GlFs1-@ z$%=^XFkYo@G-3*|J$o_OU_ZtlAr)2QA5X$v_7=Gzh6i|Az!Z`HxVxpVZ0P|?2e7`< zI5~BP7jgS~QCxXrOZOJ%6MU4Z?45USRMDexPsyc?-RZC_^D?s@wqGppWRjNB3hX7Z z4HN7r8RBnSt}1zDgDf@&sj^;?mNqD=EW?GLDpe_mpcUyIY;V3y_YSk3Z#z$lh@>QT zXhnswzNt)HC?Y_Uxx?BugG>KIffvwT6D$k@*T6y$_}hRrxBzQhK!*np#KTV5g>@dn zI@)x&dKc|`D~f8TmPnjKF>F=3Kf!cd!){tPy$^f&nI79G2KV(rtkMQQs)_4o4U**l eO#L1+#)em$u6~V*qgPRc7*Plte9a5nq5n4@ugpsT literal 0 HcmV?d00001 diff --git a/custom_components/hacs/validate/__pycache__/base.cpython-38.pyc b/custom_components/hacs/validate/__pycache__/base.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73727d98fc9d98a81bc897af507108d8374bb160 GIT binary patch literal 2261 zcmZ`)&2Aev5GMCeE6I}O#BkC!ZMH31SFI~w+FMZgCvIb)kO9eYQe777B_*%C+EvL_ z8wpA-Hjqov7ibIEM?Xi;eF0v3>MP{b8On-m*P4$0vRznLMQmP!QzZSS{#{Dw`) zzbIU776=g#$Hvv%VgtNjvJShXa|V zJwHr){WRf8CfCuY^3wn!@Cb=-`-&bu_5WJttC`-jq zn4qRCL_|65Ac{FtPMmf+TtF-=$>X*t!Il}YB99snjVOsS-$zLz>V!Jf1sdJE?tE>! zQ#lZVUV8lsXdbBuBpbjoazeVaO~%IWl#&cC@g6yYOPm9y$F5lGT+WT@Be>*beGGe! zbD}4=P!6TfO7J?>7laqxqz`=)`EUZ1V&rgT!<8EOUFg!`SgjKFR+R~(HDcn*JpeH z+7jI%MuFa^23>)SsbIl9oQvAL=C#n>Oc;C@XnkV;BRGD}6Y)9BAx)Bmj+W=lY42{e zq0>VrCD;&Oy*>LjE32?ty<+7?D$dZXQgIh|7j-m*gXiXu-9d(RrQE(qp>ngKc!+af zBEvffG^0wF^%(Lvp4Ox;%m=tdS0`pSPD#uPmzJGn*PsQ{ExM+_wGil9GeOz_lJ|j4 zKL&2%SD>GLOQ)!%mN#``UB~cN^tSP|X-QX&`u@8^gqN)0_RBisrz|+4*3+Rvw*(Y$OUd^coH~!Ze{w6hf6bP>0$;ls+fBTzHssT~dhg``w YZ4~V(_tE5w{|@t2Vu&kd#VUXHKUp&D`v3p{ literal 0 HcmV?d00001 diff --git a/custom_components/hacs/validate/base.py b/custom_components/hacs/validate/base.py new file mode 100644 index 0000000..481201c --- /dev/null +++ b/custom_components/hacs/validate/base.py @@ -0,0 +1,48 @@ +from custom_components.hacs.share import SHARE, get_hacs + + +class ValidationException(Exception): + pass + + +class ValidationBase: + def __init__(self, repository) -> None: + self.repository = repository + self.hacs = get_hacs() + self.failed = False + self.logger = repository.logger + + def __init_subclass__(cls, category="common", **kwargs) -> None: + """Initialize a subclass, register if possible.""" + super().__init_subclass__(**kwargs) + if SHARE["rules"].get(category) is None: + SHARE["rules"][category] = [] + if cls not in SHARE["rules"][category]: + SHARE["rules"][category].append(cls) + + @property + def action_only(self): + return False + + async def _async_run_check(self): + """DO NOT OVERRIDE THIS IN SUBCLASSES!""" + if self.hacs.system.action: + self.logger.info(f"Running check '{self.__class__.__name__}'") + try: + await self.hacs.hass.async_add_executor_job(self.check) + await self.async_check() + except ValidationException as exception: + self.failed = True + self.logger.error(exception) + + def check(self): + pass + + async def async_check(self): + pass + + +class ActionValidationBase(ValidationBase): + @property + def action_only(self): + return True diff --git a/custom_components/hacs/validate/common/__pycache__/hacs_manifest.cpython-38.pyc b/custom_components/hacs/validate/common/__pycache__/hacs_manifest.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6102be9c6d373ec3b81297713665de4cd9f6136b GIT binary patch literal 799 zcmZuv!EVz)5S?8+t!uRim5L*>P}M3IhYyHCp&+&=EVd@WgS*V<%=eg7V!76`wDfNUd zBvq^%$K(i)+e4=zV+t~^nBq5pC;_5`e*1MU-J85FhT1g}x!)Sa0iI(#?lC&5B%aqW zhB12qb9TOIA2nQsbM_T`6YG=N)(9J>V~2d!yAc=ac1qoL(VbzRkMS3F&BpMZU!dk6 z&}BXv7NxH9N~3h1KJo0*cJOuU{ygnTQxA)?o}9YIR6?3+VrpGCu1Bc7&x;{xd{$Ma zZa<3ZpSW~#<>P)?piC?qY!MOK%LZ_m9juQbl_M?B(|&1WUb+DW%c9xAyOB<;o)}j& z#$F|(+$FV14sS&tCM2L6_;~fohmFC)a zLePAK@Uak;QPYy*Z6PkEdAYc;_ps3#L_0yku_SntP-q_!YjbM4e`lg;D``2=^b`Jd YmWo;#ZQcI>eAHi18qo4vl=~Zh0o?M!4FCWD literal 0 HcmV?d00001 diff --git a/custom_components/hacs/validate/common/__pycache__/repository_description.cpython-38.pyc b/custom_components/hacs/validate/common/__pycache__/repository_description.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddc39256eaf823defda1ac3b170dc0e8ebefa9ab GIT binary patch literal 657 zcmZuv%}(1u5T0F|ViJ(5LVMv7q+D{bUjQN6g32kCpm6YIWp+2j!S<%RPDLV4g(EN0 zhv^IKwWq!UCuST$EWt=Kn$gaD-~8a2lzCZjGF$4HVL0du$4pChnjRM6AC|Sx% zo^l4?Kyek^g9fdIG*z-<5H09gs8Go3*kFLlt#@8@;8O}Im?RWi|ry-(`(DnpRmNTj_PQI5o=-s o?Y|l&rcvVOQQ}Lyv!$YDBTZcko7Q59z(@PbmJaAn4a@s?Px<_*;{X5v literal 0 HcmV?d00001 diff --git a/custom_components/hacs/validate/common/__pycache__/repository_information_file.cpython-38.pyc b/custom_components/hacs/validate/common/__pycache__/repository_information_file.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..47673632faa93cc7a49d37de19676f6532050e91 GIT binary patch literal 1008 zcma)5!H&}~5Ve!EO$$Y6Ss@{D=wYeUOS)H9RRw4PaiB;naEOGg5WCxS6T54tu&v64 z?gfbpe*j0mgEK$iD<^(|6ElgrjkF-)XeMLFp7;D_^0M7-5g5-t{a_y*LVn_;Iy{)% z12cQzIN>xU84YRXgbpQ-33s@APPi-R+6`Uq9g$w`7wjZG$L`*b^F+y4QJU}wqX&@@ z_QrPp@GKTH{1D@b>HsDs>MjI=gp`xe;gmb)1W;T+@$9YVVx~-zE4_Fk$4X}f#-k(^ zF|FjnRA}G9ObD(_vm#S6rH5oq-_s&?rmpUCcSTR?x-0iD)(*H=tR2YPyjDbqa);N8 zXe9mIoC#MP6S6KsqH~=vVUDIR(YOp;-PHh|4Gwww9`Q zLc@|1>|1)KYjCh|zn}JFCCABeKc1UhWh_?NOi3Yg)1O4K>Ax+N7O*YLlVlNx$VY=+Tn264nS!WWNj#Qlr!44Aa)og3K%X+yM!g|n0k!@=sxzMbH&@Rvh zwLBxzv2AW-G+W&?cDTJ#q3+{#v9$=@fyXJ#hc}qByz^hzA3+QHjA6+bvn|Fl#pfx; zR~b8hKN9K-SO5S3 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/validate/common/__pycache__/repository_topics.cpython-38.pyc b/custom_components/hacs/validate/common/__pycache__/repository_topics.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e738d8c328f84a623a4b186d42d60346d5c799bc GIT binary patch literal 632 zcmZWmy-veG4E9|@DFq}1hzW@fEM1ZpK&XHaTaox1vYf)*K~s`k$fXD>CSc@6co<&b zRwiD72|E$A0!z01$@b^J^X26Zpd7!xi!BE5L10#V1btMyjYffDDP$~S8IL%FBT!s< zFQ7clp1sIZ{weh4J~0z|+^z4)Dlz#*oF*zJZ$Gx$txfUaz0?H-Bvs4`N6<&L>u5AY zOhLpIQ~U+6i-%o&w{oHjW0T614`-%Gq?I`TuaHkH2dH)f%_EFp#75k%R-R(d>0E7AVF8!qLveNnnS;e1W literal 0 HcmV?d00001 diff --git a/custom_components/hacs/validate/common/hacs_manifest.py b/custom_components/hacs/validate/common/hacs_manifest.py new file mode 100644 index 0000000..6701c12 --- /dev/null +++ b/custom_components/hacs/validate/common/hacs_manifest.py @@ -0,0 +1,10 @@ +from custom_components.hacs.validate.base import ( + ActionValidationBase, + ValidationException, +) + + +class HacsManifest(ActionValidationBase): + def check(self): + if "hacs.json" not in [x.filename for x in self.repository.tree]: + raise ValidationException("The repository has no 'hacs.json' file") diff --git a/custom_components/hacs/validate/common/repository_description.py b/custom_components/hacs/validate/common/repository_description.py new file mode 100644 index 0000000..100de02 --- /dev/null +++ b/custom_components/hacs/validate/common/repository_description.py @@ -0,0 +1,10 @@ +from custom_components.hacs.validate.base import ( + ActionValidationBase, + ValidationException, +) + + +class RepositoryDescription(ActionValidationBase): + def check(self): + if not self.repository.data.description: + raise ValidationException("The repository has no description") diff --git a/custom_components/hacs/validate/common/repository_information_file.py b/custom_components/hacs/validate/common/repository_information_file.py new file mode 100644 index 0000000..0447988 --- /dev/null +++ b/custom_components/hacs/validate/common/repository_information_file.py @@ -0,0 +1,19 @@ +from custom_components.hacs.validate.base import ( + ActionValidationBase, + ValidationException, +) + + +class RepositoryInformationFile(ActionValidationBase): + async def async_check(self): + filenames = [x.filename.lower() for x in self.repository.tree] + if self.repository.data.render_readme and "readme" in filenames: + pass + elif self.repository.data.render_readme and "readme.md" in filenames: + pass + elif "info" in filenames: + pass + elif "info.md" in filenames: + pass + else: + raise ValidationException("The repository has no information file") diff --git a/custom_components/hacs/validate/common/repository_topics.py b/custom_components/hacs/validate/common/repository_topics.py new file mode 100644 index 0000000..194faad --- /dev/null +++ b/custom_components/hacs/validate/common/repository_topics.py @@ -0,0 +1,10 @@ +from custom_components.hacs.validate.base import ( + ActionValidationBase, + ValidationException, +) + + +class RepositoryTopics(ActionValidationBase): + def check(self): + if not self.repository.data.topics: + raise ValidationException("The repository has no topics") diff --git a/custom_components/hacs/validate/integration/__pycache__/integration_manifest.cpython-38.pyc b/custom_components/hacs/validate/integration/__pycache__/integration_manifest.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d78ae619babef1b745b3d90900c30fde5fa38a18 GIT binary patch literal 865 zcmZ`%&2AGh5FUFsKLmmht&q5&2q8)i?DheosI(x25akfyuoAKs+etRvpVZ!S!jA9<=df`024-iAJR+fQVf6#2V2O;k+Ter&bd+SJcZ zq@L4(q!;zYvDm|Hw=rmln1YBaruYRQi-#<}+j^0gdSWW%S8<+YJas&LBtm!kqS|vF+tWZ-*3Bec0)dr@rSpn_JE=jn;-bf~I||)A7cqAw8~9QMT98 zfZbusyZ@DTh+}OFL7fxAb%e+YwMZ$yA;j?_PFE-9CgRnYs@@WvA^R;Nqe-<{RlDo8 vM$&L(jQ<8rB3ty}pCJU5<@FGP5BO&dp;b03z^j;Dd$7_vr2kUU-)jB=pupUf literal 0 HcmV?d00001 diff --git a/custom_components/hacs/validate/integration/integration_manifest.py b/custom_components/hacs/validate/integration/integration_manifest.py new file mode 100644 index 0000000..41b1c97 --- /dev/null +++ b/custom_components/hacs/validate/integration/integration_manifest.py @@ -0,0 +1,10 @@ +from custom_components.hacs.validate.base import ( + ActionValidationBase, + ValidationException, +) + + +class IntegrationManifest(ActionValidationBase, category="integration"): + def check(self): + if "manifest.json" not in [x.filename for x in self.repository.tree]: + raise ValidationException("The repository has no 'hacs.json' file") diff --git a/custom_components/hacs/webresponses/__init__.py b/custom_components/hacs/webresponses/__init__.py new file mode 100644 index 0000000..4b9a8ba --- /dev/null +++ b/custom_components/hacs/webresponses/__init__.py @@ -0,0 +1 @@ +"""Initialize HACS Web responses""" diff --git a/custom_components/hacs/webresponses/__pycache__/__init__.cpython-38.pyc b/custom_components/hacs/webresponses/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2eaec8f2ca0ca2f80060a209009b30a5b1f4f32b GIT binary patch literal 187 zcmWIL<>g`k0{@<~@hU+2F^Gc<7=auIATH(r5-AK(3@MDk44O<;vYvUFC7FpinN_I@ z9*)kz3gM|q3Pq{K1^Ic!sl|SpjJMe1<5TjJ13Bn35HkP( DS~)UT literal 0 HcmV?d00001 diff --git a/custom_components/hacs/webresponses/__pycache__/category.cpython-38.pyc b/custom_components/hacs/webresponses/__pycache__/category.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c9c57de230a4e22fb54710f5fcc6c91720990db GIT binary patch literal 1254 zcmZ`&OOG5i5Vre0vojO1tUy2xeF@PZc6t>pgu{YRq7aax1Z9ObnailRUDMq|KL*>) z?6i8gEJxtP1tO%)k>A0Y8z&#(#9xSnpxnDnR*GWF<#O3o^;Nm*i{Y?Oa6I_#_t{T2 zA%E$^4+5j7LfxvoEu;E`5%;QFjw_<>u(GF+1lR`c@J@H>~HKx+hrknnbofr<*tw=hvzT%YOfP2=W`y$d06kGX~|PyzASwX zUsWKafBl-c?jKjuM{xc;&A?|R2)`($4=4DE^*eaM!C!^K4+}pPq5`zC)=<%mK-8cy z?;=Tmq-}XPLWZ&u<0~AilBhl&xfH8ZCd!HsrrL_iVxGorv2aVjacB1)+`W6}p|Tkq zS25S+q6VKJo`)coWg$Sh34{!|P&RO0a^-NyO9`rX=OluqOv^$IZ-oM$AH7a@>?rpl zU^Tj6fX#Hc%F^XI?X`il0*`kdd(c__mGCza(X*TQWg2-9RhaO6h98LFppYWaW&-Ss zPFn;~C?PKSda_)rH_p4G^)l-&S^&Bc#0?~bI@F+jx?^k`BRWE^PX~skj$z>qu75md zNACpZzpq*RDr(&y_mvx_Wg_MBJZ&$6n`m&81hOUOZ!)inNT*9ovbK4-iDuOP3n9Hs y>r#uKf8N!XsEQ=y(AEGSporIe`US9%#1@~ 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 literal 0 HcmV?d00001 diff --git a/custom_components/hacs/webresponses/category.py b/custom_components/hacs/webresponses/category.py new file mode 100644 index 0000000..b8458c4 --- /dev/null +++ b/custom_components/hacs/webresponses/category.py @@ -0,0 +1,39 @@ +from aiohttp import web + +from custom_components.hacs.helpers.functions.logger import getLogger +from custom_components.hacs.helpers.functions.path_exsist import async_path_exsist +from custom_components.hacs.share import get_hacs + +_LOGGER = getLogger() + + +async def async_serve_category_file(request, requested_file): + hacs = get_hacs() + try: + if requested_file.startswith("themes/"): + servefile = f"{hacs.core.config_path}/{requested_file}" + else: + servefile = f"{hacs.core.config_path}/www/community/{requested_file}" + + if await async_path_exsist(servefile): + _LOGGER.debug("Serving %s from %s", requested_file, servefile) + response = web.FileResponse(servefile) + if requested_file.startswith("themes/"): + response.headers["Cache-Control"] = "public, max-age=2678400" + else: + response.headers["Cache-Control"] = "no-store, max-age=0" + response.headers["Pragma"] = "no-store" + return response + else: + _LOGGER.error( + "%s tried to request '%s' but the file does not exist", + request.remote, + servefile, + ) + + except (Exception, BaseException) as exception: + _LOGGER.debug( + "there was an issue trying to serve %s - %s", requested_file, exception + ) + + return web.Response(status=404) diff --git a/custom_components/hacs/webresponses/frontend.py b/custom_components/hacs/webresponses/frontend.py new file mode 100644 index 0000000..3dfa2bd --- /dev/null +++ b/custom_components/hacs/webresponses/frontend.py @@ -0,0 +1,50 @@ +from aiohttp import web +from hacs_frontend import locate_dir + +from custom_components.hacs.helpers.functions.logger import getLogger +from custom_components.hacs.helpers.functions.path_exsist import async_path_exsist +from custom_components.hacs.share import get_hacs + +_LOGGER = getLogger() + + +async def async_serve_frontend(requested_file): + hacs = get_hacs() + requested = requested_file.split("/")[-1] + servefile = None + dev = False + + if hacs.configuration.frontend_repo_url or hacs.configuration.frontend_repo: + dev = True + + if hacs.configuration.frontend_repo_url: + _LOGGER.debug("Serving REMOTE DEVELOPMENT frontend") + try: + request = await hacs.session.get( + f"{hacs.configuration.frontend_repo_url}/{requested}" + ) + if request.status == 200: + result = await request.read() + response = web.Response(body=result) + response.headers["Content-Type"] = "application/javascript" + + return response + except (Exception, BaseException) as exception: + _LOGGER.error(exception) + + elif hacs.configuration.frontend_repo: + _LOGGER.debug("Serving LOCAL DEVELOPMENT frontend") + servefile = f"{hacs.configuration.frontend_repo}/hacs_frontend/{requested}" + else: + servefile = f"{locate_dir()}/{requested}" + + if servefile is None or not await async_path_exsist(servefile): + return web.Response(status=404) + + response = web.FileResponse(servefile) + + if dev: + response.headers["Content-Type"] = "application/javascript" + response.headers["Cache-Control"] = "no-store, max-age=0" + response.headers["Pragma"] = "no-store" + return response diff --git a/custom_components/hacs/webresponses/iconset.py b/custom_components/hacs/webresponses/iconset.py new file mode 100644 index 0000000..4e6f37c --- /dev/null +++ b/custom_components/hacs/webresponses/iconset.py @@ -0,0 +1,15 @@ +from aiohttp import web + +ICON = """ +window.customIconsets = window.customIconsets || {}; +window.customIconsets["hacs"] = async () => { + return { + path: + "m 20.064849,22.306912 c -0.0319,0.369835 -0.280561,0.707789 -0.656773,0.918212 -0.280572,0.153036 -0.605773,0.229553 -0.950094,0.229553 -0.0765,0 -0.146661,-0.0064 -0.216801,-0.01275 -0.605774,-0.05739 -1.135016,-0.344329 -1.402827,-0.7588 l 0.784304,-0.516495 c 0.0893,0.146659 0.344331,0.312448 0.707793,0.34433 0.235931,0.02551 0.471852,-0.01913 0.637643,-0.108401 0.101998,-0.05101 0.172171,-0.127529 0.17854,-0.191295 0.0065,-0.08289 -0.0255,-0.369835 -0.733293,-0.439975 -1.013854,-0.09565 -1.645127,-0.688661 -1.568606,-1.460214 0.0319,-0.382589 0.280561,-0.714165 0.663153,-0.930965 0.331571,-0.172165 0.752423,-0.25506 1.166895,-0.210424 0.599382,0.05739 1.128635,0.344329 1.402816,0.7588 l -0.784304,0.510118 c -0.0893,-0.140282 -0.344331,-0.299694 -0.707782,-0.331576 -0.235932,-0.02551 -0.471863,0.01913 -0.637654,0.10202 -0.0956,0.05739 -0.165791,0.133906 -0.17216,0.191295 -0.0255,0.293317 0.465482,0.420847 0.726913,0.439976 v 0.0064 c 1.020234,0.09565 1.638757,0.66953 1.562237,1.460213 z m -7.466854,-0.988354 c 0,-1.192401 0.962855,-2.155249 2.15525,-2.155249 0.599393,0 1.179645,0.25506 1.594117,0.707789 l -0.695033,0.624895 c -0.235931,-0.25506 -0.561133,-0.401718 -0.899084,-0.401718 -0.675903,0 -1.217906,0.542 -1.217906,1.217906 0,0.66953 0.542003,1.217908 1.217906,1.217908 0.337951,0 0.663153,-0.140283 0.899084,-0.401718 l 0.695033,0.631271 c -0.414472,0.452729 -0.988355,0.707788 -1.594117,0.707788 -1.192395,0 -2.15525,-0.969224 -2.15525,-2.148872 z M 8.6573365,23.461054 10.353474,19.14418 h 0.624893 l 1.568618,4.316874 H 11.52037 L 11.265308,22.734136 H 9.964513 l -0.274192,0.726918 z m 1.6833885,-1.68339 h 0.580263 L 10.646796,21.012487 Z M 8.1089536,19.156932 v 4.297745 H 7.1461095 v -1.645131 h -1.606867 v 1.645131 H 4.5763876 v -4.297745 h 0.9628549 v 1.696143 h 1.606867 V 19.156932 Z M 20.115859,4.2997436 C 20.090359,4.159461 19.969198,4.0574375 19.822548,4.0574375 H 14.141102 10.506516 4.8250686 c -0.14665,0 -0.2678112,0.1020202 -0.2933108,0.2423061 L 3.690064,8.8461703 c -0.00651,0.01913 -0.00651,0.03826 -0.00651,0.057391 v 1.5239797 c 0,0.165789 0.133911,0.299694 0.2996911,0.299694 H 4.5762579 20.0711 20.664112 c 0.165781,0 0.299691,-0.133905 0.299691,-0.299694 V 8.8971848 c 0,-0.01913 0,-0.03826 -0.0065,-0.05739 z M 4.5763876,17.358767 c 0,0.184917 0.1466608,0.331577 0.3315819,0.331577 h 5.5985465 3.634586 0.924594 c 0.184911,0 0.331571,-0.14666 0.331571,-0.331577 v -4.744098 c 0,-0.184918 0.146661,-0.331577 0.331582,-0.331577 h 2.894913 c 0.184921,0 0.331582,0.146659 0.331582,0.331577 v 4.744098 c 0,0.184917 0.146661,0.331577 0.331571,0.331577 h 0.446363 c 0.18491,0 0.331571,-0.14666 0.331571,-0.331577 v -5.636804 c 0,-0.184918 -0.146661,-0.331577 -0.331571,-0.331577 H 4.9079695 c -0.1849211,0 -0.3315819,0.146659 -0.3315819,0.331577 z m 1.6578879,-4.852498 h 5.6495565 c 0.15303,0 0.280561,0.12753 0.280561,0.280564 v 3.513438 c 0,0.153036 -0.127531,0.280566 -0.280561,0.280566 H 6.2342755 c -0.1530412,0 -0.2805719,-0.12753 -0.2805719,-0.280566 v -3.513438 c 0,-0.159411 0.1275307,-0.280564 0.2805719,-0.280564 z M 19.790657,3.3879075 H 4.8569594 c -0.1530412,0 -0.2805718,-0.1275296 -0.2805718,-0.2805642 V 1.3665653 C 4.5763876,1.2135296 4.7039182,1.086 4.8569594,1.086 H 19.790657 c 0.153041,0 0.280572,0.1275296 0.280572,0.2805653 v 1.740778 c 0,0.1530346 -0.127531,0.2805642 -0.280572,0.2805642 z", + }; +}; +""" + + +def serve_iconset(): + return web.Response(body=ICON, content_type="application/javascript") diff --git a/custom_components/hacs/ws_api_handlers.py b/custom_components/hacs/ws_api_handlers.py deleted file mode 100644 index 6f52660..0000000 --- a/custom_components/hacs/ws_api_handlers.py +++ /dev/null @@ -1,379 +0,0 @@ -"""WebSocket API for HACS.""" -# pylint: disable=unused-argument -import sys -import os -import voluptuous as vol -from aiogithubapi import AIOGitHubException -from homeassistant.components import websocket_api -import homeassistant.helpers.config_validation as cv -from .hacsbase.exceptions import HacsException -from .store import async_load_from_store, async_save_to_store - -from custom_components.hacs.globals import get_hacs -from custom_components.hacs.helpers.register_repository import register_repository - - -async def setup_ws_api(hass): - """Set up WS API handlers.""" - websocket_api.async_register_command(hass, hacs_settings) - websocket_api.async_register_command(hass, hacs_config) - websocket_api.async_register_command(hass, hacs_repositories) - websocket_api.async_register_command(hass, hacs_repository) - websocket_api.async_register_command(hass, hacs_repository_data) - websocket_api.async_register_command(hass, check_local_path) - websocket_api.async_register_command(hass, hacs_status) - websocket_api.async_register_command(hass, acknowledge_critical_repository) - websocket_api.async_register_command(hass, get_critical_repositories) - - -@websocket_api.async_response -@websocket_api.websocket_command( - { - vol.Required("type"): "hacs/settings", - vol.Optional("action"): cv.string, - vol.Optional("category"): cv.string, - } -) -async def hacs_settings(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - action = msg["action"] - hacs.logger.debug(f"WS action '{action}'") - - if action == "set_fe_grid": - hacs.configuration.frontend_mode = "Grid" - - elif action == "onboarding_done": - hacs.configuration.onboarding_done = True - - elif action == "set_fe_table": - hacs.configuration.frontend_mode = "Table" - - elif action == "set_fe_compact_true": - hacs.configuration.frontend_compact = False - - elif action == "set_fe_compact_false": - hacs.configuration.frontend_compact = True - - elif action == "reload_data": - hacs.system.status.reloading_data = True - hass.bus.async_fire("hacs/status", {}) - await hacs.recuring_tasks_all() - hacs.system.status.reloading_data = False - hass.bus.async_fire("hacs/status", {}) - - elif action == "upgrade_all": - hacs.system.status.upgrading_all = True - hacs.system.status.background_task = True - hass.bus.async_fire("hacs/status", {}) - for repository in hacs.repositories: - if repository.pending_upgrade: - repository.status.selected_tag = None - await repository.install() - hacs.system.status.upgrading_all = False - hacs.system.status.background_task = False - hass.bus.async_fire("hacs/status", {}) - hass.bus.async_fire("hacs/repository", {}) - - elif action == "clear_new": - for repo in hacs.repositories: - if msg.get("category") == repo.data.category: - if repo.status.new: - hacs.logger.debug( - f"Clearing new flag from '{repo.data.full_name}'" - ) - repo.status.new = False - else: - hacs.logger.error(f"WS action '{action}' is not valid") - hass.bus.async_fire("hacs/config", {}) - await hacs.data.async_write() - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/config"}) -async def hacs_config(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - config = hacs.configuration - - content = {} - content["frontend_mode"] = config.frontend_mode - content["frontend_compact"] = config.frontend_compact - content["onboarding_done"] = config.onboarding_done - content["version"] = hacs.version - content["dev"] = config.dev - content["debug"] = config.debug - content["country"] = config.country - content["experimental"] = config.experimental - content["categories"] = hacs.common.categories - - connection.send_message(websocket_api.result_message(msg["id"], content)) - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/status"}) -async def hacs_status(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - content = { - "startup": hacs.system.status.startup, - "background_task": hacs.system.status.background_task, - "lovelace_mode": hacs.system.lovelace_mode, - "reloading_data": hacs.system.status.reloading_data, - "upgrading_all": hacs.system.status.upgrading_all, - "disabled": hacs.system.disabled, - } - connection.send_message(websocket_api.result_message(msg["id"], content)) - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/repositories"}) -async def hacs_repositories(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - repositories = hacs.repositories - content = [] - for repo in repositories: - if repo.data.category in hacs.common.categories: - data = { - "additional_info": repo.information.additional_info, - "authors": repo.data.authors, - "available_version": repo.display_available_version, - "beta": repo.status.show_beta, - "can_install": repo.can_install, - "category": repo.data.category, - "country": repo.data.country, - "config_flow": repo.config_flow, - "custom": repo.custom, - "default_branch": repo.data.default_branch, - "description": repo.data.description, - "domain": repo.integration_manifest.get("domain"), - "downloads": repo.releases.downloads, - "file_name": repo.data.file_name, - "first_install": repo.status.first_install, - "full_name": repo.data.full_name, - "hide": repo.status.hide, - "hide_default_branch": repo.data.hide_default_branch, - "homeassistant": repo.data.homeassistant, - "id": repo.information.uid, - "info": repo.information.info, - "installed_version": repo.display_installed_version, - "installed": repo.status.installed, - "javascript_type": repo.information.javascript_type, - "last_updated": repo.information.last_updated, - "local_path": repo.content.path.local, - "main_action": repo.main_action, - "name": repo.display_name, - "new": repo.status.new, - "pending_upgrade": repo.pending_upgrade, - "releases": repo.releases.published_tags, - "selected_tag": repo.status.selected_tag, - "stars": repo.data.stargazers_count, - "state": repo.state, - "status_description": repo.display_status_description, - "status": repo.display_status, - "topics": repo.data.topics, - "updated_info": repo.status.updated_info, - "version_or_commit": repo.display_version_or_commit, - } - - content.append(data) - - connection.send_message(websocket_api.result_message(msg["id"], content)) - - -@websocket_api.async_response -@websocket_api.websocket_command( - { - vol.Required("type"): "hacs/repository", - vol.Optional("action"): cv.string, - vol.Optional("repository"): cv.string, - } -) -async def hacs_repository(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - try: - repo_id = msg.get("repository") - action = msg.get("action") - - if repo_id is None or action is None: - return - - repository = hacs.get_by_id(repo_id) - hacs.logger.debug(f"Running {action} for {repository.data.full_name}") - - if action == "update": - await repository.update_repository() - repository.status.updated_info = True - repository.status.new = False - - elif action == "install": - was_installed = repository.status.installed - await repository.install() - if not was_installed: - hass.bus.async_fire("hacs/reload", {"force": False}) - - elif action == "uninstall": - await repository.uninstall() - hass.bus.async_fire("hacs/reload", {"force": False}) - - elif action == "hide": - repository.status.hide = True - - elif action == "unhide": - repository.status.hide = False - - elif action == "show_beta": - repository.status.show_beta = True - await repository.update_repository() - - elif action == "hide_beta": - repository.status.show_beta = False - await repository.update_repository() - - elif action == "delete": - repository.status.show_beta = False - repository.remove() - - elif action == "set_version": - if msg["version"] == repository.data.default_branch: - repository.status.selected_tag = None - else: - repository.status.selected_tag = msg["version"] - await repository.update_repository() - - else: - hacs.logger.error(f"WS action '{action}' is not valid") - - repository.state = None - await hacs.data.async_write() - except AIOGitHubException as exception: - hass.bus.async_fire("hacs/error", {"message": str(exception)}) - except AttributeError as exception: - hass.bus.async_fire( - "hacs/error", {"message": f"Could not use repository with ID {repo_id}"} - ) - except Exception as exception: # pylint: disable=broad-except - hass.bus.async_fire("hacs/error", {"message": str(exception)}) - - -@websocket_api.async_response -@websocket_api.websocket_command( - { - vol.Required("type"): "hacs/repository/data", - vol.Optional("action"): cv.string, - vol.Optional("repository"): cv.string, - vol.Optional("data"): cv.string, - } -) -async def hacs_repository_data(hass, connection, msg): - """Handle get media player cover command.""" - hacs = get_hacs() - repo_id = msg.get("repository") - action = msg.get("action") - data = msg.get("data") - - if repo_id is None: - return - - if action == "add": - if "github." in repo_id: - repo_id = repo_id.split("github.com/")[1] - - if repo_id in hacs.common.skip: - hacs.common.skip.remove(repo_id) - - if not hacs.get_by_name(repo_id): - try: - registration = await register_repository(repo_id, data.lower()) - if registration is not None: - raise HacsException(registration) - except Exception as exception: # pylint: disable=broad-except - hass.bus.async_fire( - "hacs/error", - { - "action": "add_repository", - "exception": str(sys.exc_info()[0].__name__), - "message": str(exception), - }, - ) - else: - hass.bus.async_fire( - "hacs/error", - { - "action": "add_repository", - "message": f"Repository '{repo_id}' exists in the store.", - }, - ) - - repository = hacs.get_by_name(repo_id) - else: - repository = hacs.get_by_id(repo_id) - - if repository is None: - hass.bus.async_fire("hacs/repository", {}) - return - - hacs.logger.debug(f"Running {action} for {repository.data.full_name}") - - if action == "set_state": - repository.state = data - - elif action == "set_version": - repository.status.selected_tag = data - await repository.update_repository() - repository.state = None - - elif action == "add": - repository.state = None - - else: - repository.state = None - hacs.logger.error(f"WS action '{action}' is not valid") - - await hacs.data.async_write() - - -@websocket_api.async_response -@websocket_api.websocket_command( - {vol.Required("type"): "hacs/check_path", vol.Optional("path"): cv.string} -) -async def check_local_path(hass, connection, msg): - """Handle get media player cover command.""" - path = msg.get("path") - exist = {"exist": False} - - if path is None: - return - - if os.path.exists(path): - exist["exist"] = True - - connection.send_message(websocket_api.result_message(msg["id"], exist)) - - -@websocket_api.async_response -@websocket_api.websocket_command({vol.Required("type"): "hacs/get_critical"}) -async def get_critical_repositories(hass, connection, msg): - """Handle get media player cover command.""" - critical = await async_load_from_store(hass, "critical") - if not critical: - critical = [] - connection.send_message(websocket_api.result_message(msg["id"], critical)) - - -@websocket_api.async_response -@websocket_api.websocket_command( - {vol.Required("type"): "hacs/critical", vol.Optional("repository"): cv.string} -) -async def acknowledge_critical_repository(hass, connection, msg): - """Handle get media player cover command.""" - repository = msg["repository"] - - critical = await async_load_from_store(hass, "critical") - for repo in critical: - if repository == repo["repository"]: - repo["acknowledged"] = True - await async_save_to_store(hass, "critical", critical) - connection.send_message(websocket_api.result_message(msg["id"], critical)) diff --git a/custom_components/input_select/__pycache__/__init__.cpython-38.pyc b/custom_components/input_select/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d169c4d45b038027d418176156303d4bd81af12a GIT binary patch literal 5597 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 literal 0 HcmV?d00001 diff --git a/custom_components/lovelace_gen/__init__.py b/custom_components/lovelace_gen/__init__.py new file mode 100644 index 0000000..c1e0974 --- /dev/null +++ b/custom_components/lovelace_gen/__init__.py @@ -0,0 +1,97 @@ +import os +import logging +import json +import io +import time +from collections import OrderedDict + +import jinja2 + +from homeassistant.util.yaml import loader +from homeassistant.exceptions import HomeAssistantError + +_LOGGER = logging.getLogger(__name__) + +def fromjson(value): + return json.loads(value) + +jinja = jinja2.Environment(loader=jinja2.FileSystemLoader("/")) + +jinja.filters['fromjson'] = fromjson + +llgen_config = {} + +def load_yaml(fname, args={}): + try: + ll_gen = False + with open(fname, encoding="utf-8") as f: + if f.readline().lower().startswith("# lovelace_gen"): + ll_gen = True + + if ll_gen: + stream = io.StringIO(jinja.get_template(fname).render({**args, "_global": llgen_config})) + stream.name = fname + return loader.yaml.load(stream, Loader=loader.SafeLineLoader) or OrderedDict() + else: + with open(fname, encoding="utf-8") as config_file: + return loader.yaml.load(config_file, Loader=loader.SafeLineLoader) or OrderedDict() + except loader.yaml.YAMLError as exc: + _LOGGER.error(str(exc)) + raise HomeAssistantError(exc) + except UnicodeDecodeError as exc: + _LOGGER.error("Unable to read file %s: %s", fname, exc) + raise HomeAssistantError(exc) + + +def _include_yaml(ldr, node): + args = {} + if isinstance(node.value, str): + fn = node.value + else: + fn, args, *_ = ldr.construct_sequence(node) + fname = os.path.abspath(os.path.join(os.path.dirname(ldr.name), fn)) + try: + return loader._add_reference(load_yaml(fname, args), ldr, node) + except FileNotFoundError as exc: + _LOGGER.error("Unable to include file %s: %s", fname, exc); + raise HomeAssistantError(exc) + +def _uncache_file(ldr, node): + path = node.value + timestamp = str(time.time()) + if '?' in path: + return f"{path}&{timestamp}" + return f"{path}?{timestamp}" + +loader.load_yaml = load_yaml +loader.yaml.SafeLoader.add_constructor("!include", _include_yaml) +loader.yaml.SafeLoader.add_constructor("!file", _uncache_file) + +async def async_setup(hass, config): + llgen_config.update(config.get("lovelace_gen")); + return True + +# Allow redefinition of node anchors +import yaml + +def compose_node(self, parent, index): + if self.check_event(yaml.events.AliasEvent): + event = self.get_event() + anchor = event.anchor + if anchor not in self.anchors: + raise yaml.composer.ComposerError(None, None, "found undefined alias %r" + % anchor, event.start_mark) + return self.anchors[anchor] + event = self.peek_event() + anchor = event.anchor + self.descend_resolver(parent, index) + if self.check_event(yaml.events.ScalarEvent): + node = self.compose_scalar_node(anchor) + elif self.check_event(yaml.events.SequenceStartEvent): + node = self.compose_sequence_node(anchor) + elif self.check_event(yaml.events.MappingStartEvent): + node = self.compose_mapping_node(anchor) + self.ascend_resolver() + return node + +yaml.composer.Composer.compose_node = compose_node diff --git a/custom_components/lovelace_gen/__pycache__/__init__.cpython-37.pyc b/custom_components/lovelace_gen/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000000000000000000000000000000000000..532f30ad80eda0de8d132b4336e2ae8486b1631b GIT binary patch literal 3000 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 literal 0 HcmV?d00001 diff --git a/custom_components/lovelace_gen/manifest.json b/custom_components/lovelace_gen/manifest.json new file mode 100644 index 0000000..74a3829 --- /dev/null +++ b/custom_components/lovelace_gen/manifest.json @@ -0,0 +1,8 @@ +{ + "domain": "lovelace_gen", + "name": "Lovelace Gen", + "documentation": "", + "dependencies": ["lovelace"], + "codeowners": [], + "requirements": ["jinja2"] +} diff --git a/custom_components/nodered/__init__.py b/custom_components/nodered/__init__.py new file mode 100644 index 0000000..9db7199 --- /dev/null +++ b/custom_components/nodered/__init__.py @@ -0,0 +1,258 @@ +""" +Component to integrate with node-red. + +For more details about this component, please refer to +https://github.com/zachowj/hass-node-red +""" +import asyncio +import logging +import os +from typing import Any, Dict, Optional, Union + +from integrationhelper.const import CC_STARTUP_VERSION + +from homeassistant.const import ( + CONF_DEVICE_CLASS, + CONF_ICON, + CONF_STATE, + CONF_TYPE, + CONF_UNIT_OF_MEASUREMENT, +) +from homeassistant.core import callback +from homeassistant.helpers.dispatcher import ( + async_dispatcher_connect, + async_dispatcher_send, +) +from homeassistant.helpers.entity import Entity + +from .const import ( + CONF_COMPONENT, + CONF_CONFIG, + CONF_DEVICE_INFO, + CONF_NAME, + CONF_NODE_ID, + CONF_REMOVE, + CONF_SERVER_ID, + CONF_VERSION, + DEFAULT_NAME, + DOMAIN, + DOMAIN_DATA, + ISSUE_URL, + NODERED_DISCOVERY_UPDATED, + NODERED_ENTITY, + REQUIRED_FILES, + VERSION, +) +from .discovery import ( + ALREADY_DISCOVERED, + CHANGE_ENTITY_TYPE, + CONFIG_ENTRY_IS_SETUP, + NODERED_DISCOVERY, + start_discovery, + stop_discovery, +) +from .websocket import register_websocket_handlers + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup(hass, config): + """Stub to allow setting up this component. + + Configuration through YAML is not supported. + """ + return True + + +async def async_setup_entry(hass, config_entry): + """Set up this integration using UI.""" + + # Print startup message + _LOGGER.info( + CC_STARTUP_VERSION.format(name=DOMAIN, version=VERSION, issue_link=ISSUE_URL) + ) + + # Check that all required files are present + file_check = await hass.async_add_executor_job(check_files, hass) + if not file_check: + return False + + # Create DATA dict + hass.data[DOMAIN_DATA] = {} + + register_websocket_handlers(hass) + await start_discovery(hass, hass.data[DOMAIN_DATA], config_entry) + hass.bus.async_fire(DOMAIN, {CONF_TYPE: "loaded", CONF_VERSION: VERSION}) + + return True + + +def check_files(hass): + """Return bool that indicates if all files are present.""" + # Verify that the user downloaded all files. + base = f"{hass.config.path()}/custom_components/{DOMAIN}/" + missing = [] + for file in REQUIRED_FILES: + fullpath = "{}{}".format(base, file) + if not os.path.exists(fullpath): + missing.append(file) + + if missing: + _LOGGER.critical(f"The following files are missing: {str(missing)}") + returnvalue = False + else: + returnvalue = True + + return returnvalue + + +async def async_remove_entry(hass, config_entry): + """Handle removal of an entry.""" + if hass.data[DOMAIN_DATA][CONFIG_ENTRY_IS_SETUP]: + await asyncio.wait( + [ + hass.config_entries.async_forward_entry_unload(config_entry, platform) + for platform in hass.data[DOMAIN_DATA][CONFIG_ENTRY_IS_SETUP] + ] + ) + + stop_discovery(hass) + del hass.data[DOMAIN_DATA] + hass.bus.async_fire(DOMAIN, {CONF_TYPE: "unloaded"}) + + +class NodeRedEntity(Entity): + """nodered Sensor class.""" + + def __init__(self, hass, config): + """Initialize the entity.""" + self.hass = hass + self.attr = {} + self._config = config[CONF_CONFIG] + self._component = None + self._device_info = config.get(CONF_DEVICE_INFO) + self._state = None + self._server_id = config[CONF_SERVER_ID] + self._node_id = config[CONF_NODE_ID] + self._remove_signal_discovery_update = None + self._remove_signal_entity_update = None + + @property + def should_poll(self) -> bool: + """Return True if entity has to be polled for state. + + False if entity pushes its state to HA. + """ + return False + + @property + def unique_id(self) -> Optional[str]: + """Return a unique ID to use for this sensor.""" + return f"{DOMAIN}-{self._server_id}-{self._node_id}" + + @property + def device_class(self) -> Optional[str]: + """Return the class of this binary_sensor.""" + return self._config.get(CONF_DEVICE_CLASS) + + @property + def name(self) -> Optional[str]: + """Return the name of the sensor.""" + return self._config.get(CONF_NAME, f"{DEFAULT_NAME} {self._node_id}") + + @property + def state(self) -> Union[None, str, int, float]: + """Return the state of the sensor.""" + return self._state + + @property + def icon(self) -> Optional[str]: + """Return the icon of the sensor.""" + return self._config.get(CONF_ICON) + + @property + def unit_of_measurement(self) -> Optional[str]: + """Return the unit this state is expressed in.""" + return self._config.get(CONF_UNIT_OF_MEASUREMENT) + + @property + def device_state_attributes(self) -> Optional[Dict[str, Any]]: + """Return the state attributes.""" + return self.attr + + @property + def device_info(self) -> Optional[Dict[str, Any]]: + """Return device specific attributes.""" + info = None + if self._device_info is not None and "id" in self._device_info: + # Use the id property to create the device identifier then delete it + info = {"identifiers": {(DOMAIN, self._device_info["id"])}} + del self._device_info["id"] + info.update(self._device_info) + + return info + + @callback + def handle_entity_update(self, msg): + """Update entity state.""" + _LOGGER.debug(f"Entity Update: {msg}") + self.attr = msg.get("attributes", {}) + self._state = msg[CONF_STATE] + self.async_write_ha_state() + + @callback + def handle_discovery_update(self, msg, connection): + """Update entity config.""" + if CONF_REMOVE not in msg: + self._config = msg[CONF_CONFIG] + self.async_write_ha_state() + return + + # Otherwise, remove entity + if msg[CONF_REMOVE] == CHANGE_ENTITY_TYPE: + # recreate entity if component type changed + @callback + def recreate_entity(): + """Create entity with new type.""" + del msg[CONF_REMOVE] + async_dispatcher_send( + self.hass, + NODERED_DISCOVERY.format(msg[CONF_COMPONENT]), + msg, + connection, + ) + + self.async_on_remove(recreate_entity) + + self.hass.async_create_task(self.async_remove()) + + async def async_added_to_hass(self) -> None: + """Run when entity about to be added to hass.""" + + self._remove_signal_entity_update = async_dispatcher_connect( + self.hass, + NODERED_ENTITY.format(self._server_id, self._node_id), + self.handle_entity_update, + ) + self._remove_signal_discovery_update = async_dispatcher_connect( + self.hass, + NODERED_DISCOVERY_UPDATED.format(self.unique_id), + self.handle_discovery_update, + ) + + async def async_will_remove_from_hass(self) -> None: + """Run when entity will be removed from hass.""" + if self._remove_signal_entity_update is not None: + self._remove_signal_entity_update() + if self._remove_signal_discovery_update is not None: + self._remove_signal_discovery_update() + + del self.hass.data[DOMAIN_DATA][ALREADY_DISCOVERED][self.unique_id] + + # Remove the entity_id from the entity registry + registry = await self.hass.helpers.entity_registry.async_get_registry() + entity_id = registry.async_get_entity_id( + self._component, DOMAIN, self.unique_id, + ) + if entity_id: + registry.async_remove(entity_id) diff --git a/custom_components/nodered/__pycache__/__init__.cpython-38.pyc b/custom_components/nodered/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc4119b4aaa091d761bb02ab6707b114badef186 GIT binary patch literal 7642 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 literal 0 HcmV?d00001 diff --git a/custom_components/nodered/__pycache__/const.cpython-38.pyc b/custom_components/nodered/__pycache__/const.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ce9bc83edc322e09357186a2e3fe4429ee4e4171 GIT binary patch literal 1497 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 literal 0 HcmV?d00001 diff --git a/custom_components/nodered/__pycache__/websocket.cpython-38.pyc b/custom_components/nodered/__pycache__/websocket.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aab66c318678b41ed3c3f05fdfe8bc70d4a224a3 GIT binary patch literal 3982 zcmaJE%W@mXab|XbU4Q@xkP;uFL_rTrhAn}XBRh_xD4|G5LfHaTBrI8b2nxau$))ze z%r2M^1y-d(C87^G{|DA`I-@posj1Zex#HKcLsYTV3Sxh}OONXcCCcLDT^bE`JOw06A zR?16TX>ZILqj*nuGhWupdO0iSja%bh-pYFgtDw#j?u0jKO)8wZ=e+ZbP~uHl(`qN_ z&UmxdtilcVf;VT)0dCqU_o8>ny5!AU^WJ6avRAZ<-WBVLch$P;U9+xv*RAW`2i6DP z4eN$?)4E9sX9pkh(jm1z;&XQTEw#t&%xm5Hn5Xy{*AH&-{2{fo3Y$Nqe0uY?!mk~} zuMF_bGQaE)JNH+{7l5kF$Kgaj_=Ml#pYosayN9~ky#%}Q`BlK~sJ$ZYIjKErkK6gz zWRLDq>$4Lw6oBp)fA4o;|2^za}IpXK5tL;Pk#=l#xd=u z)t3t(;XwP`F}yF4qXUl_dp70)(tlyIRZ_apoqNt-hC%xkkBW;=tHrfI6l;OaZ?9CA z%kMC-YNe!{YK1#~yD9j(6GmJ#+kxk`d|T!Q`{7pLhg@d)Hus~Z$HTC-4zGg$uHy*a zY}uaU%beh0#~mFPen0s1fMF>ijiq|+VY9j{QwpgqKB>sGLRTA$jY@yJ@#1Mkj;rnG zl?RXO^{?PciAuBGa^07$_A9{8$HLi8xYdf<8z6SbeH#u6j{}b{h9T(E@}tJi7Jo-U z(b8C-<6@(+Qho4Eqp~V<>Jv-#Cr|6OO06Ns`tP-e)kiX~UY0A*s!Nq-wf3+sGis++ zUxxSPKJiNBN&Q)+&uO)?@~pCghsovolf`OH7LXc@-CVA&E&<`n3z-{i!56EI7t(;; z)oQ(VM5Vsb3d1%^JNO~4!CHb9-U1Mj9*O84qcO&Ym^Q?8#B2)wXWimT->JoDgBOP$ zDQu5CDQ0{{3H+}sF)S8{U5q6wC72SBkK#vlz zo19fO7P6EbQn0JGN{;mI_$v29NTz}g8*M3jU*VmuHA& zkk5%<)=6Z%N{b$aW=zRGIWVIXP^H03hHVZ)^o0u2Z%H(Ekg-$y_^tGx!8qR&d+Y}d z&C}EO$!0dbdG9oz%zzJudMVqTUJ|?&sp5ee4{doub@VsJ^50PPeE>anKn_V2a_2GG z)w)wXP0U9W@(cvfIeUrUqyAX^AhOPB* z%F+9(BzlSO$qzcJ(>DZFhDi1jLj^DjjPA^1RqU|(#R~MB2-H3%jjh&>8?<>!Co1_%$vF9$10rBUwM*2a*rVxY7*7}aq;}`^| zB_ji3(1hrP=pQ-_2N$C|y}~`{E+ePKGfDZldyv5XvRD%fAkmMkToyM0X_Qh}N(W6w z8h!i7d^|C9{6T{e*Z{=m0Hp5tP^nLqF05KddTe{F(8>Wa-RIYI?BmW5O5TBi{{-Z5 zbz_W9upCXY3;*8{C+{mw9*&f4s~iMnm9FAfz`jw+39O$Yi{L(jUjrCTZSgcGuto|j zOR>7AbVTjNBHqh}otI%-I9u3&LKq8_%A>&hB~mUUK)Z+s2(BTx3}6jbi2EdCg2wsm z%ow94)b*t5P$q=r3^l<9IjZPOzks}G+CjV76raK^X}H1qy5q0Q^mgEOwxUkZ38eIm{?{eO5THrr z#A#~5m6_$ulLs5z-NGp*9(@g_=R%szrr+{7 zcti!obv!m#;u+tTN%#i($E>RWX}JPt5veWuJ6Nv*S86~ZHOG%+Vht){q=piK9bNTK z<@Y#tQ~)V}0VK_PUSM}zexJfy_!j^=#E!{KRx~df89G5TG{f%Z(poMBvjQ^-#1_)@ r7feBb=V0Met$(qfSQbL@40VnI^}pdXl>uIPnn?o987lohKw bool: + """Initialize of Node-RED Discovery.""" + + async def async_device_message_received(msg, connection): + """Process the received message.""" + component = msg[CONF_COMPONENT] + server_id = msg[CONF_SERVER_ID] + node_id = msg[CONF_NODE_ID] + + if component not in SUPPORTED_COMPONENTS: + _LOGGER.warning(f"Integration {component} is not supported") + return + + discovery_hash = f"{DOMAIN}-{server_id}-{node_id}" + data = hass.data[DOMAIN_DATA] + + _LOGGER.debug(f"Discovery message: {msg}") + + if ALREADY_DISCOVERED not in data: + data[ALREADY_DISCOVERED] = {} + if discovery_hash in data[ALREADY_DISCOVERED]: + + if data[ALREADY_DISCOVERED][discovery_hash] != component: + # Remove old + log_text = f"Changing {data[ALREADY_DISCOVERED][discovery_hash]} to" + msg[CONF_REMOVE] = CHANGE_ENTITY_TYPE + elif CONF_REMOVE in msg: + log_text = "Removing" + else: + # Dispatch update + log_text = "Updating" + + _LOGGER.info(f"{log_text} {component} {server_id} {node_id}") + + data[ALREADY_DISCOVERED][discovery_hash] = component + async_dispatcher_send( + hass, NODERED_DISCOVERY_UPDATED.format(discovery_hash), msg, connection + ) + else: + # Add component + _LOGGER.info(f"Creating {component} {server_id} {node_id}") + data[ALREADY_DISCOVERED][discovery_hash] = component + + async with data[CONFIG_ENTRY_LOCK]: + if component not in data[CONFIG_ENTRY_IS_SETUP]: + await hass.config_entries.async_forward_entry_setup( + config_entry, component + ) + data[CONFIG_ENTRY_IS_SETUP].add(component) + + async_dispatcher_send( + hass, NODERED_DISCOVERY_NEW.format(component), msg, connection + ) + + hass.data[DOMAIN_DATA][CONFIG_ENTRY_LOCK] = asyncio.Lock() + hass.data[DOMAIN_DATA][CONFIG_ENTRY_IS_SETUP] = set() + + hass.data[DOMAIN_DATA][DISCOVERY_DISPATCHED] = async_dispatcher_connect( + hass, NODERED_DISCOVERY, async_device_message_received, + ) + + +def stop_discovery(hass: HomeAssistantType): + """Remove discovery dispatcher.""" + hass.data[DOMAIN_DATA][DISCOVERY_DISPATCHED]() diff --git a/custom_components/nodered/manifest.json b/custom_components/nodered/manifest.json new file mode 100644 index 0000000..2850639 --- /dev/null +++ b/custom_components/nodered/manifest.json @@ -0,0 +1,14 @@ +{ + "domain": "nodered", + "name": "Node-RED", + "documentation": "https://github.com/zachowj/node-red", + "dependencies": [], + "config_flow": true, + "codeowners": [ + "@zachowj" + ], + "requirements": [ + "integrationhelper" + ], + "homeassistant": "0.96.0" +} diff --git a/custom_components/nodered/sensor.py b/custom_components/nodered/sensor.py new file mode 100644 index 0000000..34f191d --- /dev/null +++ b/custom_components/nodered/sensor.py @@ -0,0 +1,37 @@ +"""Sensor platform for nodered.""" +import logging + +from homeassistant.const import CONF_STATE +from homeassistant.helpers.dispatcher import async_dispatcher_connect + +from . import NodeRedEntity +from .const import CONF_ATTRIBUTES, CONF_SENSOR, NODERED_DISCOVERY_NEW + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up sensor platform.""" + + async def async_discover(config, connection): + await _async_setup_entity(hass, config, async_add_entities) + + async_dispatcher_connect( + hass, NODERED_DISCOVERY_NEW.format(CONF_SENSOR), async_discover, + ) + + +async def _async_setup_entity(hass, config, async_add_entities): + """Set up the Node-RED sensor.""" + async_add_entities([NodeRedSensor(hass, config)]) + + +class NodeRedSensor(NodeRedEntity): + """Node-RED Sensor class.""" + + def __init__(self, hass, config): + """Initialize the sensor.""" + super().__init__(hass, config) + self._component = CONF_SENSOR + self._state = config.get(CONF_STATE) + self.attr = config.get(CONF_ATTRIBUTES, {}) diff --git a/custom_components/nodered/services.yaml b/custom_components/nodered/services.yaml new file mode 100644 index 0000000..6bd5b36 --- /dev/null +++ b/custom_components/nodered/services.yaml @@ -0,0 +1,17 @@ +trigger: + description: Trigger a Node-RED Event Node + fields: + entity_id: + description: Entity Id of the Node-RED switch + example: switch.nodered_motion + trigger_entity_id: + description: Entity Id to trigger the event node with. Only needed if the node is not triggered by a single entity. + example: sun.sun + skip_condition: + description: Skip conditions of the node (defaults to false) + example: true + output_path: + description: Which output of the node to use (defaults to true, the top output). Only used when skip_condition is set to true. + example: true + payload: + description: The payload the node will output when triggered. Work only when triggering a entity node not an event node. diff --git a/custom_components/nodered/switch.py b/custom_components/nodered/switch.py new file mode 100644 index 0000000..4cc82cf --- /dev/null +++ b/custom_components/nodered/switch.py @@ -0,0 +1,153 @@ +"""Sensor platform for nodered.""" +import logging + +import voluptuous as vol + +from homeassistant.components.websocket_api import event_message +from homeassistant.const import ( + CONF_ENTITY_ID, + CONF_ICON, + CONF_ID, + CONF_STATE, + CONF_TYPE, + EVENT_STATE_CHANGED, +) +from homeassistant.core import callback +from homeassistant.helpers import entity_platform +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_connect +from homeassistant.helpers.entity import ToggleEntity + +from . import NodeRedEntity +from .const import ( + CONF_CONFIG, + CONF_DATA, + CONF_OUTPUT_PATH, + CONF_PAYLOAD, + CONF_SKIP_CONDITION, + CONF_SWITCH, + CONF_TRIGGER_ENTITY_ID, + NODERED_DISCOVERY_NEW, + SERVICE_TRIGGER, + SWITCH_ICON, +) + +_LOGGER = logging.getLogger(__name__) + +SERVICE_TRIGGER_SCHEMA = vol.Schema( + { + vol.Required(CONF_ENTITY_ID): cv.entity_ids, + vol.Optional(CONF_TRIGGER_ENTITY_ID): cv.entity_id, + vol.Optional(CONF_SKIP_CONDITION): cv.boolean, + vol.Optional(CONF_OUTPUT_PATH): cv.boolean, + vol.Optional(CONF_PAYLOAD): vol.Extra, + } +) +EVENT_TRIGGER_NODE = "automation_triggered" + + +async def async_setup_entry(hass, config_entry, async_add_entities): + """Set up the Switch platform.""" + + async def async_discover(config, connection): + await _async_setup_entity(hass, config, async_add_entities, connection) + + async_dispatcher_connect( + hass, NODERED_DISCOVERY_NEW.format(CONF_SWITCH), async_discover, + ) + + platform = entity_platform.current_platform.get() + + platform.async_register_entity_service( + SERVICE_TRIGGER, SERVICE_TRIGGER_SCHEMA, "async_trigger_node" + ) + + +async def _async_setup_entity(hass, config, async_add_entities, connection): + """Set up the Node-RED Switch.""" + async_add_entities([NodeRedSwitch(hass, config, connection)]) + + +class NodeRedSwitch(ToggleEntity, NodeRedEntity): + """Node-RED Switch class.""" + + def __init__(self, hass, config, connection): + """Initialize the switch.""" + super().__init__(hass, config) + self._message_id = config[CONF_ID] + self._connection = connection + self._state = config.get(CONF_STATE, True) + self._component = CONF_SWITCH + self._available = True + + @property + def is_on(self) -> bool: + """Return the state of the switch.""" + return self._state + + @property + def icon(self): + """Return the icon of the sensor.""" + return self._config.get(CONF_ICON, SWITCH_ICON) + + @property + def available(self) -> bool: + """Return True if entity is available.""" + return self._available + + async def async_turn_off(self, **kwargs) -> None: + """Turn off the switch.""" + self._update_node_red(False) + + async def async_turn_on(self, **kwargs) -> None: + """Turn on the switch.""" + self._update_node_red(True) + + async def async_trigger_node(self, **kwargs) -> None: + """Trigger node in Node-RED.""" + data = {} + data[CONF_ENTITY_ID] = kwargs.get(CONF_TRIGGER_ENTITY_ID) + data[CONF_SKIP_CONDITION] = kwargs.get(CONF_SKIP_CONDITION, False) + data[CONF_OUTPUT_PATH] = kwargs.get(CONF_OUTPUT_PATH, True) + if kwargs.get(CONF_PAYLOAD) is not None: + data[CONF_PAYLOAD] = kwargs[CONF_PAYLOAD] + + self._connection.send_message( + event_message( + self._message_id, {CONF_TYPE: EVENT_TRIGGER_NODE, CONF_DATA: data}, + ) + ) + + def _update_node_red(self, state): + self._connection.send_message( + event_message( + self._message_id, {CONF_TYPE: EVENT_STATE_CHANGED, CONF_STATE: state} + ) + ) + + @callback + def handle_lost_connection(self): + """Set availability to False when disconnected.""" + self._available = False + self.async_write_ha_state() + + @callback + def handle_discovery_update(self, msg, connection): + """Update entity config.""" + if "remove" in msg: + # Remove entity + self.hass.async_create_task(self.async_remove()) + else: + self._available = True + self._state = msg[CONF_STATE] + self._config = msg[CONF_CONFIG] + self._message_id = msg[CONF_ID] + self._connection = connection + self._connection.subscriptions[msg[CONF_ID]] = self.handle_lost_connection + self.async_write_ha_state() + + async def async_added_to_hass(self) -> None: + """Run when entity about to be added to hass.""" + await super().async_added_to_hass() + + self._connection.subscriptions[self._message_id] = self.handle_lost_connection diff --git a/custom_components/nodered/translations/en.json b/custom_components/nodered/translations/en.json new file mode 100644 index 0000000..02f6ad0 --- /dev/null +++ b/custom_components/nodered/translations/en.json @@ -0,0 +1,14 @@ +{ + "config": { + "title": "Node-RED", + "step": { + "user": { + "title": "Node-RED", + "description": "Are you sure you want to set up Node-RED?" + } + }, + "abort": { + "single_instance_allowed": "Only a single configuration of Node-RED is allowed." + } + } +} diff --git a/custom_components/nodered/websocket.py b/custom_components/nodered/websocket.py new file mode 100644 index 0000000..17ffea7 --- /dev/null +++ b/custom_components/nodered/websocket.py @@ -0,0 +1,151 @@ +"""Websocket API for Node-RED.""" +import json +import logging + +import voluptuous as vol + +from homeassistant.components.websocket_api import ( + async_register_command, + async_response, + event_message, + require_admin, + result_message, + websocket_command, +) +from homeassistant.const import ( + CONF_ID, + CONF_NAME, + CONF_STATE, + CONF_TYPE, + CONF_WEBHOOK_ID, +) +from homeassistant.core import callback +import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.dispatcher import async_dispatcher_send +from homeassistant.helpers.typing import HomeAssistantType + +from .const import ( + CONF_ATTRIBUTES, + CONF_COMPONENT, + CONF_CONFIG, + CONF_DEVICE_INFO, + CONF_NODE_ID, + CONF_REMOVE, + CONF_SERVER_ID, + DOMAIN, + NODERED_DISCOVERY, + NODERED_ENTITY, + VERSION, +) + +_LOGGER = logging.getLogger(__name__) + + +def register_websocket_handlers(hass: HomeAssistantType): + """Register the websocket handlers.""" + + async_register_command(hass, websocket_version) + async_register_command(hass, websocket_webhook) + async_register_command(hass, websocket_discovery) + async_register_command(hass, websocket_entity) + + +@require_admin +@websocket_command( + { + vol.Required(CONF_TYPE): "nodered/discovery", + vol.Required(CONF_COMPONENT): cv.string, + vol.Required(CONF_SERVER_ID): cv.string, + vol.Required(CONF_NODE_ID): cv.string, + vol.Optional(CONF_CONFIG, default={}): dict, + vol.Optional(CONF_STATE): vol.Any(bool, str, int, float), + vol.Optional(CONF_ATTRIBUTES): dict, + vol.Optional(CONF_REMOVE): bool, + vol.Optional(CONF_DEVICE_INFO): dict, + } +) +def websocket_discovery(hass, connection, msg): + """Sensor command.""" + async_dispatcher_send( + hass, NODERED_DISCOVERY.format(msg[CONF_COMPONENT]), msg, connection + ) + connection.send_message(result_message(msg[CONF_ID], {"success": True})) + + +@require_admin +@websocket_command( + { + vol.Required(CONF_TYPE): "nodered/entity", + vol.Required(CONF_SERVER_ID): cv.string, + vol.Required(CONF_NODE_ID): cv.string, + vol.Required(CONF_STATE): vol.Any(bool, str, int, float), + vol.Optional(CONF_ATTRIBUTES, default={}): dict, + } +) +def websocket_entity(hass, connection, msg): + """Sensor command.""" + + async_dispatcher_send( + hass, NODERED_ENTITY.format(msg[CONF_SERVER_ID], msg[CONF_NODE_ID]), msg + ) + connection.send_message(result_message(msg[CONF_ID], {"success": True})) + + +@require_admin +@websocket_command({vol.Required(CONF_TYPE): "nodered/version"}) +def websocket_version(hass, connection, msg): + """Version command.""" + + connection.send_message(result_message(msg[CONF_ID], VERSION)) + + +@require_admin +@async_response +@websocket_command( + { + vol.Required(CONF_TYPE): "nodered/webhook", + vol.Required(CONF_WEBHOOK_ID): cv.string, + vol.Required(CONF_NAME): cv.string, + vol.Required(CONF_SERVER_ID): cv.string, + } +) +async def websocket_webhook(hass, connection, msg): + """Create webhook command.""" + webhook_id = msg[CONF_WEBHOOK_ID] + + @callback + async def handle_webhook(hass, id, request): + """Handle webhook callback.""" + body = await request.text() + try: + payload = json.loads(body) if body else {} + except ValueError: + payload = body + + data = {"payload": payload, "headers": dict(request.headers)} + + _LOGGER.debug(f"Webhook received {id[:15]}..: {data}") + connection.send_message(event_message(msg[CONF_ID], {"data": data})) + + def remove_webhook() -> None: + """Remove webhook command.""" + try: + hass.components.webhook.async_unregister(webhook_id) + + except ValueError: + pass + + _LOGGER.info(f"Webhook removed: {webhook_id[:15]}..") + connection.send_message(result_message(msg[CONF_ID], {"success": True})) + + try: + hass.components.webhook.async_register( + DOMAIN, msg[CONF_NAME], webhook_id, handle_webhook + ) + except ValueError: + connection.send_message(result_message(msg[CONF_ID], {"success": False})) + return + + _LOGGER.info(f"Webhook created: {webhook_id[:15]}..") + connection.subscriptions[msg[CONF_ID]] = remove_webhook + connection.send_message(result_message(msg[CONF_ID], {"success": True})) diff --git a/custom_components/variable/__init__.py b/custom_components/variable/__init__.py index dd0c48d..21b45b4 100644 --- a/custom_components/variable/__init__.py +++ b/custom_components/variable/__init__.py @@ -1,66 +1,89 @@ +"""variable implementation for Homme Assistant.""" import asyncio import logging import json import voluptuous as vol -from homeassistant.const import (CONF_NAME, ATTR_ICON) +from homeassistant.const import CONF_NAME, ATTR_ICON from homeassistant.helpers import config_validation as cv -from homeassistant.helpers import template from homeassistant.exceptions import TemplateError from homeassistant.loader import bind_hass -from homeassistant.helpers.entity import Entity from homeassistant.helpers.entity_component import EntityComponent from homeassistant.helpers.restore_state import RestoreEntity _LOGGER = logging.getLogger(__name__) -DOMAIN = 'variable' -ENTITY_ID_FORMAT = DOMAIN + '.{}' +DOMAIN = "variable" +ENTITY_ID_FORMAT = DOMAIN + ".{}" CONF_ATTRIBUTES = "attributes" CONF_VALUE = "value" CONF_RESTORE = "restore" ATTR_VARIABLE = "variable" -ATTR_VALUE = 'value' -ATTR_VALUE_TEMPLATE = 'value_template' +ATTR_VALUE = "value" +ATTR_VALUE_TEMPLATE = "value_template" ATTR_ATTRIBUTES = "attributes" ATTR_ATTRIBUTES_TEMPLATE = "attributes_template" ATTR_REPLACE_ATTRIBUTES = "replace_attributes" SERVICE_SET_VARIABLE = "set_variable" -SERVICE_SET_VARIABLE_SCHEMA = vol.Schema({ - vol.Required(ATTR_VARIABLE): cv.string, - vol.Optional(ATTR_VALUE): cv.match_all, - vol.Optional(ATTR_VALUE_TEMPLATE): cv.template, - vol.Optional(ATTR_ATTRIBUTES): dict, - vol.Optional(ATTR_ATTRIBUTES_TEMPLATE): cv.template, - vol.Optional(ATTR_REPLACE_ATTRIBUTES): cv.boolean -}) +SERVICE_SET_VARIABLE_SCHEMA = vol.Schema( + { + vol.Required(ATTR_VARIABLE): cv.string, + vol.Optional(ATTR_VALUE): cv.match_all, + vol.Optional(ATTR_VALUE_TEMPLATE): cv.template, + vol.Optional(ATTR_ATTRIBUTES): dict, + vol.Optional(ATTR_ATTRIBUTES_TEMPLATE): cv.template, + vol.Optional(ATTR_REPLACE_ATTRIBUTES): cv.boolean, + } +) + +CONFIG_SCHEMA = vol.Schema( + { + DOMAIN: vol.Schema( + { + cv.slug: vol.Any( + { + vol.Optional(CONF_NAME): cv.string, + vol.Optional(CONF_VALUE): cv.match_all, + vol.Optional(CONF_ATTRIBUTES): dict, + vol.Optional(CONF_RESTORE): cv.boolean, + }, + None, + ) + } + ) + }, + extra=vol.ALLOW_EXTRA, +) -CONFIG_SCHEMA = vol.Schema({ - DOMAIN: vol.Schema({ - cv.slug: vol.Any({ - vol.Optional(CONF_NAME): cv.string, - vol.Optional(CONF_VALUE): cv.match_all, - vol.Optional(CONF_ATTRIBUTES): dict, - vol.Optional(CONF_RESTORE): cv.boolean, - }, None) - }) -}, extra=vol.ALLOW_EXTRA) @bind_hass -def set_variable(hass, variable, value, value_template, attributes, attributes_template, replace_attributes): +def set_variable( + hass, + variable, + value, + value_template, + attributes, + attributes_template, + replace_attributes, +): """Set input_boolean to True.""" - hass.services.call(DOMAIN, SERVICE_SET_VARIABLE, { - ATTR_VARIABLE: variable, - ATTR_VALUE: value, - ATTR_VALUE_TEMPLATE: value_template, - ATTR_ATTRIBUTES: attributes, - ATTR_ATTRIBUTES_TEMPLATE: attributes_template, - ATTR_REPLACE_ATTRIBUTES: replace_attributes, - }) + hass.services.call( + DOMAIN, + SERVICE_SET_VARIABLE, + { + ATTR_VARIABLE: variable, + ATTR_VALUE: value, + ATTR_VALUE_TEMPLATE: value_template, + ATTR_ATTRIBUTES: attributes, + ATTR_ATTRIBUTES_TEMPLATE: attributes_template, + ATTR_REPLACE_ATTRIBUTES: replace_attributes, + }, + ) + async def async_setup(hass, config): """Set up variables.""" @@ -77,37 +100,45 @@ async def async_setup(hass, config): attributes = variable_config.get(CONF_ATTRIBUTES) restore = variable_config.get(CONF_RESTORE, False) - entities.append(Variable(variable_id, name, value, attributes, restore)) + entities.append( + Variable(variable_id, name, value, attributes, restore) + ) @asyncio.coroutine def async_set_variable_service(call): """Handle calls to the set_variable service.""" - entity_id = ENTITY_ID_FORMAT.format(call.data.get(ATTR_VARIABLE)) entity = component.get_entity(entity_id) if entity: - target_variables = [ entity ] - tasks = [variable.async_set_variable( - call.data.get(ATTR_VALUE), - call.data.get(ATTR_VALUE_TEMPLATE), - call.data.get(ATTR_ATTRIBUTES), - call.data.get(ATTR_ATTRIBUTES_TEMPLATE), - call.data.get(ATTR_REPLACE_ATTRIBUTES, False)) - for variable in target_variables] + target_variables = [entity] + tasks = [ + variable.async_set_variable( + call.data.get(ATTR_VALUE), + call.data.get(ATTR_VALUE_TEMPLATE), + call.data.get(ATTR_ATTRIBUTES), + call.data.get(ATTR_ATTRIBUTES_TEMPLATE), + call.data.get(ATTR_REPLACE_ATTRIBUTES, False), + ) + for variable in target_variables + ] if tasks: yield from asyncio.wait(tasks, loop=hass.loop) else: - _LOGGER.warning('Failed to set unknown variable: %s', entity_id) + _LOGGER.warning("Failed to set unknown variable: %s", entity_id) hass.services.async_register( - DOMAIN, SERVICE_SET_VARIABLE, async_set_variable_service, - schema=SERVICE_SET_VARIABLE_SCHEMA) + DOMAIN, + SERVICE_SET_VARIABLE, + async_set_variable_service, + schema=SERVICE_SET_VARIABLE_SCHEMA, + ) await component.async_add_entities(entities) return True + class Variable(RestoreEntity): """Representation of a variable.""" @@ -122,10 +153,14 @@ class Variable(RestoreEntity): async def async_added_to_hass(self): """Run when entity about to be added.""" await super().async_added_to_hass() - if self._restore == True: + if self._restore is True: + # If variable state have been saved. state = await self.async_get_last_state() if state: + # restore state self._value = state.state + # restore value + self._attributes = state.attributes @property def should_poll(self): @@ -142,8 +177,7 @@ class Variable(RestoreEntity): """Return the icon to be used for this entity.""" if self._attributes is not None: return self._attributes.get(ATTR_ICON) - else: - return None + return None @property def state(self): @@ -156,9 +190,15 @@ class Variable(RestoreEntity): return self._attributes @asyncio.coroutine - def async_set_variable(self, value, value_template, attributes, attributes_template, replace_attributes): + def async_set_variable( + self, + value, + value_template, + attributes, + attributes_template, + replace_attributes, + ): """Update variable.""" - current_state = self.hass.states.get(self.entity_id) updated_attributes = None updated_value = None @@ -176,7 +216,11 @@ class Variable(RestoreEntity): attributes_template.hass = self.hass try: - attributes = json.loads(attributes_template.async_render({ 'variable': current_state })) + attributes = json.loads( + attributes_template.async_render( + {"variable": current_state} + ) + ) if isinstance(attributes, dict): if updated_attributes is not None: @@ -185,8 +229,11 @@ class Variable(RestoreEntity): updated_attributes = attributes except TemplateError as ex: - _LOGGER.error('Could not render attribute_template %s: %s', - self.entity_id, ex) + _LOGGER.error( + "Could not render attribute_template %s: %s", + self.entity_id, + ex, + ) if value is not None: updated_value = value @@ -194,14 +241,19 @@ class Variable(RestoreEntity): elif value_template is not None: try: value_template.hass = self.hass - updated_value = value_template.async_render({ 'variable': current_state }) + updated_value = value_template.async_render( + {"variable": current_state} + ) except TemplateError as ex: - _LOGGER.error('Could not render value_template %s: %s', - self.entity_id, ex) + _LOGGER.error( + "Could not render value_template %s: %s", + self.entity_id, + ex, + ) - self._attributes = updated_attributes; + self._attributes = updated_attributes if updated_value is not None: - self._value = updated_value; + self._value = updated_value - yield from self.async_update_ha_state() \ No newline at end of file + yield from self.async_update_ha_state() diff --git a/custom_components/variable/__pycache__/__init__.cpython-37.pyc b/custom_components/variable/__pycache__/__init__.cpython-37.pyc index 9970ea6cb6857609896cfea1e6fd2ecbc308a86d..cb9dcdd41b72c41f5230dddf46af1cdb11c9cb94 100644 GIT binary patch delta 1976 zcmZ`)-ESL35WiiYeP^HT_$#sF#<9~hX`QA`NJ`VDrKBzWfTn!3eW+F{mYZALYwvv4 z-UVnCREhMlfM9vyiAI745CWY2`ap+@i!ZVoR znam`(UU!pTidl-s-L#is8O0NB)*E00iW_du%d@=Vrd#letVlFYZnZRPh~(*MT<`Yy3|-rudYTxDHnA-= z1)xbIx_efj{w5Jc6xR?&0q&&^x?s(U@AVFaFb*g{!jK;)-m@A&#Tden+;7yY+ki$! zYx$)UCyt$oj5pe%>vTKKxF||tJd+uL>HqYP%DYCrb_FDTJ7WcsqB=>?lwm?&iCvqK z8;KdY-5e+zc)}Dy2_T9)A@>3?CQqBkCwHJEx#A0dHFUaM;H9H%JGk0iX$jr|%efGP z@_TcEjL6^2#g6iya$OoV&LZGtn^epoM?u5^`Bz(^_%p6VMxS-P%P=dE2Uy z=j6}U*r_9UnmXtxFu3Y56uf|(M9Efb#cc;ctMwr2;v~#{1T8=>X*37X$-Ze`d}%@TN~V46pm0>}4#!Q98p3ofdqtsm_|4%x90a3;z*}(MI3U@7vhNY^0lhE_hVnJ z{3O3P`iMvBu>VJB!OH+zO8%9v%tEe_w4Nd+_q4iJ16YFymm9rG-to+*>%i z?>KB%QH;!<@OxZ@SH&{QROX#Vh7J)gBH$|$`w;5#r$X_SO{2h-r%;TQKr8^f3r)RP zIFy=ZI-QQgXCSKi=DfIoYt_3nT))$S3LwtNM)B-IRJr7P95U^Gb>nZY@E+E_K%noU z(!6-x-{f&L@e^iaQ6ma|s oa{xMo1wIUzYz|J2(=?=L8tVlq=sHcnM1P#6sd!3H51V`c1w($0*#H0l delta 1896 zcmZ`(O>7%Q6y6!H*Xv!|@o((7abh>LN!t9hp+BUge`)#yYJpNf)j+Tu?~;98txE0|GAG;8vmK!i5V*ZtaQp#%W8d;?;cfz2})XZ{E*r z?EYt0W-*<%H2B^AdUpBm>gSot`e#Jb9L*t~#`-g1M9Gvew zyh&KavDgqFa_q9ks!ob+bJA?s$*>yeM}Uty1FR1EhLdI6og5o;^6Uk!-`xRYCm-1) z+`2pte1eZEKB;(Jaa-{Q@Lh^+U;mGKO!3{W0ZvSD{qhbz1dDcnxoMbZ)CTzPvH4-e z`{uU+@0+Y`S38 zF)AO#_T};*?1vF#0JNAz?k(z7dTs~=QAOB>FpN+Gn4}S%*(1>QBoQT4k0R6o9$E{Y z8-#7Y(>wAm$N>c~jH@SZnX~eD<9&NO$YKnkB9A9V$xhiyR7go)O&rdTfclZeI`F6} ze=w@$H-QG|qo(JHMRj5j%P@(2m{=+&K`W*Z1_7eD3$X~qn0(tjwR0D0l5Jo3YoXia zqKI;)6M&twY(1Hq-iV+4v%pu^-CaKtq9Q{+bjO@OZfQM_n z@iCC0hByW*ndv1{a% z{L5|}U4m2K0;ln?v&c!5Y&P3oD+s{s`2j@@1@_s?Y{&!=fyKTSw z33_J$V2P%v9L!v-tJ^)-Gj-Y9^3%-nG#&wUr9$wG``I9b#%Z>~P`E2=ArJ1{s|x|xkDG9K3_Dz z$d@pJMF36DLd12NfhsqN33oJbrj!(^rDQBDgA9Y#01X9Q9zN8_mHdVEGhjm{GBSI@ z?{N`c5wD?6mD=mbumr>#2-qIt5CS?8rMHTr%{}i?VQl6794@LO%mer^R6CmBs%fUo zR2)9UN?a|N+^1|u3C~|$b-SzLEqT6hp&{b1F{)hhI~-~cy06s@XZQzg-V0s78wf-G zSg0BaTr@2IEDTd&%0CKI2DW%K2AaXcf(M~5cvEFWV4+6IRUV#&b07upf&Anx5aTEw zK)|<69F)sOIrZcfgU;V5&SeF@E>U8|_dVX~iUs*warb_FYy@UR-KXCqPbV%i7dpPP q=J6v0mIdzs$i1b4JXWfYk(BzNMoB(~uN7nxKI|upS~Qh1XZ{6FCVtES diff --git a/custom_components/variable/__pycache__/__init__.cpython-38.pyc b/custom_components/variable/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d0bf3a58d4fb1aacf9291b77e0fdae4c6c41819b GIT binary patch literal 5642 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 literal 0 HcmV?d00001 diff --git a/custom_components/wundergroundpws/__pycache__/sensor.cpython-38.pyc b/custom_components/wundergroundpws/__pycache__/sensor.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b40e31faac5a924feb3c6ff0bdffce8ec5b09df9 GIT binary patch literal 19085 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 z zE6F*`U{xlk(5~tv=P=qE`Q(de?kpvTz0VlRkea!?LH^et6YuHULfHhZ>|aTq7ZVBD`omiJkowR=BAeiJbf+|XbW z3P1r0Irq<)cA{Eg6G^LkfNw7ukOwhVS2fd4*M;|=f9zcgaMt0^Y=55{!2C&)N#yrX ztqAF4H06>MTsPX7bhm>Yq~K7N86Rl`F$MyoP&eboLt6{xAh(vvj)&XWzLe1D7OMhahOXUC6#(HQ{2Efn$y7hCM5j zr!h3IFI-B$_ps6A>-i>V=84dIpudrng{mBVl9 zdPMCC=&FtzBvRudfiB#b!^rt#Xi4W?+n?^ItY1K+^@iHiP27IKO^~i`bhv%rN4Fqw zEeeZ>h~83+h#3_z(Qyk(vJpc(E;>2hfq0kb=6D?O9-y$|S%O#fN6;h}^v6pdiqrUq@w09qV1FTn{e^Jz4bLFW4%j+wy5Td#X*j==zfeut zj0g>MsANE)%gScV?x?|BupB44vOAZ%HoHZj7C5MIogUCmZl(vgc9A zIWUfm1OKnQL(U#`Gvq*;y()~5cQxPy*$0kQivB+NF(3?Y25=$oV&4sUZYQ#v3VN6- zT08{Zjuw9uH<)3Uk8W>kH=JisDYvkgF>hAD4&D81bIr6n=D6L`#AdeaOx>v@K9fq% zOipKJuT7<9V-i6M9j7{7a;;aPHy7HSlRaCQSzGgvtJ|!KQA86OiYfBrl#HS zYL=a@h#giWu8ba`KJ-<12+HrKXaMNC0r)1l0aMEDOS^?CpQ5EbiiiTP7gKR*gS(+F z^$>AD@V?MD^}qqmIkwZ{HaB#e<%PB=9oM|P--igu5;t~v1&1gWW>ejwqf>r|wyt># zzM;yRv!9s5a$tj&Pp@guC|8y1uWVcBn^GR~=l&Lqyyb333D~@j`tr9a*oI4x8+^3|nL|4u!Em+{b_>KwjPe9k(%6ye+CTi(TzTf{js$2vsAo3A0I;Fi958^(^YPUL;$%Z;ZjPNeSC z4W%-;P{=M?{oNj){V0avDI{AIV>16OiVab~S3|VP-B{kr!`G7Rb23dC8Vt_?t2&R9 zc{Gi8DZ@iEDc*??f0-KxsYd7(_@i4{ci>z-5=QJNa9mzg@F*Zx8t|F+M^&^ydks9TB>Ki}!`)D`b- z4HbL(Mli$AdZPGvb_b{(O`AgiIyRg#eO<=#NUs;8gv)sSiB!Zp^hpUy@vC-OPFUEJ9M%acnp&Z7Kx6gBkfQ>3JS{)-98$%%Zm}F{wE3mGWj{3QHtg}oe zF)G0(LTPYatI?Y#&~?P^$xNqaCg-MyQ^Rx9cs}Q;mhw*kUVfQ^FH!KP6#N+lUqOKD zVdjHjvcV89`~xa!A1k)Td^KMX8I&KPAu}O>A0E(-YVEqCw@{7RZJdwBOJe31@w3i@ z1>kL~mdByue8DDvKpGVcf-5_3%}x=mg_gUvU@&CuCbS;>(nu6wF(&N{@9L3Iq_CW?8%!o^2u`#T^UbjX2-9jG8a>` zS5v8UW^QU^Xcm?!7Ok4}&M{`ubRLEX-8dq2P)9l=dIS!rd{Pp}ne$xd|1EV85!i;b zf)Y(|AQZ}ri&%X51*GJ^r{EiuAG=X5R4aD1T(w}@C>PwLZ4W^QmiQTuHDIq*s5P-k zsZ+y4?e&`Pq}Zxv+`n7#fM->fBoVuP+iTIg9AZmsGxx0VL>RJz0vfmMq@areHXv?8 z!@NnfMX!-0ojt}bqw%yCxsR$m)Q&@a!2VA3pCL;+YmMIas#QB*ki41?w-?l5K+49| zO9mFBJXQl(5n0o-D9hb?`9F6WC?5sp;lZZ9f?wNnIKOJIPE8rAOu8QmX@i3z5 z<3khP%X3aeXQooA5wBnh>~3amIz=g&00e&2!CuhpX2_O6H%92=b^8cjpNH_Sf-X}c z(=h(h6Hh#m=O?djWRkZ9+3tqmV55zf+~{~ISEyR#M*kWCrwbCd10E3&NHPV;CvIrM zEG^ngd?mrME>l{|VcZ~2JSOs<>pe&6g!L)Vi5`mJVuKk7hdq4JaayPA4*Ks^CkFo&}(84!Fy1C0E~z85yF62j|aO>^xW5TuqOdQ9seOs`=5Gf$2gV$4?VI= z*;{(JQKD+Qr3i)xpDu6c|84k_LgV_jrc@24(H5xN)7$&>EaxpxEJU*O8ZSInKPW}6TqvS0;s^hZJ`^A+mP$Bj%sHpip P_TNKY%G str: + """Return a unique ID.""" + return self._unique_id + + +class WUndergroundData: + """Get data from WUnderground.""" + + def __init__(self, hass, api_key, pws_id, unit_system_api, lang, latitude, longitude): + """Initialize the data object.""" + self._hass = hass + self._api_key = api_key + self._pws_id = pws_id + self._unit_system_api = unit_system_api + self._lang = 'language={}'.format(lang) + self._latitude = latitude + self._longitude = longitude + self._features = set() + self.data = None + self._session = async_get_clientsession(self._hass) + + def request_feature(self, feature): + """Register feature to be fetched from WU API.""" + self._features.add(feature) + + def _build_url(self, baseurl): + if baseurl is _RESOURCECURRENT: + url = baseurl.format(self._pws_id, self._unit_system_api, self._api_key) + else: + url = baseurl.format(self._latitude, self._longitude, self._unit_system_api, self._lang, self._api_key) + + return url + + @Throttle(MIN_TIME_BETWEEN_UPDATES) + async def async_update(self): + """Get the latest data from WUnderground.""" + headers = {'Accept-Encoding': 'gzip'} + try: + with async_timeout.timeout(10, loop=self._hass.loop): + response = await self._session.get(self._build_url(_RESOURCECURRENT), headers=headers) + result_current = await response.json() + + # need to check specific new api errors + # if "error" in result['response']: + # raise ValueError(result['response']["error"]["description"]) + # _LOGGER.debug('result_current' + str(result_current)) + + if result_current is None: + raise ValueError('NO CURRENT RESULT') + with async_timeout.timeout(10, loop=self._hass.loop): + response = await self._session.get(self._build_url(_RESOURCEFORECAST), headers=headers) + result_forecast = await response.json() + + if result_forecast is None: + raise ValueError('NO FORECAST RESULT') + + result = {**result_current, **result_forecast} + + self.data = result + except ValueError as err: + _LOGGER.error("Check WUnderground API %s", err.args) + except (asyncio.TimeoutError, aiohttp.ClientError) as err: + _LOGGER.error("Error fetching WUnderground data: %s", repr(err)) diff --git a/custom_components/wundergroundpws/sensor.wundergroundpws.markdown b/custom_components/wundergroundpws/sensor.wundergroundpws.markdown new file mode 100644 index 0000000..9adcf36 --- /dev/null +++ b/custom_components/wundergroundpws/sensor.wundergroundpws.markdown @@ -0,0 +1,198 @@ +--- +layout: page +title: "Weather UndergroundPWS (WUndergroundPWS)" +description: "Instructions on how to integrate Weather Underground (WUnderground) Personal Weather Station within Home Assistant." +date: 2019-03-19 +sidebar: true +comments: false +sharing: true +footer: true +logo: wunderground.png +ha_category: Weather +ha_release: 0.89 +ha_iot_class: "Cloud Polling" +--- + +The `wundergroundpws` platform uses [Weather Underground](http://www.wunderground.com) as a source for current weather information. + +

+Free API keys are only issued to registered and active Weather Underground personal weather station users. + +To use this sensor, you need to enter your personal weather station API key and Station ID in configuration.yaml. + +To get a free API key: +1) You must have a personal weather station registered and uploading data to Weather Underground + + a) Join weather Underground + + b) Sign In + + c) My Profile -> My Weather Stations + + d) Add a New PWS +2) get API key at https://www.wunderground.com/member/api-keys + +Please consider this when using the following information. + +Current conditions are generated from the wundergroundpws configured pws_id. + +Forecast is generated from the HA configured latitude/longitude. +

+ +{% linkable_title Configuration %} + +To add Wunderground to your installation, add the following to your `configuration.yaml` file: + +```yaml +# Example configuration.yaml entry +sensor: + - platform: wundergroundpws + api_key: YOUR_API_KEY + pws_id: YOUR_STATION_ID + monitored_conditions: + - temp + - dewpt + - heatIndex +``` + +{% configuration %} +api_key: + description: The API key for Weather Underground. See above for details. + required: true + type: string +pws_id: + description: "You must enter a Personal Weather Station ID. The station id will be used to display current weather conditions." + required: true + type: string +lang: + description: Specify the language that the API returns. The current list of all Wunderground language codes is available [here](https://docs.google.com/document/d/13HTLgJDpsb39deFzk_YCQ5GoGoZCO_cRYzIxbwvgJLI/edit#). If not specified, it defaults to English (en-US). + required: false + type: string + default: en-US +latitude: + description: Latitude coordinate for weather forecast (required if **longitude** is specified). + required: false + type: string + default: Coordinates defined in your `configuration.yaml` +longitude: + description: Longitude coordinate for weather forecast (required if **latitude** is specified). + required: false + type: string + default: Coordinates defined in your `configuration.yaml` +monitored_conditions: + description: Conditions to display in the frontend. The following conditions can be monitored. + required: true + type: list + default: symbol + keys: + (generated from PWS) + stationID: + description: Your personal weather station (PWS) ID + solarRadiation: + description: Current levels of solar radiation + neighborhood: + description: WU PWS reference name + obsTimeLocal: + description: Text summary of local observation time + UV: + description: Current levels of UV radiation. See [here](https://www.wunderground.com/resources/health/uvindex.asp) for explanation. + winddir: + description: Wind degrees + humidity: + description: Relative humidity + dewpt: + description: Temperature below which water droplets begin to condense and dew can form + heatIndex: + description: Heat index (combined effects of the temperature and humidity of the air) + windChill: + description: Wind Chill (combined effects of the temperature and wind) + elev: + description: Elevation + precipTotal: + description: Today Total precipitation + precipRate: + description: Rain intensity + pressure: + description: Atmospheric air pressure + temp: + description: Current temperature + windGust: + description: Wind gusts speed + windSpeed: + description: Current wind speed + (generated from lat/lon forecast) + precip_1d: + description: "[[1d]](#1d): Forecasted precipitation intensity" + precip_chance_1d: + description: "[[1d]](#1d): Forecasted precipitation probability in %" + temp_high_1d: + description: "[[1d]](#1d): Forecasted high temperature" + temp_low_1d: + description: "[[1d]](#1d): Forecasted low temperature" + wind_1d: + description: "[[1d]](#1d): Forecasted wind speed" + weather_1d: + description: "[[12h]](#12h): A human-readable weather forecast of Day" + weather_1n: + description: "[[12h]](#12h): A human-readable weather forecast of Night" +{% endconfiguration %} + +All the conditions listed above will be updated every 5 minutes. + +## {% linkable_title Forecasts %} + +### {% linkable_title Daily forecasts %} + +Conditions above marked with
[1d] are daily forecasts. To get forecast for different day, replace the number +in `_1d_` part of the sensor name. Valid values are from `1` to `5`. + +Conditions above marked with [1n] are nightly forecasts. To get forecast for different night, replace the number +in `_1n_` part of the sensor name. Valid values are from `1` to `5`. + +## {% linkable_title Additional examples %} + +### {% linkable_title Daily forecast %} + +```yaml +sensor: + - platform: wunderground + api_key: YOUR_API_KEY + pws_id: YOUR_STATION_ID + monitored_conditions: + - weather_1d + - weather_1n + - weather_2d + - weather_2n + - weather_3d + - weather_3n + - weather_4d + - weather_4n + +group: + daily_forecast: + name: Daily Forecast + entities: + - sensor.pws_weather_1d + - sensor.pws_weather_1n + - sensor.pws_weather_2d + - sensor.pws_weather_2n + - sensor.pws_weather_3d + - sensor.pws_weather_3n + - sensor.pws_weather_4d + - sensor.pws_weather_4n +``` + + +### {% linkable_title Weather overview %} + +

+Note: While the platform is called “wundergroundpws” the sensors will show up in Home Assistant as “WUPWS” (eg: sensor.wupws_weather_1d). +

+ +Note that the Weather Underground sensor is added to the entity_registry, so second and subsequent Personal Weather Station ID (pws_id) will have their monitored conditions suffixed with an index number e.g. + +```yaml +- sensor.wupws_weather_1d_metric_2 +``` + +Additional details about the API are available [here](https://docs.google.com/document/d/1eKCnKXI9xnoMGRRzOL1xPCBihNV2rOet08qpE_gArAY/edit). \ No newline at end of file diff --git a/esphome/inside_temp_humd.yaml b/esphome/inside_temp_humd.yaml new file mode 100644 index 0000000..1d0e867 --- /dev/null +++ b/esphome/inside_temp_humd.yaml @@ -0,0 +1,33 @@ +esphome: + name: inside_temp_humd + platform: ESP8266 + board: d1_mini + +wifi: + ssid: "zoesplace" + password: "PalDgk19591960" + + # Enable fallback hotspot (captive portal) in case wifi connection fails + ap: + ssid: "Inside Temp Humd" + password: "CP8pP6Tlg3cE" + +i2c: + +sensor: + - platform: dht12 + temperature: + name: "Inside Temperature" + humidity: + name: "Inside Humidity" + update_interval: 60s + +captive_portal: + +# Enable logging +logger: + +# Enable Home Assistant API +api: + +ota: diff --git a/esphome/outside-temp-humd.yaml b/esphome/outside_temp_humd.yaml similarity index 75% rename from esphome/outside-temp-humd.yaml rename to esphome/outside_temp_humd.yaml index 52e935e..ee61449 100644 --- a/esphome/outside-temp-humd.yaml +++ b/esphome/outside_temp_humd.yaml @@ -9,8 +9,19 @@ wifi: # Enable fallback hotspot (captive portal) in case wifi connection fails ap: - ssid: "Outside-Temp-Humd" - password: "1kmZsOcaCm44" + ssid: "Outside Temp Humid" + password: "jhazXSLEWNFr" + + +i2c: + +sensor: + - platform: dht12 + temperature: + name: "Outside Temperature" + humidity: + name: "Outside Humidity" + update_interval: 60s captive_portal: @@ -21,17 +32,3 @@ logger: api: ota: - -i2c: - sda: D2 - scl: D1 - scan: True - id: bus_a - -sensor: - - platform: dht12 - temperature: - name: "Outside Temperature" - humidity: - name: "Outside Humidity" - update_interval: 60s diff --git a/hass.code-workspace b/hass.code-workspace new file mode 100644 index 0000000..bccea69 --- /dev/null +++ b/hass.code-workspace @@ -0,0 +1,12 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": { + "files.associations": { + "*.yaml": "home-assistant" + } + } +} \ No newline at end of file diff --git a/lovelace/closet.yaml b/lovelace/_manual/closet.yaml similarity index 100% rename from lovelace/closet.yaml rename to lovelace/_manual/closet.yaml diff --git a/lovelace/lights.yaml b/lovelace/_manual/lights.yaml similarity index 100% rename from lovelace/lights.yaml rename to lovelace/_manual/lights.yaml diff --git a/lovelace/_manual/temps.yaml b/lovelace/_manual/temps.yaml new file mode 100644 index 0000000..071b3d1 --- /dev/null +++ b/lovelace/_manual/temps.yaml @@ -0,0 +1,15 @@ +title: Temps - Humidity +icon: mdi:thermometer-lines +cards: + - type: entities + show_header_toggle: false + title: Outside + entities: + - sensor.outside_temperature + - sensor.outside_humidity + - type: entities + show_header_toggle: false + title: Inside + entities: + - sensor.inside_temperature + - sensor.inside_humidity diff --git a/lovelace/_manual/weather.yaml b/lovelace/_manual/weather.yaml new file mode 100644 index 0000000..4823160 --- /dev/null +++ b/lovelace/_manual/weather.yaml @@ -0,0 +1,45 @@ +title: Weather +cards: + - type: weather-forecast + name: NWS + entity: weather.praire_city_weather + - type: 'custom:lovelace-darksky-card' + name: Dark Sky + entity_current_conditions: sensor.dark_sky_icon + entity_temperature: sensor.dark_sky_temperature + entity_forecast_high_temp_1: sensor.dark_sky_daytime_high_temperature_1d + entity_forecast_high_temp_2: sensor.dark_sky_daytime_high_temperature_2d + entity_forecast_high_temp_3: sensor.dark_sky_daytime_high_temperature_3d + entity_forecast_high_temp_4: sensor.dark_sky_daytime_high_temperature_4d + entity_forecast_high_temp_5: sensor.dark_sky_daytime_high_temperature_5d + entity_forecast_icon_1: sensor.dark_sky_icon_1d + entity_forecast_icon_2: sensor.dark_sky_icon_2d + entity_forecast_icon_3: sensor.dark_sky_icon_3d + entity_forecast_icon_4: sensor.dark_sky_icon_4d + entity_forecast_icon_5: sensor.dark_sky_icon_5d + entity_forecast_low_temp_1: sensor.dark_sky_overnight_low_temperature_0d + entity_forecast_low_temp_2: sensor.dark_sky_overnight_low_temperature_1d + entity_forecast_low_temp_3: sensor.dark_sky_overnight_low_temperature_2d + entity_forecast_low_temp_4: sensor.dark_sky_overnight_low_temperature_3d + entity_forecast_low_temp_5: sensor.dark_sky_overnight_low_temperature_4d + entity_summary_1: sensor.dark_sky_summary_1d + entity_summary_2: sensor.dark_sky_summary_2d + entity_summary_3: sensor.dark_sky_summary_3d + entity_summary_4: sensor.dark_sky_summary_4d + entity_summary_5: sensor.dark_sky_summary_5d + entity_sun: sun.sun + entity_visibility: sensor.dark_sky_visibility + entity_daytime_high: sensor.dark_sky_daytime_high_temperature_0d + entity_wind_bearing: sensor.dark_sky_wind_bearing + entity_wind_speed: sensor.dark_sky_wind_speed + entity_humidity: sensor.dark_sky_humidity + entity_pressure: sensor.dark_sky_pressure + entity_apparent_temp: sensor.dark_sky_apparent_temperature + entity_daily_summary: sensor.dark_sky_daily_summary + entity_pop: sensor.dark_sky_precip_probability + entity_pop_intensity: sensor.dark_sky_precip_intensity + entity_pop_1: sensor.dark_sky_precip_probability_1d + entity_pop_2: sensor.dark_sky_precip_probability_2d + entity_pop_3: sensor.dark_sky_precip_probability_3d + entity_pop_4: sensor.dark_sky_precip_probability_4d + entity_pop_5: sensor.dark_sky_precip_probability_5d diff --git a/lovelace/dashboards.yaml b/lovelace/dashboards.yaml new file mode 100644 index 0000000..a614bba --- /dev/null +++ b/lovelace/dashboards.yaml @@ -0,0 +1,12 @@ +lovelace-panel: + mode: yaml + title: panel + icon: mdi:script + show_in_sidebar: true + filename: lovelace/panel.yaml +lovelace-manual: + mode: yaml + title: manual + icon: mdi:script + show_in_sidebar: true + filename: lovelace/manual.yaml diff --git a/lovelace/irrigation.yaml b/lovelace/irrigation.yaml deleted file mode 100644 index ef3a9eb..0000000 --- a/lovelace/irrigation.yaml +++ /dev/null @@ -1,43 +0,0 @@ -# --------------- IRRIGATION ------------- -title: Irrigation -icon: mdi:water -cards: - - type: vertical-stack - cards: - - type: markdown - content: > - # Irrigation - - type: vertical-stack - cards: - - type: vertical-stack - cards: - - type: markdown - content: > - ## Front Yard North - - type: entities - entities: - - entity: sensor.zone_1_last_run - icon: mdi:clock - - entity: sensor.zone_1_repeat - icon: mdi:timer - - entity: sensor.zone_1_next_run - icon: mdi:clock - - type: horizontal-stack - cards: - - type: entity-button - entity: switch.zone_1 - icon: mdi:water - name: on/off - tap_action: - action: toggle - - type: entity-button - entity: switch.zone_1_timer - icon: mdi:timer - name: timer - tap_action: - action: toggle - - type: entity-button - name: set - entity: group.zone_1_set - tap_action: - action: more-info diff --git a/lovelace/manual.yaml b/lovelace/manual.yaml new file mode 100644 index 0000000..90762a2 --- /dev/null +++ b/lovelace/manual.yaml @@ -0,0 +1,23 @@ +title: Manual Dashboard +# custom cards registration +resources: + - url: /haccsfiles/community/lovelace-darksky-card/lovelace-darksky-card.js + type: module +# - url: /hacsfiles/banner-card/banner-card.js +# type: module + - url: /hacsfiles/button-card/button-card.js + type: module +# - url: /hacsfiles/lovelace-auto-entities/auto-entities.js +# type: module +# - url: /hacsfiles/lovelace-fold-entity-row/fold-entity-row.js +# type: module +# - url: /hacsfiles/lovelace-card-mod/card-mod.js +# type: module +# - url: /hacsfiles/canvas-gauge-card/canvas-gauge-card.js +# type: module + +button_card_templates: + default: !include /config/templates/button-card-templates/default.yaml + large_value: !include /config/templates/button-card-templates/large_value.yaml + +views: !include_dir_list _manual diff --git a/lovelace/panel.yaml b/lovelace/panel.yaml new file mode 100644 index 0000000..5519283 --- /dev/null +++ b/lovelace/panel.yaml @@ -0,0 +1,13 @@ +title: Panel Dashboard +panel: true +# theme: slate +# custom cards registration +# resources: +# - url: /hacsfiles/banner-card/banner-card.js +# type: module + +# button_card_templates: +# default: !include /config/templates/button-card-templates/default.yaml +# large_value: !include /config/templates/button-card-templates/large_value.yaml + +# views: !include_dir_list _manual diff --git a/lovelace/temps.yaml b/lovelace/temps.yaml deleted file mode 100644 index 9828a06..0000000 --- a/lovelace/temps.yaml +++ /dev/null @@ -1,24 +0,0 @@ -title: Temps - Humidity -icon: mdi:thermometer-lines -cards: - - type: entities - show_header_toggle: false - title: Outside - entities: - - sensor.outside_temperature - - sensor.outside_humidity - - type: weather-forecast - entity: weather.praire_city_weather - # - type: custom:banner-card - # heading: - # - mdi:door - # - Closet - # background: "#B0C2ED" - # link: /lovelace/2 - # entities: - # - entity: sensor.closet_temperature - # name: Temperature - # - entity: input_number.fan_on_temp - # name: Set Temperature - # - entity: fan.fan_state - # name: Fan State diff --git a/packages/aa_system.yaml b/packages/aa_system.yaml index 6b91c36..0c7ab10 100644 --- a/packages/aa_system.yaml +++ b/packages/aa_system.yaml @@ -1,9 +1,9 @@ -homeassistant: - name: 238 McHaley +# homeassistant: + # name: 238 McHaley # latitude: !secret latitude_loc_home # longitude: !secret longitude_loc_home # elevation: !secret evelation_loc_home - time_zone: America/Los_Angeles + # time_zone: America/Los_Angeles # temperature_unit: F # unit_system: metric # customize_glob: !include config/customize_glob.yaml @@ -12,6 +12,7 @@ websocket_api: system_health: # Enables the frontend frontend: + themes: !include_dir_merge_named themes # Enables configuration UI config: # Show links to resources in log and frontend diff --git a/packages/darksky_weather.yaml b/packages/darksky_weather.yaml new file mode 100644 index 0000000..9753ee5 --- /dev/null +++ b/packages/darksky_weather.yaml @@ -0,0 +1,42 @@ +weather: + - platform: darksky + api_key: !secret darksky_api_key + name: Local Weather + mode: daily + +sensor: + - platform: darksky + api_key: !secret darksky_api_key + name: dark_sky + forecast: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + monitored_conditions: + - icon + - summary + - nearest_storm_distance + - nearest_storm_bearing + - humidity + - temperature + - temperature_high + - temperature_low + - apparent_temperature + - apparent_temperature_high + - apparent_temperature_low + - wind_speed + - wind_bearing + - precip_type + - precip_probability + - precip_accumulation + - precip_intensity + - precip_intensity_max + - uv_index + - daily_summary + - pressure + - visibility + scan_interval: + minutes: 10 diff --git a/packages/indoor_security_light.yaml b/packages/indoor_security_light.yaml index 356b5ca..dce4f4b 100644 --- a/packages/indoor_security_light.yaml +++ b/packages/indoor_security_light.yaml @@ -22,7 +22,7 @@ automation: trigger: platform: sun event: sunset - offset: "-00:30:00" + offset: "-00:45:00" action: - service: switch.turn_on data: diff --git a/packages/irrigation/common.yaml b/packages/irrigation/common.yaml new file mode 100644 index 0000000..613b43a --- /dev/null +++ b/packages/irrigation/common.yaml @@ -0,0 +1,39 @@ +homeassistant: + customize: + input_boolean.irrigation_pump: + friendly_name: "Well Pump" + icon: mdi:water-pump + input_boolean.irrigation_enabled: + friendly_name: "Irrigation System Enabled" + icon: mdi:water + variable.irrigation_next_trigger: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:alarm + variable.irrigation_next_trigger_dt: + friendly_name: "Next Scheduled Run at" + icon: mdi:alarm + variable.irrigation_next_schedule_name: + friendly_name: "Next Schedule to Run" + icon: mdi:calendar + variable.irrigation_running_names: + friendly_name: "Zones Running Now" + icon: mdi:view-list + variable.irrigation_queue_names: + friendly_name: "Zones Queued to Run" + icon: mdi:view-list + +input_boolean: + irrigation_pump: + irrigation_enabled: + +variable: + irrigation_next_trigger: + value: '00:00:00' + irrigation_next_trigger_dt: + value: '' + irrigation_next_schedule_name: + value: '' + irrigation_running_names: + value: '' + irrigation_queue_names: + value: '' diff --git a/packages/irrigation/zone1.yaml b/packages/irrigation/zone1.yaml new file mode 100644 index 0000000..97951ca --- /dev/null +++ b/packages/irrigation/zone1.yaml @@ -0,0 +1,57 @@ +homeassistant: + customize: + variable.irrigation_zone_1_schedule_countdown: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:timer-sand + variable.irrigation_zone_1_schedule_next_dt: + friendly_name: "Time of Next Scheduled Run" + icon: mdi:clock + input_boolean.irrigation_zone_1_schedule_enabled: + friendly_name: "Schedule (enabled/disabled)" + input_boolean.irrigation_zone_1_state: + friendly_name: "Zone State (manual override)" + +variable: + irrigation_zone_1_schedule_countdown: + value: 'disabled' + attributes: + icon: mdi:clock + irrigation_zone_1_schedule_next_dt: + value: 'disabled' + +input_boolean: + irrigation_zone_1_schedule_enabled: + irrigation_zone_1_state: + +input_number: + irrigation_zone_1_duration: + name: "Duration in minutes" + min: 1 + max: 30 + step: 1 + icon: mdi:timer-sand + irrigation_zone_1_schedule_base_hour: + name: "Hour of Day" + # unit_of_measurement: hours + initial: 6 + min: 0 + max: 24 + step: 1 + icon: mdi:clock + irrigation_zone_1_schedule_base_minute: + name: "Minute of Day" + # unit_of_measurement: minutes + initial: 0 + min: 0 + max: 45 + step: 15 + icon: mdi:clock + +# use sensor.irrigation_zone_1_schedule_repeatin to get corresponding value for option +input_select: + irrigation_zone_1_schedule_delta: + name: Repeat Every + icon: mdi:repeat + # options are set from the websocket client + options: + - 12 Hours diff --git a/packages/irrigation/zone2.yaml b/packages/irrigation/zone2.yaml new file mode 100644 index 0000000..12afb07 --- /dev/null +++ b/packages/irrigation/zone2.yaml @@ -0,0 +1,57 @@ +homeassistant: + customize: + variable.irrigation_zone_2_schedule_countdown: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:timer-sand + variable.irrigation_zone_2_schedule_next_dt: + friendly_name: "Time of Next Scheduled Run" + icon: mdi:clock + input_boolean.irrigation_zone_2_schedule_enabled: + friendly_name: "Schedule (enabled/disabled)" + input_boolean.irrigation_zone_2_state: + friendly_name: "Zone State (manual override)" + +variable: + irrigation_zone_2_schedule_countdown: + value: 'disabled' + attributes: + icon: mdi:clock + irrigation_zone_2_schedule_next_dt: + value: 'disabled' + +input_boolean: + irrigation_zone_2_schedule_enabled: + irrigation_zone_2_state: + +input_number: + irrigation_zone_2_duration: + name: "Duration in minutes" + min: 1 + max: 30 + step: 1 + icon: mdi:timer-sand + irrigation_zone_2_schedule_base_hour: + name: "Hour of Day" + # unit_of_measurement: hours + initial: 6 + min: 0 + max: 24 + step: 1 + icon: mdi:clock + irrigation_zone_2_schedule_base_minute: + name: "Minute of Day" + # unit_of_measurement: minutes + initial: 0 + min: 0 + max: 45 + step: 15 + icon: mdi:clock + +# use sensor.irrigation_zone_2_schedule_repeatin to get corresponding value for option +input_select: + irrigation_zone_2_schedule_delta: + name: Repeat Every + icon: mdi:repeat + # options are set from the websocket client + options: + - 12 Hours diff --git a/packages/irrigation/zone3.yaml b/packages/irrigation/zone3.yaml new file mode 100644 index 0000000..7ad28d3 --- /dev/null +++ b/packages/irrigation/zone3.yaml @@ -0,0 +1,57 @@ +homeassistant: + customize: + variable.irrigation_zone_3_schedule_countdown: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:timer-sand + variable.irrigation_zone_3_schedule_next_dt: + friendly_name: "Time of Next Scheduled Run" + icon: mdi:clock + input_boolean.irrigation_zone_3_schedule_enabled: + friendly_name: "Schedule (enabled/disabled)" + input_boolean.irrigation_zone_3_state: + friendly_name: "Zone State (manual override)" + +variable: + irrigation_zone_3_schedule_countdown: + value: 'disabled' + attributes: + icon: mdi:clock + irrigation_zone_3_schedule_next_dt: + value: 'disabled' + +input_boolean: + irrigation_zone_3_schedule_enabled: + irrigation_zone_3_state: + +input_number: + irrigation_zone_3_duration: + name: "Duration in minutes" + min: 1 + max: 30 + step: 1 + icon: mdi:timer-sand + irrigation_zone_3_schedule_base_hour: + name: "Hour of Day" + # unit_of_measurement: hours + initial: 6 + min: 0 + max: 24 + step: 1 + icon: mdi:clock + irrigation_zone_3_schedule_base_minute: + name: "Minute of Day" + # unit_of_measurement: minutes + initial: 0 + min: 0 + max: 59 + step: 1 + icon: mdi:clock + +# use sensor.irrigation_zone_3_schedule_repeatin to get corresponding value for option +input_select: + irrigation_zone_3_schedule_delta: + name: Repeat Every + icon: mdi:repeat + # options are set from the websocket client + options: + - 12 Hours diff --git a/packages/irrigation/zone4.yaml b/packages/irrigation/zone4.yaml new file mode 100644 index 0000000..8010f10 --- /dev/null +++ b/packages/irrigation/zone4.yaml @@ -0,0 +1,57 @@ +homeassistant: + customize: + variable.irrigation_zone_4_schedule_countdown: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:timer-sand + variable.irrigation_zone_4_schedule_next_dt: + friendly_name: "Time of Next Scheduled Run" + icon: mdi:clock + input_boolean.irrigation_zone_4_schedule_enabled: + friendly_name: "Schedule (enabled/disabled)" + input_boolean.irrigation_zone_4_state: + friendly_name: "Zone State (manual override)" + +variable: + irrigation_zone_4_schedule_countdown: + value: 'disabled' + attributes: + icon: mdi:clock + irrigation_zone_4_schedule_next_dt: + value: 'disabled' + +input_boolean: + irrigation_zone_4_schedule_enabled: + irrigation_zone_4_state: + +input_number: + irrigation_zone_4_duration: + name: "Duration in minutes" + min: 1 + max: 30 + step: 1 + icon: mdi:timer-sand + irrigation_zone_4_schedule_base_hour: + name: "Hour of Day" + # unit_of_measurement: hours + initial: 6 + min: 0 + max: 24 + step: 1 + icon: mdi:clock + irrigation_zone_4_schedule_base_minute: + name: "Minute of Day" + # unit_of_measurement: minutes + initial: 0 + min: 0 + max: 59 + step: 1 + icon: mdi:clock + +# use sensor.irrigation_zone_4_schedule_repeatin to get corresponding value for option +input_select: + irrigation_zone_4_schedule_delta: + name: Repeat Every + icon: mdi:repeat + # options are set from the websocket client + options: + - 12 Hours diff --git a/packages/irrigation/zone5.yaml b/packages/irrigation/zone5.yaml new file mode 100644 index 0000000..47f32ca --- /dev/null +++ b/packages/irrigation/zone5.yaml @@ -0,0 +1,57 @@ +homeassistant: + customize: + variable.irrigation_zone_5_schedule_countdown: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:timer-sand + variable.irrigation_zone_5_schedule_next_dt: + friendly_name: "Time of Next Scheduled Run" + icon: mdi:clock + input_boolean.irrigation_zone_5_schedule_enabled: + friendly_name: "Schedule (enabled/disabled)" + input_boolean.irrigation_zone_5_state: + friendly_name: "Zone State (manual override)" + +variable: + irrigation_zone_5_schedule_countdown: + value: 'disabled' + attributes: + icon: mdi:clock + irrigation_zone_5_schedule_next_dt: + value: 'disabled' + +input_boolean: + irrigation_zone_5_schedule_enabled: + irrigation_zone_5_state: + +input_number: + irrigation_zone_5_duration: + name: "Duration in minutes" + min: 1 + max: 30 + step: 1 + icon: mdi:timer-sand + irrigation_zone_5_schedule_base_hour: + name: "Hour of Day" + # unit_of_measurement: hours + initial: 6 + min: 0 + max: 24 + step: 1 + icon: mdi:clock + irrigation_zone_5_schedule_base_minute: + name: "Minute of Day" + # unit_of_measurement: minutes + initial: 0 + min: 0 + max: 45 + step: 15 + icon: mdi:clock + +# use sensor.irrigation_zone_5_schedule_repeatin to get corresponding value for option +input_select: + irrigation_zone_5_schedule_delta: + name: Repeat Every + icon: mdi:repeat + # options are set from the websocket client + options: + - 12 Hours diff --git a/packages/irrigation/zone6.yaml b/packages/irrigation/zone6.yaml new file mode 100644 index 0000000..6741279 --- /dev/null +++ b/packages/irrigation/zone6.yaml @@ -0,0 +1,57 @@ +homeassistant: + customize: + variable.irrigation_zone_6_schedule_countdown: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:timer-sand + variable.irrigation_zone_6_schedule_next_dt: + friendly_name: "Time of Next Scheduled Run" + icon: mdi:clock + input_boolean.irrigation_zone_6_schedule_enabled: + friendly_name: "Schedule (enabled/disabled)" + input_boolean.irrigation_zone_6_state: + friendly_name: "Zone State (manual override)" + +variable: + irrigation_zone_6_schedule_countdown: + value: 'disabled' + attributes: + icon: mdi:clock + irrigation_zone_6_schedule_next_dt: + value: 'disabled' + +input_boolean: + irrigation_zone_6_schedule_enabled: + irrigation_zone_6_state: + +input_number: + irrigation_zone_6_duration: + name: "Duration in minutes" + min: 1 + max: 30 + step: 1 + icon: mdi:timer-sand + irrigation_zone_6_schedule_base_hour: + name: "Hour of Day" + # unit_of_measurement: hours + initial: 6 + min: 0 + max: 24 + step: 1 + icon: mdi:clock + irrigation_zone_6_schedule_base_minute: + name: "Minute of Day" + # unit_of_measurement: minutes + initial: 0 + min: 0 + max: 45 + step: 15 + icon: mdi:clock + +# use sensor.irrigation_zone_6_schedule_repeatin to get corresponding value for option +input_select: + irrigation_zone_6_schedule_delta: + name: Repeat Every + icon: mdi:repeat + # options are set from the websocket client + options: + - 12 Hours diff --git a/packages/irrigation/zone7.yaml b/packages/irrigation/zone7.yaml new file mode 100644 index 0000000..e0b90e9 --- /dev/null +++ b/packages/irrigation/zone7.yaml @@ -0,0 +1,57 @@ +homeassistant: + customize: + variable.irrigation_zone_7_schedule_countdown: + friendly_name: "Countdown to Next Scheduled Run" + icon: mdi:timer-sand + variable.irrigation_zone_7_schedule_next_dt: + friendly_name: "Time of Next Scheduled Run" + icon: mdi:clock + input_boolean.irrigation_zone_7_schedule_enabled: + friendly_name: "Schedule (enabled/disabled)" + input_boolean.irrigation_zone_7_state: + friendly_name: "Zone State (manual override)" + +variable: + irrigation_zone_7_schedule_countdown: + value: 'disabled' + attributes: + icon: mdi:clock + irrigation_zone_7_schedule_next_dt: + value: 'disabled' + +input_boolean: + irrigation_zone_7_schedule_enabled: + irrigation_zone_7_state: + +input_number: + irrigation_zone_7_duration: + name: "Duration in minutes" + min: 1 + max: 30 + step: 1 + icon: mdi:timer-sand + irrigation_zone_7_schedule_base_hour: + name: "Hour of Day" + # unit_of_measurement: hours + initial: 6 + min: 0 + max: 24 + step: 1 + icon: mdi:clock + irrigation_zone_7_schedule_base_minute: + name: "Minute of Day" + # unit_of_measurement: minutes + initial: 0 + min: 0 + max: 45 + step: 15 + icon: mdi:clock + +# use sensor.irrigation_zone_7_schedule_repeatin to get corresponding value for option +input_select: + irrigation_zone_7_schedule_delta: + name: Repeat Every + icon: mdi:repeat + # options are set from the websocket client + options: + - 12 Hours diff --git a/packages/outside.yaml b/packages/outside.yaml new file mode 100644 index 0000000..b8aa7c3 --- /dev/null +++ b/packages/outside.yaml @@ -0,0 +1,57 @@ +# {"Time":"2020-12-08T02:09:02","BME280":{"Temperature":21.5,"Humidity":39.1,"DewPoint":6.9,"Pressure":906.5},"PressureUnit":"hPa","TempUnit":"C"} + +homeassistant: + customize: + sensor.bme_outside_temperature: + friendly_name: 'Outside Temperature' + + +sensor: + - platform: mqtt + name: "bme_outside_temperature" + state_topic: "tele/outside/SENSOR" + qos: 0 + unit_of_measurement: "F" + value_template: "{{ value_json.BME280.Temperature * 9 / 5 + 32 }}" + - platform: mqtt + name: "bme_outside_dew_point_temperature" + state_topic: "tele/outside/SENSOR" + qos: 0 + unit_of_measurement: "F" + value_template: "{{ value_json.BME280.DewPoint * 9 / 5 + 32 }}" + - platform: mqtt + name: "bme_outside_humidity" + state_topic: "tele/outside/SENSOR" + qos: 0 + unit_of_measurement: "%" + value_template: "{{ value_json.BME280.Humidity }}" + - platform: mqtt + name: "bme_outside_barometric_pressure" + state_topic: "tele/outside/SENSOR" + qos: 0 + unit_of_measurement: "inHg" + value_template: "{{ value_json.BME280.Pressure / 33.86 }}" + +switch: + - platform: mqtt + name: "Outside North Circuit 1" + state_topic: "stat/outside/POWER1" + command_topic: "cmnd/outside/POWER1" + availability_topic: "tele/outside/LWT" + qos: 1 + payload_on: "ON" + payload_off: "OFF" + payload_available: "Online" + payload_not_available: "Offline" + retain: false + - platform: mqtt + name: "Outside North Circuit 2" + state_topic: "stat/outside/POWER2" + command_topic: "cmnd/outside/POWER2" + availability_topic: "tele/outside/LWT" + qos: 1 + payload_on: "ON" + payload_off: "OFF" + payload_available: "Online" + payload_not_available: "Offline" + retain: false diff --git a/packages/weather.yaml b/packages/weather.yaml deleted file mode 100644 index ee28807..0000000 --- a/packages/weather.yaml +++ /dev/null @@ -1,34 +0,0 @@ -weather: - - platform: darksky - api_key: 21b1870b9e52cf1ce234798ddfd0fbce - name: Praire City Weather - mode: daily - - -sensor: - - platform: darksky - api_key: 21b1870b9e52cf1ce234798ddfd0fbce - name: forecast - forecast: - - 0 - - 2 - - 3 - hourly_forecast: - - 0 - - 1 - - 2 - - 3 - monitored_conditions: - - summary - - icon - - temperature - - dew_point - - wind_speed - - wind_bearing - - cloud_cover - - temperature_high - - temperature_low - - moon_phase - - sunset_time - - alerts - scan_interval: 00:10 diff --git a/python_scripts/state_change.py b/python_scripts/state_change.py new file mode 100644 index 0000000..378e284 --- /dev/null +++ b/python_scripts/state_change.py @@ -0,0 +1,31 @@ +#================================================================================================== +# python_scripts/set_statev2.py +#================================================================================================== + +#-------------------------------------------------------------------------------------------------- +# Set the state or other attributes for the entity specified in the Automation Action +#-------------------------------------------------------------------------------------------------- + +# service: +# python_script.set_statev2 +# {"entity_id": "light.keukeneiland","state":"on","icon":"mdi:door","friendly_name":"test"} + +inputEntity = data.get('entity_id') +if inputEntity is None: + logger.warning("===== entity_id is required if you want to set something.") +else: + inputStateObject = hass.states.get(inputEntity) + inputState = inputStateObject.state + inputAttributesObject = inputStateObject.attributes.copy() + + for item in data: + newAttribute = data.get(item) + logger.debug("===== item = {0}; value = {1}".format(item,newAttribute)) + if item == 'entity_id': + continue # already handled + elif item == 'state': + inputState = newAttribute + else: + inputAttributesObject[item] = newAttribute + + hass.states.set(inputEntity, inputState, inputAttributesObject) diff --git a/python_scripts/states_change.py b/python_scripts/states_change.py new file mode 100644 index 0000000..2f7875b --- /dev/null +++ b/python_scripts/states_change.py @@ -0,0 +1,49 @@ +# EXEMPLE 1 +# - service: python_script.state_change +# data_template: +# entity_id: person.one, person.two +# state: 'not_home' + +# EXEMPLE 2 +# - service: python_script.state_change +# data_template: +# entity_id: +# - person.one +# - person.two +# state: 'not_home' + +# EXEMPLE 3 +# - service: python_script.state_change +# data_template: +# entity_id: > +# {% set entities = state_attr('group.family_persons', 'entity_id') %} +# {{ states.person | selectattr('entity_id', 'in', entities) +# | selectattr('state', 'eq', 'home') +# | map(attribute='entity_id') +# | join(', ') }} +# state: 'not_home' + +if isinstance(data.get('entity_id'), str): + EntityList = data.get('entity_id').split(', ') +else: + EntityList = data.get('entity_id') +for item in EntityList: + inputEntity = item + if inputEntity is None: + logger.warning("===== entity_id is required if you want to set something.") + else: + inputStateObject = hass.states.get(inputEntity) + inputState = inputStateObject.state + inputAttributesObject = inputStateObject.attributes.copy() + + for item in data: + newAttribute = data.get(item) + logger.debug("===== item = {0}; value = {1}".format(item,newAttribute)) + if item == 'entity_id': + continue # already handled + elif item == 'state': + inputState = newAttribute + logger.info(inputEntity + ': ' 'state' + '> ' + inputState) + else: + inputAttributesObject[item] = newAttribute + hass.states.set(inputEntity, inputState, inputAttributesObject) diff --git a/templates/button-card-templates/default.yaml b/templates/button-card-templates/default.yaml new file mode 100644 index 0000000..6bfca80 --- /dev/null +++ b/templates/button-card-templates/default.yaml @@ -0,0 +1,38 @@ +tap_action: + action: toggle +hold_action: + action: more-info +show_state: true +size: 30% +state: + - styles: + card: + - filter: opacity(50%) + icon: + - filter: grayscale(100%) + value: 'off' + - styles: + card: + - filter: opacity(25%) + icon: + - filter: grayscale(100%) + value: unavailable +styles: + card: + - width: 150px + - height: 150px + grid: + - grid-template-areas: '"i" "n" "s"' + - grid-template-columns: 1fr + - grid-template-rows: 1fr min-content min-content + img_cell: + - justify-content: start + - margin-left: 20px + - margin-bottom: 30px + name: + - justify-self: start + - margin-left: 10px + state: + - justify-self: start + - margin-left: 10px + - font-weight: lighter diff --git a/templates/button-card-templates/large_value.yaml b/templates/button-card-templates/large_value.yaml new file mode 100644 index 0000000..b5449e5 --- /dev/null +++ b/templates/button-card-templates/large_value.yaml @@ -0,0 +1,30 @@ +show_label: true +show_state: true +size: 100% +styles: + card: + - width: 140px + - height: 140px + - margin: 5px + grid: + - grid-template-areas: '"s s" "i n" "i l"' + - grid-template-columns: 20% 80% + - grid-template-rows: 60% 20% 10% + img_cell: + label: + # - margin-left: 10px + # - font-weight: lighter + - justify-self: start + - font-size: 1em + name: + - justify-self: start + - overflow: visible + - font-size: 1.7em + - font-weight: bold + state: + # - margin-right: 30px + - justify-self: center + - align-self: center + - font-size: 2.2em + - font-weight: bold + - overflow: visible diff --git a/themes/clear/clear.yaml b/themes/clear/clear.yaml new file mode 100644 index 0000000..ca8d4e8 --- /dev/null +++ b/themes/clear/clear.yaml @@ -0,0 +1,60 @@ +clear: + # Background image + lovelace-background: 'center / cover no-repeat url(data:image/jpeg;base64,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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA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hERERERERNuxEREREREREREREREREREREREREREREREREREREREREREREREREREREREREb4t30REREREREQiIQhCDRocEREREREREREREREREQ0IiIiIiIiIiIiIiIiIiIiIiIiIiIiIJFtvTES/hd4fV02LW3a8bNvvy3fW2aTGptqeESokW29MRLh4NeC3h9XQmyhCEIQhCEIQhCDjxrW2hvi3fRCdpCEIQhCEHY421PBgAAKSEIQhCEIQhCHoCTYwhCEIQhCEIIhCEIQhCEIQhCG9Pd9kIQhCEIQhCE20GIQhCEIQhCEIQhCEIQhCEIQhBiEIQhCEIQhCdjfOH0Qm0dDghCEIQhCEIQgzCEIQhCEIQhCE7CjbeiNBCEIQhCE2yd2zdT04Ob/we43TxrXjZtnEtdxEQhCEIQhCEIQVepCEIQhCEIQhCDlIQhCEIQhCEIQREIQhCEIQhCEIRNNnGCMEYIwRgjBGCMEYIwRgaQiIhRkREREREREREREREcuPz0IkJ8w44EREREREREREREREOcOxQiIiIiIiIiIiIiIiLtiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIhoREREREREREREREQ4tX2ERERERERERERERERERC8yIiIiIiIiIiIiIiIiIiHCMERERERERERERERERERERCjIiIiIiIiIiIiIiIiIhaCoiIiIiIiIiIiI36+aPlH2tk3Bu9xO7L8T/wbu4Gpsm+4n/GNbJuavbiHnt2/Bp3bJwTuyrUc1/R97AA1DeQhCEFGQhCEIQ/Mf8ABIQhCdiahCEIQnYyEIQmyhCEITxwAAAJsADi1Y9SEIQhNgAehCEIQhCEIQhCDwQhCEJtwABbYAAlBRkIQhCE7d+jmv8AHmu89B19/UTve/EXpsPUDU395/xj2zcGe2NPM12zfh09sm0LXuzu79HNf49Nune5v76iTZBprTbLu67KeNnceg3ds3PM12zePEaid2ybQndlvF819g3Bu7RO9m/vq2lBqeNa8e3NuG5t2/Fp3bJwTuy1KVI83n+w62wABT68N6yTa0HtptoZEIQhCEIQhCEITbAAIPUhCEIQhCEIQhwJtgADXjf/AP8A/wCSIQhCEIQhCEILQhCE7rO/yXFsI/z+JhHAe2TbVaNCEwm9f2uW1bGrtkoNbWnl2Q8vG31W/wC9u1Nq3t0oNbVK7fiPYM7/ACXFsI/z+JhdvAe2TbVaNCEwm9f2uWzb7WrtEp2tbOnl3IeXjb6rf97dqbNvbpTta2aV2/EewZX+S4thF/8A0JhEIQgsbZNtVo0IXnxX7hsW+61SEIQhCEIJTusQhCEIQgrIQhCEPLxtdVvIQhCEIQnfQhCEIQg9tBIiEIQeOxVEEIQnYhCEIQhDishCEITsZX+S4thH+HBMLvp7ZNtVvELz4r9w77fjUrsIeXjeIvDN7dKd+mq399K7fiPYPr/JcWwj/DgmFsUQhCEIQhCEIQVap6keHxX+ciEIQZCEIQhCE7sIQhCEIQg13VWxhHyIQhCEIQhCEIQhCEIQhCEIQhDiohCEIQhCEIQhBqd1kIQhCEIQhCEKFGxoQhCComxhCEIcR7Hh89BCEIQhCEIQwZCEIQhCEIQhBVOp6kfPiu1vZtdsJs2u1Vs4OPLxvFW3YhBkIQhCEIQhCEKFBCEIQhCEIMwgmfYhCEIQhCEJ2oQhCEIQhNrwHtk2nVvFLz4r+/Ib2rW3aErtmp5eN4i8A3t0ptWhK7fiPw6rbzyYx1Gp41PO3ceXjG4a7Z426q8altGM0Rretmlt4ZOCx17Wp41PbtTy8U3Nu34Hcb/4dCVaJueeTGM0Rretilt4ZOCx17rU8ant2p5eIbm3b8HuN/8ADpbpO5/0+Q6NNRoiIiIiIiIm3lq1r9de/PDOd9bdqeGcXZEREREREREREREREREOERERERERERERERERERERERERERELQ0IiIiIiIiIiIiIiIiIiIiIiIiIiIiIsERERERERERERERERERF2oXC7nnkxps01GvCT1e/gsdfGt7FPbtTwjc27fh05/EIXC7nnkxps01GvBT1e/gsdfGt7NPbtTwTc27fiU5/EIXC7nnkxps09GvAT1e/gsdfGt7VPbtTwDc27fi05/Ebj645kREREREREREREREREREcR6ERERERERERERERERERF4CEREREREREREREREREQiEIQhCEIQhCDngIRERERERERERERERERDREREREREREREREREREIOSIiIiIiIiIiIiIiIiIS/hkJFv8AxzY2269W/AcR6P8AmW749raJEPPsh5fyU8XRzY63W62QhCEIQhCEITshOzgv0f8AETbvUhCEIQhCEIQhCEJ2JtmIQhCEIQnYUdsHPkQhCEIQhO0hCEIQhCEIQhCEIQhCEIQhCEIQhCE7dxXRzHrq7SEIQmyhCEIcJ++xevjUtu/CNUhCEIQhCEIQhB7BBQQhCEHHlsITwi8DuC6DV6vYRkZGRkZGRkZGRkZGRkY73PvukZGRkZGRkZGRkZGRkZGRkZGRkZGRkffS278NNu8CMjIyMjIxRsGyIyMjIyMohGRkZGRkZGRkZGRkZGRkZGRkZGRkZqRkZGRkZGRkZGRkZGRkZwHQb/EcB+Na7qW3b/jEpsmp/F7yLCIsIiwiLCIsIiwiLCIsIiwiLCIsEREREREREREREREREREJ+NhOyfxMRORERERERERERERERERERYRFhEWERYRERERERERERERERYIwiLCIsIiwRERERERERERERERERERERERERERERERERGhEREREREREREREREXjE/8A44kNbXeJQa/jGv8A0cZGRkZGRkZGRkZGJdrRGRkZGRkZGRkZGRiU7WvHwhCEIQhCdxrZwjIyMmzhGR9iMjIyMjIyMjIyMjJt4yMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyM1/iGv4xrxCXda/wDRtfxjXhku+1/+Gh//2gAMAwEAAgADAAAAEG24ekkkkkkkkkkkkkAfttttttttttttt5JJJJJJ5PJJJJJJJJJJJJJUkkkkkkkWkkkkkkkkkkkkk5JJJJJIA23bbG4SSSSSSSSSSST4kl//AP8A/wD/AP8A/wD/AP8A4JJJJJJKTcrbbbbbbbbbbbZAAAAAAAADpJJJJJJJJJJJJJJJJJJJW22AK/8A6Ekkkkkkkkkkjbb5bbbbbbbbbbb8kkkkklWABxbbbbbbbbbbaD//AP8A/wD/AP8A/wCtbbbbbbbbbbatttttttpJJIbSSSVpJJJJJJJJJG222dJJJJJJJJJI7/8A/wD/AP8A47bbNySSSSSSSSSGkkkkkkkkkk5ySSSSSSSSSBJJJJJJIgAADtJJJJSSSSSSSSSJJJJJfkkkkkkkkk8AAAAAAb//AP8A/jttttttttsc22222222222Vttttttttsf8A/wD/AP8A/wDn/wD/AP8AbbbbbJJJJJJJJP8Abbbbbs22222222G222222Kkkkkl1bbbbbbbbHJJJJJJJJJJJJBJJJJJJJJtttttttr//AP8A/ttttttTbbbbbbdrbbbbbewAAAAAACbbbbbbY0kkkkkhDbbbbbbdSSSSSSSSSSSSSVFttttttuJJJJJJJrbbbbdtttttr/8A/wD/AP8A+K//AP8A/wD/AOWSSSSSSRW222225bbbbbbY9JJJJJJJ622222222222230kkkkkkmdttttt87bbbbYkkkkkn/8A/wD/AP8A5xH22222+kkkkkkkk+AAAAGlrJJJJJnJJJJJJJJ4bbbbbbbbbbbbc1JJJJJJJm//AP8A/wD0m/8A/wD/ALLbbbfkkkkkkhoAZAAAAPSSSSSSSSST/wD/APtkmj//AP8A9P8A/wD/AP8A/wD/APxP/wD/AP8A/wD/AP8A/wD/AFts+2222228kkkkH/72222H9tttttttttqiSSZAAAtbbbbbbbbbbnttru22mJJJqJJJJJJJJJJ2/wD/AP8A/wD/AP8A/wD9tttbJJJJJJC222+kkkySSb+i222222222q222wpJI7bbbbbbbbbbWbav/wD/AP8A2262222222222275JJJJJJJJF2222UAAAAAAH9t22223dtlkkESSSSSSSSRttttvf+NtttttttttttiANJJJJBEr/wD/AP8A/wD/AP8A/wD/AP8AyCSSSSSSSSAAAAAWSSSSSSc+y22222dv3JJl7bbbbbaDJJJJJOgSSSSSSSSSSSSSYi222226122222222222223P/wD/AP8A/wD/APtttttv7bbbbbb0QAAAAAGMA/8A/oAAAAABi9tttttvJJJJJJJJJJJJJJKUkkkkksUkkkkkkakkkkkl1/8A/wD/AP8A81tttttt77bbbbbaEkkkkkkwkgAAeJJJJJY4AAAAAAA1JJJJJJJJJJJJJ+m22222va22222rD22222gqf/8A/wD/AMAySSSSSHsgAAAAAIttttttttvbbbS2222k7Yf/AP8A/wD/AP8AG22222222222ZttttttlCSEkkklySgkkkl1bX/8A/wD/AA/+G2223Ml7ttttmkkkkkkkkkkAAAAi2232AAKZJJJJJJP/AP8A/wD/AP8A/wD/AP8A1ttR/wD/APPbaRJJJObbApJJTgACkkknBttX/wD/APjJIbJJIHJJJJJJJJJJNttttU23w2222JJJJJJJbbbbbbbbbb1bbbfpJCJJJKCSS7JJJmSTfbbbRbbUAAAN/wD/AJ6SSWn/AP6kkkkkkkkkkl//AP8A/wC+lIkkkku222222+W222222233/wD/AP8A6gCrbbbTtsTbbbbBsk22226SUW2223iTs2223C2+22226wAAAAAP/wD/AP8A+tBtttttt/8A/wD/AP8A/wDJJJJJJJJX/wD/AP8A/wAw9ttttld1ttttsE9//wD/AP8A9cpJJJJJcV//AP8A/wCap/8A/wD/AP8A/wD/AP8A/wD9tttttppJJJJJJrbbbbbb4JJJJJJJ/wD/AP8A/wD/APvbbbbbaObbbbbbPySSSSSSy222222ONttttt/NtttttvNttttu22222npJJJJJJIEkkkkk1/8A/wD/AP8A7VtttttttKSSSSSSSSSSSSSvckkkkkUAAAAAACN222223pW22222E622220AAAAA3/8A/wD/AP8A/wD8dttttlJJJJJJaAAAAAAAAAW22222222222jN5ySSSR8Af/8A/wD+xACSSSSXIASSSSZvyySSSaySSSakkkkkkkkkqSSSS0kkkkkkpJJJJJJJJJAkkkkkkkkkkkwSTuAAANLbeSSSTgAB9tttsABTtttmLbX2223udtsbbbbbbbbbbbTJJIw222223XJJJJJJJJJJASSSSSSSSSTUAAB1ttlJJJMkklIAAJ9ttuAAAdttlv8A/wDckkitlJOIAAAAAAAAAAAMJKJJJJJJJBJJJJJJJJJJJxJJJJJJJJNEkkkhn/bSSSSrbb7bbbc22jbbbe226/8A/wD9W2z22LlAAAAAAAAAAAACfFJJJJJJJSSSSSSSSSSSST7bbbbbbbRpJJJJevltttttftNtttthstttttzP2SSSSSNrS21aAAAAAAAAAAAAADNbbbbbbDAAAAAAAAAAAAACgAAAAAASpJJJJJMNtttttu//AP8A/wD/AP8AN/8A/wD/AP8AoX//AP8A/wD8UyJJJb//AP8A/wD/AP8A/wD/AP8A/wDoW222223v/wD/AP8A/wD/AP8A/wD/AP8A/wD7bbbbbbfttttttHEkkkkkkBtttttttttttttgkkkkkkkNtbbbWbbbbbbbbbbbbcJaSSSSSSRkkkkkkkkkkkkkk7bbbbbb9tttttDyDbbbbbbKbbbbbbbbbbbb3bbbbbbZFtrbbbfkkkkkkkkkkkiySZJJJJJJJSSSSSSSSSSSSSSoAAAAAAj2222NAAUkkkkkkLbbbbbbbbbbb2SSSSSSTEkkkkkkgttttttttttqgAApJJJJJJOAAAAAAAAAAAAAAwAAAAAAg222Xbbf2222222oAAAAAAAAAApJJJJJJOzbbckkkknAAAAAAAAAGpJJJ2222222iAAAAAAAAAAAAAAO222222X223JJJM8kkkkkkRJJJJJJJJJkkkkkkki6SSSAAAAAASSSSSSSSX22222CSSSSSSESSSSSSSSSSSSSSCSSSSSSp/sJJJJNf/8A/wD/AP8AwAAAAAAAALSSSSSSTzJJJIAAAAAB7bbbbbbblttttttttttttsbbbbbbbbbbbbbbV/8A/wD/AP8A9khJJJJJRNttttttAAAAAAAAVttttttk/wD/AP8A/wD/AP8A/wD/AGUkkkkkkYSSSSSSa222222/ttttttttttttttuAAAAAAPwAAAAAAQAAAAAAI2222223wAAAAAAckkkkk222228pAAAAAB2OSSSSTOYkkkkktz//AP8A/wD/AP8A/wD/AP8A/wD/AMAAAAAA/lJJJJJGEkkkkkku2222220pttttttkEkkkmpJJJJf8AfSSSSTgDiSSSSm27bbbbr2222222222222222SSSSSekgbbbbYe222222ySSSSSSSSVSSSSSSXW222y6SSSC22zkkknjbbv/AP8A+ttt22219ttySSSSSSSSSSQJJLgAAAu22okkkrJJJJJJJySQNttttttgkkkkkkpAAAFLtts0AAAVJJYAAABSSTSSSTSSR6SSSW22222222212222tttckkk1JJNf/wD/AP8A/wDbbbbIAAAAAAFkkkkkkl223iSO2Ckkkki2xUkkkjySxJJJJVt/JJJJLkkkkkkkkkAAAAARJEAAAAGtv7bbbbbbv/8A/wD/AP8A/wD/AP8A/wBxtttttt2pJJJIXnbbbbYuobbbbbbipJJJJKaBJJJJJT//AP8A/wD/AP8AykkkkkkmUkkkkk8pJJJJJJFttttttttttttlpJJJJJIySSAAMpJJJJJAu222222wSSSSSSYSSSSSSSwAAAAAAISSSSSSQwAAAAAIf/8A/wD/AP8A8AAAAAAAAAAAAAAWSSSSSSXElJIO/wD/AP8A/wD/AKpJJJJJAKAAAAAOsAAAAAAqtttttttttttttnMAAAAABnEkkkkkrttttttsZttttttyUkkkkkrtskg27/8A/wD/AP8A+qSSSSQfigAAADr/AAAAAACAq222222222222cAwAAAAGbcSSSSSu222222xtm22223NpSSSSSu22SwbbEkkkkkk/kkkk0km7bbb/kkjbbbYskZtttttttttttu//jbbbbQAAttttBJJJJJJl/8A6SSSSt/8rbbbQAAAttttm222222zZJJG223bbbZW22/bbaFttjgAAAAAAAAACkkkvbbbbbbbv/8A9ySSSSSQYAABSSSaAAAT/wD/AEkkkn22226SSSSSSfwAJJJJIm23pJJJI23skkksbbbbbbbbbbv/AP8A/wCSSSSSSbSSSSSSSSSQf/8A/wD5JJH/AP8A/ttsySSSUAAAACSSSSSSTTY22222umu22220kgJJJJKdttttttttptttttf8kkkkkInW222222/bbbbb2zbbbbbK222222SSSSSSJJJJJJJf7bbbbbTjbbbbbZ7JJJJJLb//AP8A/wD/APt222222Q222222xEkkkkkkO2222228+22222xu2222ySSSSSXbbbbbbbO2222222222222wBJJJJJJ8AAAAAAF222222xgAAAAAAySSSSSSXoAAAAAAIAAAAAAm222224AAAAEAAAAAAAr9kkkkkkkkkkkkl2SSSSSSSzbbbbbfkkkkkki/wD/AP8A/wD/APgAAAAAAAHf/wD/AP8A/wD/AP8A/wD/AP8ASSSSSSTgAAAQAAAAAACtv2SSSSSSSSSSSXbZJJJJJJLNtttt+SSSSSSL/wD/AP8A/wD/AOAAAAAAAAAd/wD/AP8A/wD/AP8A/wD/AP0kkkkkkhu22zJJJJJJNqSSHkkkkkkkkkkibbbe222222k2222kkkkkkmttttttte222222222zFttttttttttySSSSSSQBJJOtttttttzbbbYbbbbbbbbbdm222Dbbbbbb7AAAAAAAAAAaSSSSSSpJJJJJJJJJJMySSSSSSSSSG2222222ABJOtttttttzbbbbYbbbbbbbbdm2222Dbbbbbb7AAAAAAAAAaSSSSSSpJJJJJJJJJJJMySSSSSSSSG2222222wABOtttttttzbbbbbYbbbbbbbdm22222Dbbbbbb7AAAAAAAAaSSSSSSpJJJJJJJJJJJJIySSSSSSSG22222222AAFX/AP8A/wD/AP8A2222223gAAAAAARkkkkkkl7bbbbbZNttttttsSSSSSSTSSSSSST6SSSSSSAkkkkkkmEkkkkkkkkgAMMAAAAAADbbbbbadtttttttxttttttAAAAAAAbbbbbbbbfbbbbbbYbbbbba9jbbbbbMAAAAAAABbbbbbbbbYAEJMAAAAA7ZpJJJJ9ttttttttwttttvpJJJJJKZttttttttyJJJJJJJpJJJIFvlJJJJcAAAAAAAAB//AP8A/wD/AP8A+AqSTv8A/wD/APJJGAAAJiSSSSSSSST5bbbcAAAAAABrbbbbbbbbbW2222223bbbR7bbrbbbH/8A/wD/AP8A/wD/AP6SSSSSSSQoAAAmkkkkkkiSSQW2222222222ttts2222222Ntttttttttt1JJJJJJCkksW22/Ekl+kkkkkkkkkki2222223X/8A/wD8tttttttqSYAAAAAAAAAAADttoAAAAAAjtttttttttttqtttttttiS3AAAAkSW9tttttttttttwAAAAAA0kkkkltttttttMlttttttttttttrA9ttttttuAAAAAAAAAAAAENtttttttpSSSSSRttttttttttttttySSSSSSSSSSSSySSSSSSySSSSSSSSSSSSSXCSSSSSSR2222220222222z6SSSSSS72222223SSSSSSSSSSSSSSG22222SSSSSSUySSSSSz6SSSSSSSSSSSSSSSSSSSSUO22222xx222222A6SSSSS7222222z6SSSSSSSSSSSSSSG2222wAAAAAyUAAAAAj/6SSSSSSSSSSSSSS9ttttk/JJJJJMkkJJJJJUigAAAAGJJJJJJJoAAAAAAAAAAAAAAARJJJJSSSSSSSSSSSSV//wCkkkkkkkkkkkkklWSSSSv/APpJJJR/9xJJJL//AMSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSV/wD/APpJJJJJJJJJJJJJVkkkr/8A/wCkklH/AP8AeSSS/wD/APxJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJa222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223/xAAtEQADAAEDAwQCAQQDAQEAAAAAARExITBRQEFhECBx4aHwUIGxwdFgkfFwwP/aAAgBAwEBPxD2K5HwhsobI+rjeCHyNCNOz7p8r91HROnZ9muV/ldiPhkfDI+GR8Mj4ZHwyPhkfDI+GR8Mj4ZHwyPhkfDI+GR8Mj4ZHwyPhkfDI+GR8Mj4ZHwyPhkfDEz+CMjIyMjIyMjIyMjIyMaMCQ4iqT8+RVCrIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMayS+K5+v7iU0S0IyMjIyMjIyMjIyMjIyMRHtL+x+DGhMrj6IyMjIy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eMqKioqE64sn7vPvYaKKKKK9GCCKKKKKK9Df319e5r4P7hJJFol702nUJ33U0WP4FCxjGj9jXTttfoF3mk9Hgz10amixtNIiIiIVIhIkREREREREREREXohERERlMREREREREREREJIyuMiIhK6JaiUh/p9kRERERERERERDXtAd1G1g/wDr9ERERFv/AKRJJRY2U46hkhERERbRY2kRERERERERERERERERbi1jyMaP0a/G5+oXeAAOtHgzV0IAANWixuAAf/f0TNEJEotuEdVj0X1eCTbQkYxozVosn7vPvfAAAQDcAB+vz69DX/8AQJJItEttaCdXo10WP4VSaj107EEEEEEEEEEEEEH6hSiiiiiiiiiiiih1o8CIYFFFFFFFFFFFFFEZT0WCiiiiiiiiiiiih2KFpvf/APubz/8AvE0W5+3x9lFFFFFFFFFFFFEUsk0s7sGsXyX+hSoloUUUUUUUUUUUUJNao7BRRRRRRRRRRRRQ1MkZRRRRRRRRRRERERERERERERERDSSrwYaERERERERERERERERERERFeXdhyEREREREREREREREREREPX4IiIiIiIiIiIiIiIiIiIiE5p26uH71BoxqbjyFMWd7OeCLHYhqsbsNXkaTyNR7jaWrG66Q+NtC7R6KvAx4uieR7cOTfa/HRJ/9FFFFFFFFFFFFFFFFFFFFFFFFFH79PVqjTW09BEF7KKKKKKKKKKHPV4J6w1WNuGr9Wqoxpp7Tc1YzfrD42lo6si9D2Di1eDBXSFPkIiIiIiIiIiITyCIiIiIiIiIiIiIiIiI0G78EREREREREREREREREREREREREQnPjqF/tIiIiIiGjHBERERERERENeIWxe1PZzngiIiIiIhK1WCIiIiIiIiIiE5ZERERENGoxw4yIiIiIiIiIcSrG6RERERDT4NCIiIiIiIiL0KNVkQvuNpKvA7SumAAACnJ78Bb569MAAAFpp26WH6BPe1Rqe5rxC1i9699NXjYhqse+Grz72k1GNl7sDr94GjTt7wGha1EECUdWRaCEIaIwUIIIIIIIIIIIIIIIIIIIIIIIF8gnyT5J8k+RloyJ8k+SfJPknyT5J8k+SfJPknyT5J8kjS7EEEEEEEEEEEEEEEEEEEEEEECLvgjyR5I8keSPJHkjyR5I8keSPJHkjyR5I8keSPJHkjyIAggggggggascsgggY8QtiIIIIIIIIEkQQQQIfwQQQQQQQQQIWqwQQQQIWryQQQQQQQQNWoOWQQQNJasepCEIQhCDTOPSEIKWLMuW/ZTadWRaejBjLqlOT18g3278dVDR463BpNDTT9GPELWLbT9afG5DVY9e49tpNDTT9G5qxu7cNHj1WsRY3c9yqdWRaa5MJdYryGAzvuv4KKKKKKKKKKKKKKKKKKKKKKKKKKKKWjx1uDVQmIFrFuplPgm7DVYIavdaqE7h1lFFFFFFFFFFFFLOBCRauUU1KKIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIhOhZEvQtSLgi4IuCLgi4IuCLgi4IuCLgi4IuCLgi4Q0XwRERERERERERERERERERERERERERERERERERERCmCIiIiIiIiIiIiIiIiIWmqyK0sREREREREREREREREaIoqGRUREREREREREREREREREtHg0IiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIcSrGrwRERERERERERERERERCiIPV15IiIi2Gp1WAvVTVZ32p1qe/+4XbbSQ22/Rj1CEq3p6PHVNpKsY3jeQobbded1qdRLRZIQQQd7IhCEIQhCEIQhCCCCCCCCCCCCCCCCCCCCCCCCCCCCCBJEEEEEEEEEEEEEEMJ7LA1WQQQNdRBUQQQQQQQQQQQQQPNHjqG0lWMbxvIUNtuvPuiIiIiIiIiIiIaIiIiIiIiIiIiIiIiIiIiIiIiIiIiIToXvJeqyREREREREREREREREREQ4+OtT38B7B6+5jVCEq3oaPHTOLVjVqRERERERERERERERERCUPV15IiIiIiIi22uknos+kRERERERER31vxEREREREREREREREREREREREREREREREREREREQoREREREREREREREREReidDERERERoPUiIiIiIiIiGqoZJVvS0eDQiIiIiIiIiIiIiIiIiIiIiIiIcSrKPG8hQ2268/woAADzRZ2wA+U351oAAAAJTfAAL6PPseu0zaohKt4AA+Dx0TaSrGN43lroGm3W9f4UAwFndAAW9VnfAAJ1qe/gMQau4ACmqGSVb0NHjoHpqym8bwABChtt152oIIIIIIIIIIIIIiCCCCCCCCCCCCBOyyPVrkgggggggggggggRnoQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQJIgggggggggggggVPTI0++8jtWN9iWu+rz233rkIIIIIIIIII3EqQQNTdiPlBonGREREREREREREREQyeiwRERERERERERERERERERERERERERERERERERERERERERERERERERERERERERETcY0WRB5IGhERERERERERERERCvgJFoiIiIiIiIiIiIiIiIiIStXn0iIiIiIiIiIiIiIiIiIiBK1WCIiIiIiIiIiIiIiIiIQRlEZERERERERERERkR8r/K2kr7WpvITyNNOPavosfzTGiyLWLPsa2v6YSmi3oavPRw1WN5CRjGj2kvQ17oiIiIiIiIiJ7wHOwAahUVcFXBVwVcCISSSSSSSQPdFj3ASXckkkkkkkaIa9wIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiL0ySSSSMxERERERERFUQhNM+4DgiIiIiIiIiP6ASLRbwADuPpAIR1WN5KRjGj2gH+QRGRkZGRkYlNhqe5ufJqRkZGRkZGR+9+hYIyMjIyMjIxN4fvdZGRkZGRkZGTpUlllRUVFRUVFQ0sojIyMjIxM0QtYs7DXuf8BKaLe7j2GiMjIyMjIyMjIyMjIyMjGrVYIyMjIyMjIyMjIyMjIyCMY0ftSNMez/AD69sZGRkZGRkZGRkY0yMjI/SMjIyMjIyMjIx18jTWfZTRY24yMj3GuCMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMV9iTaIi8kZGRkZGRkZGRjZEZGRjW1wJTRb3cfUw+N5CRjGjIyMjIyP2/wCfXrjqkJ5GmnH6U0WNxPs/RuDd/i0m0QtYs7jU9KfG/wBx9XDVY3mkkYxo9pT+0Tdg0UUUUUUUUUUUUUVWol0WN7/+g63v/wC53wABciCCCCCCCCCCCCBI9BCaFFFFFFFFFFFFehU/BBBBBBBBBBBBBBAkWu9jfQnoUUUUUUUUUUUUUOUY3ThRRXtQtY96xruusposDV3UqJQa7rfanx1eRKE9Hjdbhe5T53Uu73sFPgho8bsPkvcT3e2thC1j3r9Wu66qmix6tXbSolPVrut9qfHU5Ep6z0eNtueynztpd3vYKfHrDR424fJn1T2+2thCVjnr9rQggggggggggggggamy107EEEEDV/JJo9hK6ISJEEEEHdWy9ClZWVlIiCCCCCCCCCCCCCCCCCCCCCDUJEQQQQNNHjahBBBBO7PdZBBBBBBBBBBBBA0kqxr8e2GjwQQQQQQQQMvkZ9qez21sIWsY9fS5Gp7278e+nyY0fuSuiIfPv7i97c63IlPfDR43077+495tJVjG8e+ejx7/AOoG7q/en7+2thCVjNrKKKKKKKKK3pRsvj29jsVFRUVFRUVFRD+SiiiiiF8lRUVFRUVFRUOnoUUUUNNEZRRRRRRRGTo5RKbLdnv6vkoooo7r3npqxm3goooooooodrR49rT5Gm9WUUUUUUUV7nYRRRRRRRRQxq9trvvvUUUM3osFFFFFFFFFFFFFFFFFFFFFFFFFFFFDRGUUUUUUUUUUUUUUUUUUUUUUUUUUUJmKSiiiiiiiiiisMooooooooooooooTuc+qXd72Btv424aPHq0XkztylFDTREIQhCEITda32U0WOtb3nvpUSm7DR4306Jd3vYKfG7DR4HA3dXupnbXTvwRkZGRkZGRkZGRkZGRkY9dBifgjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyP0aIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIxMiMjIyMjIyMjIyMjIyMTMSmm93HvtVQaaz1jTREZGRkZGRkZGRkZGNNOPqm7osejVGp1Tc65KbyV3+4+haTQ00+qb3UJ5GmnH1Dd0WPY1RqdQ3OuSm8ld/uPo2k0NNPqG96IWnkk0IiIiIiIiHFvsiIiIiIiIaIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIuCOERwiOERwiOERcIjhEcIjhEcIjhEcIjhEcIjhEcIjhEcIjhEcDRERERERERERFvU+CIiIiIiIiIiIiIiIiIiE5ZERERERERERERERERERERERERERDRocOMiIiIiIiIiIiIiIiIiIiIiIiIiIiIcIiIiIiIiIiIiIiIiIiL1zl7jc329lqfxeRqdJT4Mb3cfTtJoaacfSN9HnL2G5vt7bU/i8jU6KnwY3u4+paTQ1NH0TfS5yGMjc3291qfxeRqdBT4Mb3cfVtUamj6Bvp53zxvxERERERERERERERERERF232+CsrKysrKysrKysrKys1ZEREREREREREREREREREQpNMb0XXeiSrEj3kXfJERERERERERERERERERDXBEREREREREREREREREREREREXtxlkyNXfa461q7rfXpzf7D3NEqx7ePSnzuNzoWulxFn2NUm6IPu2gBOjapjaa3wACdEnBK6reAAN2e0lNWM/h7AvztPTo2uj/qDPtapBBBBBBBBF7mtlqkEEEEEEEEEEEEEEEEEEDR+iCCCCCCCCCCCCCCCCCCCCCCIgggggggggggggggggggWm+ywyCCCCCCCJasa/grKysrKGXyIIIIIIIGkiUgggggggggggggggggggaZIIIIIIIIIIIIIII9IYyRsjIyMjIyMj32iMjIyMjI+napGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGKojIyMjIyMjIyMjIyMjIxnh+7AzbwRkZGRkZGRrVFFrn3Nzp2uN9ufO01ejwN3/gicM7yfswN342qfPsbnUtdTERERERERERERERERERERERERERwiOERwiOERwiOERwiOERwiOERwiOERwiOERwiOERwiOERwiOERwiOERwiOERwhoREQkyyOERwiOERwiOERwiOERwiOERwiOERwiOEROyIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIURoRERERERERERERERERex6kREREREREREREREJ31iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIhoRERERERERERERERF/MtUS53271ic6Bu7yd61r/ijXdbqVPA1N1Lu99u7yV69r/g1RUVFRUVFRUVFRUL1a7ojIyMjIyMjIyMjEn7GmRkZGRkZGRkZGRi5es3GmyMjIyMjIyMjIyMjIyMjJ/ANEZGRkZGRkZGR/zjW0lP8A5E1sJT/8OL//xAAsEQEBAQABBAEEAgEEAwEBAAAAEQFhITAxUXEQIEBBgfBQkaHh8WDB0bHA/9oACAECAQE/ELntc9rntc9sjltbd1mMXPeLntc9rntc9rntc9rntc9rntc9rntc9rntc9rntc9rntc9rntc9rntc9rntc9rntnXZnlm3dw6L/8A4/0nb7/Ly30Li4uLi4uLi4uLi4uLjcF3czMVeDH+7I25rHC4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uLi4uP8AZHPf/C5nTFxcXFxcXFxcXFxcXFxcZG3N6snlcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXFxcXG49rntc9rntc9pSljjltdd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3P0NHeXl+ipufoqbn6Km5+ipufoqbn6Km5+ipufoqbn6Km5+ipufoqbn6Km5+ipufoqbn6GzBPc/RU3P0VNz9FTc/RU3P0VNz9FTc/RU3P0QhNtLA6WG62FJpKEkt23Mtt9Wedvp52+nnb6edvp52+nnb6edvp52+iUYN2+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+iNIIS219Ikm0zyf6f0QQQQQQQQQ/wBrq2PlM8U+z6tijGo4MFgZaP66ExIIIIIIIIIIIIIIIIIIMVlCdy/07Hnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6SJsJIUtt9liYQ0MepI+bfcggggggggcsGOHe3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO30SiZuoXilfZYw2XDGzNhs223i23i223LbZ52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+iaPCxbszt2DujTWvCe6X+EEEEEEEEEEEEEEEEEEGRPHx0PK308rfTyt9PK308rfTyt9PK308rfTyt9PK308rfTyt9PK308rfTsJ2+nnb6edvp52+nnb6edvp52+nnb6edvosAlCSu2+rbfV/jTjB9P4LExJFLbIApSPyf6f1+PL4NnipaFZ9WxRkUGCwUtas+ifwcS/KuxcR5/M9MwkhS230SRhoQxfVI7+b7+PxtJqGNNOH/BWqBsoXikfZdmy4Y2222222223LbeLbbxbb/k3KCbszQM7/w1rBcnul/n8FuMF1/PJgun9/ijXmhGvNCNeaEa80I15oRrzQjXmhGvNCNeaEa80I15oRrzQSSzIIIIIIIIIIIIIIHIXCSZdlnXEbApSaj/ALP6IIIIIIIIIIIIMpi2eKloVn1bFDKmZKGk0e/WU+jWJAgQIECBAgQIECBAgLm99qLFxBHN80I15oRrzQjXmhGvNCNeaEa80I15oRrzQjXmhGvNCNeaEa80GKWwSRi23gksDCyhi8GkfWvm+5BBBBBBBBBBBA0ahkr+iCCCCCCCCCCCCCBK4EQjohMZkPDJj1HJtts22225bbxbbeLbZGvNCNeaEa80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6Tsu/f8AEo7pbELJbIhZLYhZLYcY47KFLdklmQsUdjomSrmyFktiFktiFktiFktiFktiFktiFktiFktiEsUkmhUIEnDxP/CDDbrylfd1yQ8TxG3Lbxbb6tshZLYhZLYhZLYhZLYhZLYhZLYhZLYhZLYSQ0th7w+nZ/8AsCVgKeyy+kLJbELJbELJbELJbELJbELJbELJbELJbELJbDTskR+Xyt9PK308rfTyt9PK308rfTyt9PK308rfTyt9PK30kpLeNN8GuzHVnJ0xT7spwaEzaSct4JJfRKS3L9cOlFiedvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+jIvnvf04wIqQQQQQQQQQQQQJRinDR0bcJeqIIIIIIIIIIIIIIIIqdEvr4QQQQQQQQQQQRYNhnl8PO30ist+FGLeSxJvLfhRgluedvp52+nnb6edvp52+nnb6edvp52+nnb6edvomTTUGsU11T3Iyfg6PpHNCCI3psuvVVvPIhs222bblt9W9zzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9FXzlGCZPEmR5XdsmOEm20kk228EksW230SGM3krhGDZf0nZdyCCCCCCCCCCCKnnb6OuG7KMW8lj1JCYaCZKpBBBBBBBBBBFTCMwdTtyfEkC5lN1h3Su5khjNhs5beLbfd4nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6NGobt9Gt18OPp52+kWLePbDpfqedvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6a90a90a90a90a90a90a90a90a90a90a90a90Q6RpkaePpCydiFJY13afgowknldE8ronldE8ronldE8ronldE8ronldE8ronldE8ronldDGU+Vyt3EEa2NdjXY12NdjXY12NdjXY12NdjXY12FHFSmqoZIjVcMakK2IVsQrYhWxCtiFbEK2IVsQrYhWxCtiFbEK2I5taoboUryWPmhCtiFbEK2IVsQrYhWxCtiFbEK2IVsQrYhWxCth2sYtdlKnSg8rQsqEEEEEEEEEEEECTTTUprFNPFPNHasiwxFGTYabbblt4tt4t5sgggggggggggglVEdZ6NdWqBJiYXWpyJyPpn1IVsQrYhWxCtiFbEK2IVsQrYhWxCtiFbEK2IVsO0xwuiwlvJVHx5GYoT2a90a90a90a90a90a90a90a90a90a90a90a90TWKlNVQnGN0SKIzTJ2z/tszYzblttS2QrYhWxCtiFbEK2IVsQrYhWxCtiFbEK2IVsNWoc29GMczDw6bkK2IVsQrYhWxCtiFbEK2IVsQrYhWxCtiFbEK2FPBS9UYMHNiFbEK2IVsQrYhWxCtiFbEK2IVsQrYhEIhEIhEIhEIhEIhDXMwkjFtvshMSksX1SO7myEQiEQiEQiEQiEQiEQiEQiEJhSSPQ6FC7KF2ULsoXZQuyhdlC7KF2ULsoXZQuyhdjGLWHZY4/CEQiEQiEQiEQiEQiEQiEYHRL6+EIhEIhEIhEIhEIhEIhEIhEIS8Yx/sof2ULsoXZQuyhdlC7KF2ULsoXZQuyhdlC7KF2ULsoXZQuyhdlC7KF2ULsoXZQuyhdlC7KF2ULsaE28EsW28Es3iKd4KdXjL8aiwYkylK7KF2ULsoXZQuyhdlC7KF2ULsoXZQuyhdjB0Oyly3kseoyZgsxwnshl/ZQuyhdlC7KF2ULsoXZQuyhdlC7KF2ULsoXZgCV2BBuX1zl+x2wax8vH6ULsoXZQuyhdlC7KF2ULsoXZQuyhdlC7KF2Onglh3cvH4JJpX+iIlLypeBQuyhdlC7KF2ULsoXZQuyhdlC7KF2ULsStwldiCI89THEsfLxIrBr+yhdlC7KF2ULsoXZQuyhdlC7KF2daA7kt4OuT7k6c0J05oTpzQnTmhOnNCdOaE6c0J05oTpzQTnISRLbb7LATgZYvqkdDrm+5OnNCdOaE6c0J05oTpzQnTmhOnNCdOaE6c0J05oTpzQnTmhOnNCdOaE6c0J05oTpzQZF0/oHT8bsTDsnv8J05oTpzQnTmhOnNCdOaE6c0J05oTpzQnTmhOnNCdOaE6c0J05oNkpbSS5kM8Cw/t8J05oTpzQnTmhOnNCdOaE6c0J05oTpzQnTmhOnNCdOaE6c0J05oTpzQnTmhOnNCdOaDHion+yGQQQQQQQQQQQQQQQQQQQQQQQRGLaSXVk6yE6vGX2+idOaE6c0Ehymr+hM1Ka5oTpzQnTmhOnNCdOaE6c0J05oTpzQnTmhOnNBlMKOy7t5ImwozHCbdc2TpzQnTmhOnNCCCCCCCCCCBiQo/wSYtq/onTmhPNX9E3g2p/u3UnS/onS/onS/onS/onS/onS/onS/onS/onS/onmuaE3gmo7vO3QnTmhOnNBIcpq/oRpKaqsrE6X9E6X9E6X9E6X9E6X9E6X9E6X9E6X9E6X9CY4UX9ELtPd8ROnNCdOaDsSif7p0J0v6J05oTpzQnTmhOnNCdOaE6c0J05oTpzQflkIbLBobESOhi39mT7kLJbELJbELJbELJbELJbELJbELJbDVSBCSUtt9kQzTVi1EK7mb/AImG6fTIo6GH4OgKFx0IWX5cFi4OiXRev8RLxhT46kLJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbELJbDSJtpJLFtxCWbJ7wE6uIb8a/wDKcOUJpqV+B46HZd28lUbEKi7JELJbELJbELJbELJbDTrC2IWS2IWS2IWS2IWS2IWS2IWS2IWS2IWS2EsJJtRYVZ1/5XgfX+/v4V9Gr/5aUoZJK/X4FJwkITDV5/8AK8Sif78kRg1+DyGpaQhprBolA27oS2/syzPK308rfTyt9PK308rfRCkYSSSltvokpMIaWL7K7mbIIIIIIIIIIIIIIIIIIIIIME+h/wCqf0edvp52+nnb6edvp52+nnb6SYt4dlHW/Q87fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9HnBPD+/hBBBBBBBBBBBBBBBBBBBBBJinD/s87fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9MOLgl1fGYqAnVx7/R52+nnb6edvp52+nnb6edvomTlO30SNSnb6edvp52+nnb6edvp52+kxgOyjFvJY9RpDeHZRglv1PO3087fTzt9PO3087fTzt9H3Tj4+nnb6edvp52+nnb6edvp52+naTx8dLmPF2fTyt9PK308rfTyt9PK308rfTHg3j4636nnb6edvp52+nnb6edvp52+mPBPDx1v0PK308rfTyt9PK308rfTyt9EzSrPoiTrj3UfTzt9PO3087fTzt9PO30Uu9volK92QQQQQQdYuv9mAggggg6N45b7twFnElR29FR29DEuBDTiGthEibLsSn4Ov9lTcvRU3L0VNy9CGUlmkkobbfRJQI8dDF4NI6HXNlR29FR29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bELJbELJbELJbELJbELJbELJbEuWoXq4WNFUSiSRJdFBCyWxCyWxCyWxCyWxCyWxCyWxCyW35MXkggggggggggggggggga3TTou7hIlCwSwSSwSIIIIIIIIIHPGYPO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTSEEEEEEEEEEEEEEEEEEEEEEEEEEEEEvkgggggggggggggaNNPFPBprqSpMdNjx6Hnb6edvopd7fRQ72+nnb6edvp52+nnb6edvp52+nnb6edvp52+iFhvw46XHPD0fZrNEEEEEEEEEEEEECcSwfev08rfTyt9PK308rfTyt9PK308rfTyt9PK308rfTyt9PK308rfTyt9PK308rfTyt9E7cJy3T6KXB493HW/Q87fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fRzS3CdXHWiqJESWCXREEEEEEEEHnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvph72+iT3x8fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fTzt9PO3087fSUuj7RHh16CVKFglgklgkeR5HkeR5HkeR5HkeR5DMEEEEEEEEEEEEDlgyB9cO2B52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+kuKePj6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvo0aablPqo+jmhi/fNk6CkKBBBBBBBBBBBApYej7pjWhu3W552+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+k2KePjrfqedvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6edvp52+nnb6Jm4Tt9FLm+752IIIIIIIIIIIIIHvknV/4qiREk4S6KPp52+nnb6edvp52+nnb6edvp52+nnb6edvpBBBBBBBBBBBA4WIpcmuq/1EEEEEEEEEEEEECxz5rrSyzMY012I7R2ihBBBBBBBBBBBBA0pIIIIIIIIIIIIIIRB4IIIIIIIIIIIIIIIIIIIIIIIIIIIIGjx7kEEEEEEEEEEEEE4+1DT70jvImtwmlOCmcPMdiNeaEa80I15oRrzQjXmhGvNCNeaEa80I15oRrzQjXmhGvNCNeaEGHL7OmFehGvNCNeaEa80I15oRrzQjXmhGvNCNeaEa80I15oRrzQjXmhGvNCObt6FfBNXh12I15oRrzQjXmhGvNCNeaEa80I15oRrzQjXmhGvNCNeaEa80I15oRrzQ6DGdMbEa80I15oRrzQjXmhGvNCNeaEa80I15oRrzQjXmhGvNCNeaEa80I15oTr0pYRl5IIIIIIIIIIOZv2czfs5m/ZzN+zmb9nM37OZv2czfs5m/ZzN+zmb9iSWrvG42Pp4UvC4kOUoaq/Y6OkNdVL3WPQoXZQuyhdlC7KF2ULsoXZQuyhdlC7KF2JdIadgsU2XZUd3p4SCSSSShJYJJdEkK8Vg6P/AB0Es04NVfspXfspXfspXfspXfspXfspXfspXfspXfspXfspXfspXfsZnCYa4kIRsGsfL9lK79lK79lK79lK79lK79lK79lK79lK79lK79lK79lK79jTi1d+yFlBCKV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37KV37OoS0xKF2ULsoXZQuyhdlC7KF2ULsoXZQuyhdkN4CVXjRY9Rk0tYLopeF+pSu/YmdsfL9lK79lK79lK79lK79lK79lK79lK79lK79lK79lK79lK79kPpLdFLwq8RuctS3V+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+ybBLDu5eN+hzN+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+yld+zpGsfLxuUrv2Urv2Urv2Urv2Urv2Urv2Urv2Urv2Urv2Urv2Urv2Urv2Urv2ReUvKlw1uRZS8qXKe5Su/ZSu/ZSu/ZSu/ZSu/ZSu/ZSu/ZSu/ZSu/ZJ1nFB17pu+51X4kJL/Yxpeiy/9TacrBoWmTXVf6qfie6Q0j8Nl2X9mJJEkkklCSwSS6JL/wBQmTXR87DGaahr8U+C6f38/wCOw9H+JuMWNz/8HuLVfiQrZCV6KoxpeCXRZff+E58/iQubdFlVjbbl4t/mnwXTu8/n8Ppn1/v8z3lars0LWVqu6f4kYPeKbr3a3fJ3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) fixed' + + # Colors + text-color: '#636B75' # Grey text + text-medium-color: '#8c96a5' # Medium grey text + text-light-color: '#BAC0C6' # Light grey text + accent-color: '#00a1ff' # Blue + background-color: '#F7F8F9' # Light grey background + background-color-2: '#F4F5F6' # Light grey background + background-card-color: 'rgba(255,255,255,1.0)' # White background + border-color: '#E8E8E8' # Light grey border + + # Header + primary-color: 'var(--text-color)' # Background + text-primary-color: '#FFF' # Text + + # Left Menu + paper-listbox-background-color: 'var(--background-color)' # Background + + # UI + paper-card-header-color: 'var(--text-color)' # Title in settings + primary-background-color: 'var(--background-color)' # Background color (also title background in left menu) + + # Card + paper-card-background-color: 'var(--background-card-color)' # Background + dark-primary-color: 'var(--text-color)' + primary-text-color: 'var(--text-color)' + paper-listbox-color: 'var(--text-color)' + light-primary-color: 'var(--text-light-color)' + secondary-text-color: 'var(--text-medium-color)' + disabled-text-color: 'var(--text-light-color)' + paper-dialog-button-color: 'var(--text-color)' + secondary-background-color: 'var(--background-color-2)' # Background more info title + + # Icons + paper-item-icon-color: 'var(--text-light-color)' # Off + paper-item-icon-active-color: 'var(--accent-color)' # On + + # Switches + switch-checked-button-color: '#FFF' # Knob On + switch-unchecked-button-color: '#FFF' # Knob Off + switch-checked-track-color: '#0077FF' # Background On + switch-unchecked-track-color: 'var(--disabled-text-color)' # Background Off + + # Slider + paper-slider-active-color: 'var(--accent-color)' # Line On + paper-slider-container-color: '#e5e7ea' # Line Off + paper-slider-knob-color: 'var(--text-light-color)' # Knob On + paper-slider-knob-start-color: 'var(--text-light-color)' # Knob Off + + # Shadows + ha-card-box-shadow: 'inset 0px 0px 0px 1px var(--border-color)' + + # HACS + hacs-badge-color: 'var(--accent-color)' # New Badge + hacs-status-installed: 'var(--text-color)' # Installed Icon + hacs-status-pending-restart: 'var(--text-light-color)' # Restart Icon + hacs-status-pending-update: 'var(--accent-color)' # Update Icon \ No newline at end of file diff --git a/themes/dark_themes/dark_themes.yaml b/themes/dark_themes/dark_themes.yaml new file mode 100644 index 0000000..2373727 --- /dev/null +++ b/themes/dark_themes/dark_themes.yaml @@ -0,0 +1,269 @@ +Dark Blue: + ha-card-border-radius: 2px + ha-card-box-shadow: "1px 1px 2px 0px rgba(0,0,0,0.3)" + + primary-color: "#00A0E2" + light-primary-color: "#19BAFF" + graph-color: var(--primary-color) + primary-background-color: "#3c3f40" + secondary-background-color: var(--primary-background-color) + divider-color: "#292a2e" + disabled-color: "#666666" + + primary-text-color: "#FBFBFB" + secondary-text-color: var(--primary-color) + text-primary-color: var(--primary-text-color) + disabled-text-color: "#7A7D90" + + sidebar-icon-color: var(--primary-color) + sidebar-text-color: var(--primary-text-color) + sidebar-background-color: "#323536" + sidebar-selected-background-color: var(--primary-background-color) + sidebar-selected-icon-color: var(--light-primary-color) + sidebar-selected-text-color: var(--sidebar-selected-icon-color) + + state-icon-color: var(--primary-color) + state-icon-active-color: "#ffdd00" + state-icon-unavailable-color: var(--disabled-text-color) + + paper-slider-knob-color: var(--primary-color) + paper-slider-knob-start-color: var(--paper-slider-knob-color) + paper-slider-pin-color: var(--paper-slider-knob-color) + paper-slider-active-color: var(--paper-slider-knob-color) + paper-slider-secondary-color: var(--light-primary-color) + label-badge-background-color: var(--divider-color) + label-badge-text-color: var(--primary-text-color) + label-badge-red: var(--light-primary-color) + + paper-card-background-color: "#4d5052" + paper-listbox-background-color: var(--primary-background-color) + + paper-toggle-button-checked-button-color: var(--primary-color) + paper-toggle-button-checked-bar-color: var(--light-primary-color) + paper-toggle-button-unchecked-button-color: var(--disabled-color) + paper-toggle-button-unchecked-bar-color: "#333333" + + switch-checked-color: var(--paper-toggle-button-checked-button-color) + switch-unchecked-button-color: var(--paper-toggle-button-unchecked-button-color) + switch-unchecked-color: var(--paper-toggle-button-unchecked-bar-color) + switch-unchecked-track-color: var(--paper-toggle-button-unchecked-bar-color) + + table-row-background-color: var(--divider-color) + table-row-alternative-background-color: var(--secondary-background-color) + card-background-color: var(--primary-background-color) + +Dark Green: + ha-card-border-radius: 2px + ha-card-box-shadow: "1px 1px 2px 0px rgba(0,0,0,0.3)" + + primary-color: "#27ae61" + light-primary-color: "#50FA9A" + graph-color: var(--primary-color) + primary-background-color: "#39403c" + secondary-background-color: var(--primary-background-color) + divider-color: "#292a2e" + disabled-color: "#666666" + + primary-text-color: "#FBFBFB" + secondary-text-color: var(--primary-color) + text-primary-color: var(--primary-text-color) + disabled-text-color: "#7A7D90" + + sidebar-icon-color: var(--primary-color) + sidebar-text-color: var(--primary-text-color) + sidebar-background-color: "#323633" + sidebar-selected-background-color: var(--primary-background-color) + sidebar-selected-icon-color: var(--light-primary-color) + sidebar-selected-text-color: var(--sidebar-selected-icon-color) + + state-icon-color: var(--primary-color) + state-icon-active-color: "#ffdd00" + state-icon-unavailable-color: var(--disabled-text-color) + + paper-slider-knob-color: var(--primary-color) + paper-slider-knob-start-color: var(--paper-slider-knob-color) + paper-slider-pin-color: var(--paper-slider-knob-color) + paper-slider-active-color: var(--paper-slider-knob-color) + paper-slider-secondary-color: var(--light-primary-color) + label-badge-background-color: var(--divider-color) + label-badge-text-color: var(--primary-text-color) + label-badge-red: var(--light-primary-color) + + paper-card-background-color: "#4e524f" + paper-listbox-background-color: var(--primary-background-color) + + paper-toggle-button-checked-button-color: var(--primary-color) + paper-toggle-button-checked-bar-color: var(--light-primary-color) + paper-toggle-button-unchecked-button-color: var(--disabled-color) + paper-toggle-button-unchecked-bar-color: "#333333" + + switch-checked-color: var(--paper-toggle-button-checked-button-color) + switch-unchecked-button-color: var(--paper-toggle-button-unchecked-button-color) + switch-unchecked-color: var(--paper-toggle-button-unchecked-bar-color) + switch-unchecked-track-color: var(--paper-toggle-button-unchecked-bar-color) + + table-row-background-color: var(--divider-color) + table-row-alternative-background-color: var(--secondary-background-color) + card-background-color: var(--primary-background-color) + +Dark Orange: + ha-card-border-radius: 2px + ha-card-box-shadow: "1px 1px 2px 0px rgba(0,0,0,0.3)" + + primary-color: "#E77E23" + light-primary-color: "#FF9940" + graph-color: var(--primary-color) + primary-background-color: "#403e3c" + secondary-background-color: var(--primary-background-color) + divider-color: "#292a2e" + disabled-color: "#666666" + + primary-text-color: "#FBFBFB" + secondary-text-color: var(--primary-color) + text-primary-color: var(--primary-text-color) + disabled-text-color: "#7A7D90" + + sidebar-icon-color: var(--primary-color) + sidebar-text-color: var(--primary-text-color) + sidebar-background-color: "#363332" + sidebar-selected-background-color: var(--primary-background-color) + sidebar-selected-icon-color: var(--light-primary-color) + sidebar-selected-text-color: var(--sidebar-selected-icon-color) + + state-icon-color: var(--primary-color) + state-icon-active-color: "#ffdd00" + state-icon-unavailable-color: var(--disabled-text-color) + + paper-slider-knob-color: var(--primary-color) + paper-slider-knob-start-color: var(--paper-slider-knob-color) + paper-slider-pin-color: var(--paper-slider-knob-color) + paper-slider-active-color: var(--paper-slider-knob-color) + paper-slider-secondary-color: var(--light-primary-color) + label-badge-background-color: var(--divider-color) + label-badge-text-color: var(--primary-text-color) + label-badge-red: var(--light-primary-color) + + paper-card-background-color: "#524f4d" + paper-listbox-background-color: var(--primary-background-color) + + paper-toggle-button-checked-button-color: var(--primary-color) + paper-toggle-button-checked-bar-color: var(--light-primary-color) + paper-toggle-button-unchecked-button-color: var(--disabled-color) + paper-toggle-button-unchecked-bar-color: "#333333" + + switch-checked-color: var(--paper-toggle-button-checked-button-color) + switch-unchecked-button-color: var(--paper-toggle-button-unchecked-button-color) + switch-unchecked-color: var(--paper-toggle-button-unchecked-bar-color) + switch-unchecked-track-color: var(--paper-toggle-button-unchecked-bar-color) + + table-row-background-color: var(--divider-color) + table-row-alternative-background-color: var(--secondary-background-color) + card-background-color: var(--primary-background-color) + +Dark Turqoise: + ha-card-border-radius: 2px + ha-card-box-shadow: "1px 1px 2px 0px rgba(0,0,0,0.3)" + + primary-color: "#00BCD4" + light-primary-color: "#19E4FF" + graph-color: var(--primary-color) + primary-background-color: "#3c3f40" + secondary-background-color: var(--primary-background-color) + divider-color: "#292a2e" + disabled-color: "#666666" + + primary-text-color: "#FBFBFB" + secondary-text-color: var(--primary-color) + text-primary-color: var(--primary-text-color) + disabled-text-color: "#7A7D90" + + sidebar-icon-color: var(--primary-color) + sidebar-text-color: var(--primary-text-color) + sidebar-background-color: "#323536" + sidebar-selected-background-color: var(--primary-background-color) + sidebar-selected-icon-color: var(--light-primary-color) + sidebar-selected-text-color: var(--sidebar-selected-icon-color) + + state-icon-color: var(--primary-color) + state-icon-active-color: "#ffdd00" + state-icon-unavailable-color: var(--disabled-text-color) + + paper-slider-knob-color: var(--primary-color) + paper-slider-knob-start-color: var(--paper-slider-knob-color) + paper-slider-pin-color: var(--paper-slider-knob-color) + paper-slider-active-color: var(--paper-slider-knob-color) + paper-slider-secondary-color: var(--light-primary-color) + label-badge-background-color: var(--divider-color) + label-badge-text-color: var(--primary-text-color) + label-badge-red: var(--light-primary-color) + + paper-card-background-color: "#4d5152" + paper-listbox-background-color: var(--primary-background-color) + + paper-toggle-button-checked-button-color: var(--primary-color) + paper-toggle-button-checked-bar-color: var(--light-primary-color) + paper-toggle-button-unchecked-button-color: var(--disabled-color) + paper-toggle-button-unchecked-bar-color: "#333333" + + switch-checked-color: var(--paper-toggle-button-checked-button-color) + switch-unchecked-button-color: var(--paper-toggle-button-unchecked-button-color) + switch-unchecked-color: var(--paper-toggle-button-unchecked-bar-color) + switch-unchecked-track-color: var(--paper-toggle-button-unchecked-bar-color) + + table-row-background-color: var(--divider-color) + table-row-alternative-background-color: var(--secondary-background-color) + card-background-color: var(--primary-background-color) + +Dark Yellow: + ha-card-border-radius: 2px + ha-card-box-shadow: "1px 1px 2px 0px rgba(0,0,0,0.3)" + + primary-color: "#e5ba0e" + light-primary-color: "#FFD429" + graph-color: var(--primary-color) + primary-background-color: "#403f3c" + secondary-background-color: var(--primary-background-color) + divider-color: "#292a2e" + disabled-color: "#666666" + + primary-text-color: "#FBFBFB" + secondary-text-color: var(--primary-color) + text-primary-color: var(--primary-text-color) + disabled-text-color: "#7A7D90" + + sidebar-icon-color: var(--primary-color) + sidebar-text-color: var(--primary-text-color) + sidebar-background-color: "#363432" + sidebar-selected-background-color: var(--primary-background-color) + sidebar-selected-icon-color: var(--light-primary-color) + sidebar-selected-text-color: var(--sidebar-selected-icon-color) + + state-icon-color: var(--primary-color) + state-icon-active-color: "#ffdd00" + state-icon-unavailable-color: var(--disabled-text-color) + + paper-slider-knob-color: var(--primary-color) + paper-slider-knob-start-color: var(--paper-slider-knob-color) + paper-slider-pin-color: var(--paper-slider-knob-color) + paper-slider-active-color: var(--paper-slider-knob-color) + paper-slider-secondary-color: var(--light-primary-color) + label-badge-background-color: var(--divider-color) + label-badge-text-color: var(--primary-text-color) + label-badge-red: var(--light-primary-color) + + paper-card-background-color: "#52514d" + paper-listbox-background-color: var(--primary-background-color) + + paper-toggle-button-checked-button-color: var(--primary-color) + paper-toggle-button-checked-bar-color: var(--light-primary-color) + paper-toggle-button-unchecked-button-color: var(--disabled-color) + paper-toggle-button-unchecked-bar-color: "#333333" + + switch-checked-color: var(--paper-toggle-button-checked-button-color) + switch-unchecked-button-color: var(--paper-toggle-button-unchecked-button-color) + switch-unchecked-color: var(--paper-toggle-button-unchecked-bar-color) + switch-unchecked-track-color: var(--paper-toggle-button-unchecked-bar-color) + + table-row-background-color: var(--divider-color) + table-row-alternative-background-color: var(--secondary-background-color) + card-background-color: var(--primary-background-color) diff --git a/ui-lovelace.yaml b/ui-lovelace.yaml deleted file mode 100644 index afeaa43..0000000 --- a/ui-lovelace.yaml +++ /dev/null @@ -1,13 +0,0 @@ -title: 238 McHaley -# custom cards registration -resources: - - url: /hacsfiles/banner-card/banner-card.js - type: module - - url: /hacsfiles/button-card/button-card.js - type: module - -views: - - !include /config/lovelace/uci-lighting.yaml - - !include /config/lovelace/temps.yaml - - !include /config/lovelace/closet.yaml - - !include /config/lovelace/lights.yaml diff --git a/www/community/banner-card/.gitignore b/www/community/banner-card/.gitignore deleted file mode 100644 index dcbcbde..0000000 --- a/www/community/banner-card/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js.gz diff --git a/www/community/banner-card/banner-card.js b/www/community/banner-card/banner-card.js deleted file mode 100644 index 1f6b096..0000000 --- a/www/community/banner-card/banner-card.js +++ /dev/null @@ -1,4 +0,0 @@ -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t=t||self).BannerCard=e()}(this,function(){"use strict";const t=new WeakMap,e=e=>"function"==typeof e&&t.has(e),i=void 0!==window.customElements&&void 0!==window.customElements.polyfillWrapFlushCallback,s=(t,e,i=null)=>{let s=e;for(;s!==i;){const e=s.nextSibling;t.removeChild(s),s=e}},n={},r={},o=`{{lit-${String(Math.random()).slice(2)}}}`,a=`\x3c!--${o}--\x3e`,c=new RegExp(`${o}|${a}`),l="$lit$";class h{constructor(t,e){this.parts=[],this.element=e;let i=-1,s=0;const n=[],r=e=>{const a=e.content,h=document.createTreeWalker(a,133,null,!1);let d=0;for(;h.nextNode();){i++;const e=h.currentNode;if(1===e.nodeType){if(e.hasAttributes()){const n=e.attributes;let r=0;for(let t=0;t=0&&r++;for(;r-- >0;){const n=t.strings[s],r=p.exec(n)[2],o=r.toLowerCase()+l,a=e.getAttribute(o).split(c);this.parts.push({type:"attribute",index:i,name:r,strings:a}),e.removeAttribute(o),s+=a.length-1}}"TEMPLATE"===e.tagName&&r(e)}else if(3===e.nodeType){const t=e.data;if(t.indexOf(o)>=0){const r=e.parentNode,o=t.split(c),a=o.length-1;for(let t=0;t-1!==t.index,u=()=>document.createComment(""),p=/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F \x09\x0a\x0c\x0d"'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;class m{constructor(t,e,i){this._parts=[],this.template=t,this.processor=e,this.options=i}update(t){let e=0;for(const i of this._parts)void 0!==i&&i.setValue(t[e]),e++;for(const t of this._parts)void 0!==t&&t.commit()}_clone(){const t=i?this.template.element.content.cloneNode(!0):document.importNode(this.template.element.content,!0),e=this.template.parts;let s=0,n=0;const r=t=>{const i=document.createTreeWalker(t,133,null,!1);let o=i.nextNode();for(;snull===t||!("object"==typeof t||"function"==typeof t);class g{constructor(t,e,i){this.dirty=!0,this.element=t,this.name=e,this.strings=i,this.parts=[];for(let t=0;tthis.handleEvent(t)}setValue(t){this._pendingValue=t}commit(){for(;e(this._pendingValue);){const t=this._pendingValue;this._pendingValue=n,t(this)}if(this._pendingValue===n)return;const t=this._pendingValue,i=this.value,s=null==t||null!=i&&(t.capture!==i.capture||t.once!==i.once||t.passive!==i.passive),r=null!=t&&(null==i||s);s&&this.element.removeEventListener(this.eventName,this._boundHandleEvent,this._options),r&&(this._options=C(t),this.element.addEventListener(this.eventName,this._boundHandleEvent,this._options)),this.value=t,this._pendingValue=n}handleEvent(t){"function"==typeof this.value?this.value.call(this.eventContext||this.element,t):this.value.handleEvent(t)}}const C=t=>t&&(x?{capture:t.capture,passive:t.passive,once:t.once}:t.capture);const N=new class{handleAttributeExpressions(t,e,i,s){const n=e[0];if("."===n){return new w(t,e.slice(1),i).parts}return"@"===n?[new P(t,e.slice(1),s.eventContext)]:"?"===n?[new b(t,e.slice(1),i)]:new g(t,e,i).parts}handleTextExpression(t){return new _(t)}};function $(t){let e=k.get(t.type);void 0===e&&(e={stringsArray:new WeakMap,keyString:new Map},k.set(t.type,e));let i=e.stringsArray.get(t.strings);if(void 0!==i)return i;const s=t.strings.join(o);return void 0===(i=e.keyString.get(s))&&(i=new h(t,t.getTemplateElement()),e.keyString.set(s,i)),e.stringsArray.set(t.strings,i),i}const k=new Map,A=new WeakMap;(window.litHtmlVersions||(window.litHtmlVersions=[])).push("1.0.0");const E=(t,...e)=>new f(t,e,"html",N),T=133;function V(t,e){const{element:{content:i},parts:s}=t,n=document.createTreeWalker(i,T,null,!1);let r=O(s),o=s[r],a=-1,c=0;const l=[];let h=null;for(;n.nextNode();){a++;const t=n.currentNode;for(t.previousSibling===h&&(h=null),e.has(t)&&(l.push(t),null===h&&(h=t)),null!==h&&c++;void 0!==o&&o.index===a;)o.index=null!==h?-1:o.index-c,o=s[r=O(s,r)]}l.forEach(t=>t.parentNode.removeChild(t))}const z=t=>{let e=11===t.nodeType?0:1;const i=document.createTreeWalker(t,T,null,!1);for(;i.nextNode();)e++;return e},O=(t,e=-1)=>{for(let i=e+1;i`${t}--${e}`;let M=!0;void 0===window.ShadyCSS?M=!1:void 0===window.ShadyCSS.prepareTemplateDom&&(console.warn("Incompatible ShadyCSS version detected.Please update to at least @webcomponents/webcomponentsjs@2.0.2 and@webcomponents/shadycss@1.3.1."),M=!1);const U=t=>e=>{const i=j(e.type,t);let s=k.get(i);void 0===s&&(s={stringsArray:new WeakMap,keyString:new Map},k.set(i,s));let n=s.stringsArray.get(e.strings);if(void 0!==n)return n;const r=e.strings.join(o);if(void 0===(n=s.keyString.get(r))){const i=e.getTemplateElement();M&&window.ShadyCSS.prepareTemplateDom(i,t),n=new h(e,i),s.keyString.set(r,n)}return s.stringsArray.set(e.strings,n),n},R=["html","svg"],q=new Set,I=(t,e,i)=>{q.add(i);const s=t.querySelectorAll("style");if(0===s.length)return void window.ShadyCSS.prepareTemplateStyles(e.element,i);const n=document.createElement("style");for(let t=0;t{R.forEach(e=>{const i=k.get(j(e,t));void 0!==i&&i.keyString.forEach(t=>{const{element:{content:e}}=t,i=new Set;Array.from(e.querySelectorAll("style")).forEach(t=>{i.add(t)}),V(t,i)})})})(i),function(t,e,i=null){const{element:{content:s},parts:n}=t;if(null==i)return void s.appendChild(e);const r=document.createTreeWalker(s,T,null,!1);let o=O(n),a=0,c=-1;for(;r.nextNode();){for(c++,r.currentNode===i&&(a=z(e),i.parentNode.insertBefore(e,i));-1!==o&&n[o].index===c;){if(a>0){for(;-1!==o;)n[o].index+=a,o=O(n,o);return}o=O(n,o)}}}(e,n,e.element.content.firstChild),window.ShadyCSS.prepareTemplateStyles(e.element,i),window.ShadyCSS.nativeShadow){const i=e.element.content.querySelector("style");t.insertBefore(i.cloneNode(!0),t.firstChild)}else{e.element.content.insertBefore(n,e.element.content.firstChild);const t=new Set;t.add(n),V(e,t)}};window.JSCompiler_renameProperty=(t,e)=>t;const F={toAttribute(t,e){switch(e){case Boolean:return t?"":null;case Object:case Array:return null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){switch(e){case Boolean:return null!==t;case Number:return null===t?null:Number(t);case Object:case Array:return JSON.parse(t)}return t}},H=(t,e)=>e!==t&&(e==e||t==t),B={attribute:!0,type:String,converter:F,reflect:!1,hasChanged:H},D=Promise.resolve(!0),L=1,W=4,J=8,Y=16,G=32,K="finalized";class Q extends HTMLElement{constructor(){super(),this._updateState=0,this._instanceProperties=void 0,this._updatePromise=D,this._hasConnectedResolver=void 0,this._changedProperties=new Map,this._reflectingProperties=void 0,this.initialize()}static get observedAttributes(){this.finalize();const t=[];return this._classProperties.forEach((e,i)=>{const s=this._attributeNameForProperty(i,e);void 0!==s&&(this._attributeToPropertyMap.set(s,i),t.push(s))}),t}static _ensureClassProperties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_classProperties",this))){this._classProperties=new Map;const t=Object.getPrototypeOf(this)._classProperties;void 0!==t&&t.forEach((t,e)=>this._classProperties.set(e,t))}}static createProperty(t,e=B){if(this._ensureClassProperties(),this._classProperties.set(t,e),e.noAccessor||this.prototype.hasOwnProperty(t))return;const i="symbol"==typeof t?Symbol():`__${t}`;Object.defineProperty(this.prototype,t,{get(){return this[i]},set(e){const s=this[t];this[i]=e,this._requestUpdate(t,s)},configurable:!0,enumerable:!0})}static finalize(){const t=Object.getPrototypeOf(this);if(t.hasOwnProperty(K)||t.finalize(),this[K]=!0,this._ensureClassProperties(),this._attributeToPropertyMap=new Map,this.hasOwnProperty(JSCompiler_renameProperty("properties",this))){const t=this.properties,e=[...Object.getOwnPropertyNames(t),..."function"==typeof Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(t):[]];for(const i of e)this.createProperty(i,t[i])}}static _attributeNameForProperty(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}static _valueHasChanged(t,e,i=H){return i(t,e)}static _propertyValueFromAttribute(t,e){const i=e.type,s=e.converter||F,n="function"==typeof s?s:s.fromAttribute;return n?n(t,i):t}static _propertyValueToAttribute(t,e){if(void 0===e.reflect)return;const i=e.type,s=e.converter;return(s&&s.toAttribute||F.toAttribute)(t,i)}initialize(){this._saveInstanceProperties(),this._requestUpdate()}_saveInstanceProperties(){this.constructor._classProperties.forEach((t,e)=>{if(this.hasOwnProperty(e)){const t=this[e];delete this[e],this._instanceProperties||(this._instanceProperties=new Map),this._instanceProperties.set(e,t)}})}_applyInstanceProperties(){this._instanceProperties.forEach((t,e)=>this[e]=t),this._instanceProperties=void 0}connectedCallback(){this._updateState=this._updateState|G,this._hasConnectedResolver&&(this._hasConnectedResolver(),this._hasConnectedResolver=void 0)}disconnectedCallback(){}attributeChangedCallback(t,e,i){e!==i&&this._attributeToProperty(t,i)}_propertyToAttribute(t,e,i=B){const s=this.constructor,n=s._attributeNameForProperty(t,i);if(void 0!==n){const t=s._propertyValueToAttribute(e,i);if(void 0===t)return;this._updateState=this._updateState|J,null==t?this.removeAttribute(n):this.setAttribute(n,t),this._updateState=this._updateState&~J}}_attributeToProperty(t,e){if(this._updateState&J)return;const i=this.constructor,s=i._attributeToPropertyMap.get(t);if(void 0!==s){const t=i._classProperties.get(s)||B;this._updateState=this._updateState|Y,this[s]=i._propertyValueFromAttribute(e,t),this._updateState=this._updateState&~Y}}_requestUpdate(t,e){let i=!0;if(void 0!==t){const s=this.constructor,n=s._classProperties.get(t)||B;s._valueHasChanged(this[t],e,n.hasChanged)?(this._changedProperties.has(t)||this._changedProperties.set(t,e),!0!==n.reflect||this._updateState&Y||(void 0===this._reflectingProperties&&(this._reflectingProperties=new Map),this._reflectingProperties.set(t,n))):i=!1}!this._hasRequestedUpdate&&i&&this._enqueueUpdate()}requestUpdate(t,e){return this._requestUpdate(t,e),this.updateComplete}async _enqueueUpdate(){let t,e;this._updateState=this._updateState|W;const i=this._updatePromise;this._updatePromise=new Promise((i,s)=>{t=i,e=s});try{await i}catch(t){}this._hasConnected||await new Promise(t=>this._hasConnectedResolver=t);try{const t=this.performUpdate();null!=t&&await t}catch(t){e(t)}t(!this._hasRequestedUpdate)}get _hasConnected(){return this._updateState&G}get _hasRequestedUpdate(){return this._updateState&W}get hasUpdated(){return this._updateState&L}performUpdate(){this._instanceProperties&&this._applyInstanceProperties();let t=!1;const e=this._changedProperties;try{(t=this.shouldUpdate(e))&&this.update(e)}catch(e){throw t=!1,e}finally{this._markUpdated()}t&&(this._updateState&L||(this._updateState=this._updateState|L,this.firstUpdated(e)),this.updated(e))}_markUpdated(){this._changedProperties=new Map,this._updateState=this._updateState&~W}get updateComplete(){return this._getUpdateComplete()}_getUpdateComplete(){return this._updatePromise}shouldUpdate(t){return!0}update(t){void 0!==this._reflectingProperties&&this._reflectingProperties.size>0&&(this._reflectingProperties.forEach((t,e)=>this._propertyToAttribute(e,this[e],t)),this._reflectingProperties=void 0)}updated(t){}firstUpdated(t){}}Q[K]=!0;const X="adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,Z=Symbol();class tt{constructor(t,e){if(e!==Z)throw new Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t}get styleSheet(){return void 0===this._styleSheet&&(X?(this._styleSheet=new CSSStyleSheet,this._styleSheet.replaceSync(this.cssText)):this._styleSheet=null),this._styleSheet}toString(){return this.cssText}}(window.litElementVersions||(window.litElementVersions=[])).push("2.2.1");const et=t=>t.flat?t.flat(1/0):function t(e,i=[]){for(let s=0,n=e.length;s(t.add(e),t),new Set).forEach(t=>e.unshift(t))}else t&&e.push(t);return e}initialize(){super.initialize(),this.renderRoot=this.createRenderRoot(),window.ShadowRoot&&this.renderRoot instanceof window.ShadowRoot&&this.adoptStyles()}createRenderRoot(){return this.attachShadow({mode:"open"})}adoptStyles(){const t=this.constructor._styles;0!==t.length&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow?X?this.renderRoot.adoptedStyleSheets=t.map(t=>t.styleSheet):this._needsShimAdoptedStyleSheets=!0:window.ShadyCSS.ScopingShim.prepareAdoptedCssText(t.map(t=>t.cssText),this.localName))}connectedCallback(){super.connectedCallback(),this.hasUpdated&&void 0!==window.ShadyCSS&&window.ShadyCSS.styleElement(this)}update(t){super.update(t);const e=this.render();e instanceof f&&this.constructor.render(e,this.renderRoot,{scopeName:this.localName,eventContext:this}),this._needsShimAdoptedStyleSheets&&(this._needsShimAdoptedStyleSheets=!1,this.constructor._styles.forEach(t=>{const e=document.createElement("style");e.textContent=t.cssText,this.renderRoot.appendChild(e)}))}render(){}}it.finalized=!0,it.render=(t,e,i)=>{const n=i.scopeName,r=A.has(e),o=e instanceof ShadowRoot&&M&&t instanceof f,a=o&&!q.has(n),c=a?document.createDocumentFragment():e;if(((t,e,i)=>{let n=A.get(e);void 0===n&&(s(e,e.firstChild),A.set(e,n=new _(Object.assign({templateFactory:$},i))),n.appendInto(e)),n.setValue(t),n.commit()})(t,c,Object.assign({templateFactory:U(n)},i)),a){const t=A.get(c);A.delete(c),t.value instanceof m&&I(c,t.value.template,n),s(e,e.firstChild),e.appendChild(c),A.set(e,t)}!r&&o&&window.ShadyCSS.styleElement(e.host)};var st=((t,...e)=>{const i=e.reduce((e,i,s)=>e+(t=>{if(t instanceof tt)return t.cssText;if("number"==typeof t)return t;throw new Error(`Value passed to 'css' function must be a 'css' function result: ${t}. Use 'unsafeCSS' to pass non-literal values, but\n take care to ensure page security.`)})(i)+t[s+1],t[0]);return new tt(i,Z)})`:host{--bc-font-size-heading:var(--banner-card-heading-size, 3em);--bc-font-size-entity-value:var(--banner-card-entity-value-size, 1.5em);--bc-font-size-media-title:var(--banner-card-media-title-size, 0.9em);--bc-spacing:var(--banner-card-spacing, 4px);--bc-button-size:var(--banner-card-button-size, 32px);--bc-heading-color-dark:var( - --banner-card-heading-color-dark, - var(--primary-text-color) - );--bc-heading-color-light:var(--banner-card-heading-color-light, #fff)}ha-card{display:flex;flex-direction:column;align-items:center}a{cursor:pointer}paper-icon-button{width:var(--bc-button-size);height:var(--bc-button-size);padding:var(--bc-spacing)}.heading{display:flex;justify-content:center;align-items:center;font-size:var(--bc-font-size-heading);font-weight:300;cursor:pointer}ha-icon.heading-icon{--iron-icon-width:1em;--iron-icon-height:1em;margin:0 var(--bc-spacing)}.overlay-strip{background:rgba(0,0,0,.3);overflow:hidden;width:100%}.entities{padding:calc(var(--bc-spacing) * 2) 0;color:#fff;display:grid}.entity-state{display:flex;flex-direction:column;align-items:center;margin-top:var(--bc-spacing);margin-bottom:var(--bc-spacing);box-shadow:-1px 0 0 0 #fff;width:100%}.media-title{flex:1 0;overflow:hidden;font-weight:300;font-size:var(--bc-font-size-media-title);white-space:nowrap;text-overflow:ellipsis}.media-controls{display:flex;flex:0 0 calc(var(--bc-button-size) * 3)}.entity-state.expand .entity-value{width:100%}.entity-state-left{margin-right:auto;margin-left:16px}.entity-state-right{margin-left:auto;margin-right:16px}.entity-name{font-weight:700;white-space:nowrap;padding-top:calc(var(--bc-spacing) * 2);padding-bottom:calc(var(--bc-spacing) * 2)}.entity-value{display:flex;width:100%;flex:1 0;font-size:var(--bc-font-size-entity-value);align-items:center;justify-content:center}.entity-value.error{display:inline-block;word-wrap:break-word;font-size:16px;width:90%}.entity-value ha-icon{color:#fff}mwc-button{--mdc-theme-primary:white}mwc-switch{--mdc-theme-secondary:white}`;function nt({state:t,attributes:e},i=!1){return"string"==typeof i&&e.hasOwnProperty(i)?e[i]:t}function rt(t){return"object"==typeof t?(e=t,i=t=>!1===t?null:t,Object.entries(e).reduce((t,[e,s])=>({...t,[e]:i(s,e)}),{})):{entity:t};var e,i}const ot={"=":(t,e)=>e.includes(t),">":(t,e)=>t>e[0],"<":(t,e)=>t!e.includes(t)};function at(t,e){if(["string","number","boolean"].includes(typeof t))return ot["="](e,[t]);if(Array.isArray(t)){const[i,...s]=t;return ot.hasOwnProperty(i)?ot[i](e,s):ot["="](e,s)}throw new Error(`Couldn't find a valid matching strategy for '${t}'`)}var ct="banner-card";!function(t){console.info(`%c${ct}: ${t}`,"font-weight: bold")}("0.9.0");const lt=/^(mdi|hass):/;function ht(t){return"string"==typeof t&&t.match(lt)}function dt(t,e=null){return e?E`${t}`:E`${t}`}class ut extends it{static get properties(){return{config:Object,color:String,entities:Array,entityValues:Array,rowSize:Number,_hass:Object}}static get styles(){return[st]}constructor(){super(),this.config={},this.entities=[],this._hass={}}setConfig(t){if(void 0===t.heading)throw new Error("You need to define a heading");if(this.entities=(t.entities||[]).map(rt),this.config=t,this.color=t.color||function(t,e,i){if(!t||"#"!==t[0])return i;if(3===(t=t.substring(1)).length){const[e,i,s]=t;t=[e,e,i,i,s,s].join("")}if(6!==t.length)return i;const s=[parseInt(t.slice(0,2),16)/255,parseInt(t.slice(2,4),16)/255,parseInt(t.slice(4,6),16)/255],[n,r,o]=s.map(t=>t<=.03928?t/12.92:Math.pow((t+.055)/1.055,2.4));return.2126*n+.7152*r+.0722*o>.179?i:e}(t.background,"var(--bc-heading-color-light)","var(--bc-heading-color-dark)"),void 0!==t.row_size){if(t.row_size<1)throw new Error("row_size must be at least 1");this.rowSize=t.row_size}this.rowSize=this.rowSize||3}set hass(t){this._hass=t,this.entityValues=this.entities.filter(e=>(function(t,e){if(!e.hasOwnProperty(t.entity))return!1;if(t.when){const{state:i,entity:s=t.entity,attributes:n}="string"==typeof t.when?{state:t.when}:t.when,r=e[s];return!(void 0!==i&&!at(i,r.state))&&Object.entries(n||{}).every(([t,e])=>{return at(e,r.attributes[t])})}return!0})(e,t.states)).map(t=>this.parseEntity(t))}parseEntity(t){const e=this._hass;if(!e.states.hasOwnProperty(t.entity))return{...t,error:"Entity not ready"};const i=e.states[t.entity],s=i.attributes,n={};if(t.map_state&&i.state in t.map_state){const e=t.map_state[i.state],s=typeof e;"string"===s?n.value=e:"object"===s&&Object.entries(e).forEach(([t,e])=>{n[t]=e})}const r={name:s.friendly_name,state:i.state,value:nt(i,t.attribute),unit:s.unit_of_measurement,attributes:s,domain:t.entity.split(".")[0]};return s.hasOwnProperty("current_position")&&(r.state=s.current_position),{...r,...t,...n}}grid(t=1){return"full"===t||t>this.rowSize?`grid-column: span ${this.rowSize};`:`grid-column: span ${t};`}_service(t,e,i){return()=>this._hass.callService(t,e,{entity_id:i})}render(){return E`${this.renderHeading()} ${this.renderEntities()}`}renderHeading(){let t=this.config.heading;if(!1===t)return null;Array.isArray(t)||(t=[t]);return E`

this.config.link&&this.navigate(this.config.link)} style=color:${this.color}>${t.map(t=>ht(t)?E``:E`${t}`)}

`}renderEntities(){return 0===this.entityValues.length?null:E`
${this.entityValues.map(t=>{if(t.error)return E`
${dt(t.error)} ${t.entity}
`;const e={...t,onClick:()=>this.openEntityPopover(t.entity)};if(t.action)return this.renderCustom({...e,action:()=>{const{service:e,...i}=t.action,[s,n]=e.split(".");this._hass.callService(s,n,{entity_id:t.entity,...i})}});if(!t.attribute)switch(t.domain){case"light":case"switch":case"input_boolean":return this.renderAsToggle(e);case"cover":return this.renderDomainCover(e);case"media_player":return this.renderDomainMediaPlayer(e)}return this.renderDomainDefault(e)})}
`}renderValue({icon:t,value:e,image:i,action:s,click:n},r){return t||ht(e)?E``:!0===i?E``:r()}renderDomainDefault({value:t,unit:e,image:i,icon:s,name:n,size:r,onClick:o}){const a=this.renderValue({icon:s,image:i,value:t,click:o},()=>E`${t} ${e}`);return E`${dt(n,o)} ${a}`}renderCustom({value:t,unit:e,action:i,image:s,icon:n,name:r,size:o,onClick:a}){const c=this.renderValue({icon:n,image:s,value:t,click:i},()=>E`${t} ${e}`);return E`
${dt(r,a)} ${c}
`}renderDomainMediaPlayer({onClick:t,attributes:e,size:i,name:s,state:n,entity:r,domain:o}){const a="playing"===n,c=a?"media_pause":"media_play",l=[e.media_artist,e.media_title].join(" – ");return E`
${dt(s,t)}
${l}
`}renderAsToggle({onClick:t,size:e,name:i,state:s,domain:n,entity:r}){return E`
${dt(i,t)}
`}renderDomainCover({onClick:t,size:e,name:i,state:s,entity:n}){const r="closed"===s||0===s,o="open"===s||100===s;return E`
${dt(i,t)}
`}getCardSize(){return 3}navigate(t){history.pushState(null,"",t),this.fire("location-changed",{replace:!0})}openEntityPopover(t){this.fire("hass-more-info",{entityId:t})}fire(t,e,i){i=i||{},e=null==e?{}:e;const s=new Event(t,{bubbles:void 0===i.bubbles||i.bubbles,cancelable:Boolean(i.cancelable),composed:void 0===i.composed||i.composed});return s.detail=e,this.dispatchEvent(s),s}}return window.customElements.define("banner-card",ut),ut}); diff --git a/www/community/button-card/.gitignore b/www/community/button-card/.gitignore deleted file mode 100644 index dcbcbde..0000000 --- a/www/community/button-card/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.js.gz diff --git a/www/community/button-card/button-card.js b/www/community/button-card/button-card.js index 77be32e..a8d06c6 100644 --- a/www/community/button-card/button-card.js +++ b/www/community/button-card/button-card.js @@ -1,1050 +1,8 @@ -function t(t, e, n, i) { - var s, - r = arguments.length, - a = r < 3 ? e : null === i ? i = Object.getOwnPropertyDescriptor(e, n) : i;if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) a = Reflect.decorate(t, e, n, i);else for (var o = t.length - 1; o >= 0; o--) (s = t[o]) && (a = (r < 3 ? s(a) : r > 3 ? s(e, n, a) : s(e, n)) || a);return r > 3 && a && Object.defineProperty(e, n, a), a; - /** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -}const e = new WeakMap(), - n = t => (...n) => { - const i = t(...n);return e.set(i, !0), i; -}, - i = t => "function" == typeof t && e.has(t), - s = void 0 !== window.customElements && void 0 !== window.customElements.polyfillWrapFlushCallback, - r = (t, e, n = null) => { - for (; e !== n;) { - const n = e.nextSibling;t.removeChild(e), e = n; +function t(t,e,i,n){var r,s=arguments.length,a=s<3?e:null===n?n=Object.getOwnPropertyDescriptor(e,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)a=Reflect.decorate(t,e,i,n);else for(var o=t.length-1;o>=0;o--)(r=t[o])&&(a=(s<3?r(a):s>3?r(e,i,a):r(e,i))||a);return s>3&&a&&Object.defineProperty(e,i,a),a}const e="undefined"!=typeof window&&null!=window.customElements&&void 0!==window.customElements.polyfillWrapFlushCallback,i=(t,e,i=null)=>{for(;e!==i;){const i=e.nextSibling;t.removeChild(e),e=i}},n=`{{lit-${String(Math.random()).slice(2)}}}`,r=`\x3c!--${n}--\x3e`,s=new RegExp(`${n}|${r}`);class a{constructor(t,e){this.parts=[],this.element=e;const i=[],r=[],a=document.createTreeWalker(e.content,133,null,!1);let l=0,u=-1,d=0;const{strings:p,values:{length:f}}=t;for(;d0;){const e=p[d],i=h.exec(e)[2],n=i.toLowerCase()+"$lit$",r=t.getAttribute(n);t.removeAttribute(n);const a=r.split(s);this.parts.push({type:"attribute",index:u,name:i,strings:a}),d+=a.length-1}}"TEMPLATE"===t.tagName&&(r.push(t),a.currentNode=t.content)}else if(3===t.nodeType){const e=t.data;if(e.indexOf(n)>=0){const n=t.parentNode,r=e.split(s),a=r.length-1;for(let e=0;e{const i=t.length-e.length;return i>=0&&t.slice(i)===e},l=t=>-1!==t.index,c=()=>document.createComment(""),h=/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;function u(t,e){const{element:{content:i},parts:n}=t,r=document.createTreeWalker(i,133,null,!1);let s=p(n),a=n[s],o=-1,l=0;const c=[];let h=null;for(;r.nextNode();){o++;const t=r.currentNode;for(t.previousSibling===h&&(h=null),e.has(t)&&(c.push(t),null===h&&(h=t)),null!==h&&l++;void 0!==a&&a.index===o;)a.index=null!==h?-1:a.index-l,s=p(n,s),a=n[s]}c.forEach((t=>t.parentNode.removeChild(t)))}const d=t=>{let e=11===t.nodeType?0:1;const i=document.createTreeWalker(t,133,null,!1);for(;i.nextNode();)e++;return e},p=(t,e=-1)=>{for(let i=e+1;i(...e)=>{const i=t(...e);return f.set(i,!0),i},m=t=>"function"==typeof t&&f.has(t),_={},b={};class y{constructor(t,e,i){this.__parts=[],this.template=t,this.processor=e,this.options=i}update(t){let e=0;for(const i of this.__parts)void 0!==i&&i.setValue(t[e]),e++;for(const i of this.__parts)void 0!==i&&i.commit()}_clone(){const t=e?this.template.element.content.cloneNode(!0):document.importNode(this.template.element.content,!0),i=[],n=this.template.parts,r=document.createTreeWalker(t,133,null,!1);let s,a=0,o=0,c=r.nextNode();for(;at}),w=` ${n} `;class S{constructor(t,e,i,n){this.strings=t,this.values=e,this.type=i,this.processor=n}getHTML(){const t=this.strings.length-1;let e="",i=!1;for(let s=0;s-1||i)&&-1===t.indexOf("--\x3e",a+1);const o=h.exec(t);e+=null===o?t+(i?w:r):t.substr(0,o.index)+o[1]+o[2]+"$lit$"+o[3]+n}return e+=this.strings[t],e}getTemplateElement(){const t=document.createElement("template");let e=this.getHTML();return void 0!==v&&(e=v.createHTML(e)),t.innerHTML=e,t}}const x=t=>null===t||!("object"==typeof t||"function"==typeof t),k=t=>Array.isArray(t)||!(!t||!t[Symbol.iterator]);class O{constructor(t,e,i){this.dirty=!0,this.element=t,this.name=e,this.strings=i,this.parts=[];for(let n=0;n{try{const t={get capture(){return N=!0,!1}};window.addEventListener("test",t,t),window.removeEventListener("test",t,t)}catch(t){}})();class P{constructor(t,e,i){this.value=void 0,this.__pendingValue=void 0,this.element=t,this.eventName=e,this.eventContext=i,this.__boundHandleEvent=t=>this.handleEvent(t)}setValue(t){this.__pendingValue=t}commit(){for(;m(this.__pendingValue);){const t=this.__pendingValue;this.__pendingValue=_,t(this)}if(this.__pendingValue===_)return;const t=this.__pendingValue,e=this.value,i=null==t||null!=e&&(t.capture!==e.capture||t.once!==e.once||t.passive!==e.passive),n=null!=t&&(null==e||i);i&&this.element.removeEventListener(this.eventName,this.__boundHandleEvent,this.__options),n&&(this.__options=j(t),this.element.addEventListener(this.eventName,this.__boundHandleEvent,this.__options)),this.value=t,this.__pendingValue=_}handleEvent(t){"function"==typeof this.value?this.value.call(this.eventContext||this.element,t):this.value.handleEvent(t)}}const j=t=>t&&(N?{capture:t.capture,passive:t.passive,once:t.once}:t.capture);function $(t){let e=H.get(t.type);void 0===e&&(e={stringsArray:new WeakMap,keyString:new Map},H.set(t.type,e));let i=e.stringsArray.get(t.strings);if(void 0!==i)return i;const r=t.strings.join(n);return i=e.keyString.get(r),void 0===i&&(i=new a(t,t.getTemplateElement()),e.keyString.set(r,i)),e.stringsArray.set(t.strings,i),i}const H=new Map,R=new WeakMap;const V=new class{handleAttributeExpressions(t,e,i,n){const r=e[0];if("."===r){return new E(t,e.slice(1),i).parts}if("@"===r)return[new P(t,e.slice(1),n.eventContext)];if("?"===r)return[new C(t,e.slice(1),i)];return new O(t,e,i).parts}handleTextExpression(t){return new M(t)}};"undefined"!=typeof window&&(window.litHtmlVersions||(window.litHtmlVersions=[])).push("1.3.0");const D=(t,...e)=>new S(t,e,"html",V),L=(t,e)=>`${t}--${e}`;let F=!0;void 0===window.ShadyCSS?F=!1:void 0===window.ShadyCSS.prepareTemplateDom&&(console.warn("Incompatible ShadyCSS version detected. Please update to at least @webcomponents/webcomponentsjs@2.0.2 and @webcomponents/shadycss@1.3.1."),F=!1);const I=t=>e=>{const i=L(e.type,t);let r=H.get(i);void 0===r&&(r={stringsArray:new WeakMap,keyString:new Map},H.set(i,r));let s=r.stringsArray.get(e.strings);if(void 0!==s)return s;const o=e.strings.join(n);if(s=r.keyString.get(o),void 0===s){const i=e.getTemplateElement();F&&window.ShadyCSS.prepareTemplateDom(i,t),s=new a(e,i),r.keyString.set(o,s)}return r.stringsArray.set(e.strings,s),s},z=["html","svg"],U=new Set,q=(t,e,i)=>{U.add(t);const n=i?i.element:document.createElement("template"),r=e.querySelectorAll("style"),{length:s}=r;if(0===s)return void window.ShadyCSS.prepareTemplateStyles(n,t);const a=document.createElement("style");for(let c=0;c{z.forEach((e=>{const i=H.get(L(e,t));void 0!==i&&i.keyString.forEach((t=>{const{element:{content:e}}=t,i=new Set;Array.from(e.querySelectorAll("style")).forEach((t=>{i.add(t)})),u(t,i)}))}))})(t);const o=n.content;i?function(t,e,i=null){const{element:{content:n},parts:r}=t;if(null==i)return void n.appendChild(e);const s=document.createTreeWalker(n,133,null,!1);let a=p(r),o=0,l=-1;for(;s.nextNode();)for(l++,s.currentNode===i&&(o=d(e),i.parentNode.insertBefore(e,i));-1!==a&&r[a].index===l;){if(o>0){for(;-1!==a;)r[a].index+=o,a=p(r,a);return}a=p(r,a)}}(i,a,o.firstChild):o.insertBefore(a,o.firstChild),window.ShadyCSS.prepareTemplateStyles(n,t);const l=o.querySelector("style");if(window.ShadyCSS.nativeShadow&&null!==l)e.insertBefore(l.cloneNode(!0),e.firstChild);else if(i){o.insertBefore(a,o.firstChild);const t=new Set;t.add(a),u(i,t)}};window.JSCompiler_renameProperty=(t,e)=>t;const Y={toAttribute(t,e){switch(e){case Boolean:return t?"":null;case Object:case Array:return null==t?t:JSON.stringify(t)}return t},fromAttribute(t,e){switch(e){case Boolean:return null!==t;case Number:return null===t?null:Number(t);case Object:case Array:return JSON.parse(t)}return t}},B=(t,e)=>e!==t&&(e==e||t==t),W={attribute:!0,type:String,converter:Y,reflect:!1,hasChanged:B};class G extends HTMLElement{constructor(){super(),this.initialize()}static get observedAttributes(){this.finalize();const t=[];return this._classProperties.forEach(((e,i)=>{const n=this._attributeNameForProperty(i,e);void 0!==n&&(this._attributeToPropertyMap.set(n,i),t.push(n))})),t}static _ensureClassProperties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_classProperties",this))){this._classProperties=new Map;const t=Object.getPrototypeOf(this)._classProperties;void 0!==t&&t.forEach(((t,e)=>this._classProperties.set(e,t)))}}static createProperty(t,e=W){if(this._ensureClassProperties(),this._classProperties.set(t,e),e.noAccessor||this.prototype.hasOwnProperty(t))return;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 r=this[t];this[e]=n,this.requestUpdateInternal(t,r,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(t){return this._classProperties&&this._classProperties.get(t)||W}static finalize(){const t=Object.getPrototypeOf(this);if(t.hasOwnProperty("finalized")||t.finalize(),this.finalized=!0,this._ensureClassProperties(),this._attributeToPropertyMap=new Map,this.hasOwnProperty(JSCompiler_renameProperty("properties",this))){const t=this.properties,e=[...Object.getOwnPropertyNames(t),..."function"==typeof Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(t):[]];for(const i of e)this.createProperty(i,t[i])}}static _attributeNameForProperty(t,e){const i=e.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof t?t.toLowerCase():void 0}static _valueHasChanged(t,e,i=B){return i(t,e)}static _propertyValueFromAttribute(t,e){const i=e.type,n=e.converter||Y,r="function"==typeof n?n:n.fromAttribute;return r?r(t,i):t}static _propertyValueToAttribute(t,e){if(void 0===e.reflect)return;const i=e.type,n=e.converter;return(n&&n.toAttribute||Y.toAttribute)(t,i)}initialize(){this._updateState=0,this._updatePromise=new Promise((t=>this._enableUpdatingResolver=t)),this._changedProperties=new Map,this._saveInstanceProperties(),this.requestUpdateInternal()}_saveInstanceProperties(){this.constructor._classProperties.forEach(((t,e)=>{if(this.hasOwnProperty(e)){const t=this[e];delete this[e],this._instanceProperties||(this._instanceProperties=new Map),this._instanceProperties.set(e,t)}}))}_applyInstanceProperties(){this._instanceProperties.forEach(((t,e)=>this[e]=t)),this._instanceProperties=void 0}connectedCallback(){this.enableUpdating()}enableUpdating(){void 0!==this._enableUpdatingResolver&&(this._enableUpdatingResolver(),this._enableUpdatingResolver=void 0)}disconnectedCallback(){}attributeChangedCallback(t,e,i){e!==i&&this._attributeToProperty(t,i)}_propertyToAttribute(t,e,i=W){const n=this.constructor,r=n._attributeNameForProperty(t,i);if(void 0!==r){const t=n._propertyValueToAttribute(e,i);if(void 0===t)return;this._updateState=8|this._updateState,null==t?this.removeAttribute(r):this.setAttribute(r,t),this._updateState=-9&this._updateState}}_attributeToProperty(t,e){if(8&this._updateState)return;const i=this.constructor,n=i._attributeToPropertyMap.get(t);if(void 0!==n){const t=i.getPropertyOptions(n);this._updateState=16|this._updateState,this[n]=i._propertyValueFromAttribute(e,t),this._updateState=-17&this._updateState}}requestUpdateInternal(t,e,i){let n=!0;if(void 0!==t){const r=this.constructor;i=i||r.getPropertyOptions(t),r._valueHasChanged(this[t],e,i.hasChanged)?(this._changedProperties.has(t)||this._changedProperties.set(t,e),!0!==i.reflect||16&this._updateState||(void 0===this._reflectingProperties&&(this._reflectingProperties=new Map),this._reflectingProperties.set(t,i))):n=!1}!this._hasRequestedUpdate&&n&&(this._updatePromise=this._enqueueUpdate())}requestUpdate(t,e){return this.requestUpdateInternal(t,e),this.updateComplete}async _enqueueUpdate(){this._updateState=4|this._updateState;try{await this._updatePromise}catch(e){}const t=this.performUpdate();return null!=t&&await t,!this._hasRequestedUpdate}get _hasRequestedUpdate(){return 4&this._updateState}get hasUpdated(){return 1&this._updateState}performUpdate(){if(!this._hasRequestedUpdate)return;this._instanceProperties&&this._applyInstanceProperties();let t=!1;const e=this._changedProperties;try{t=this.shouldUpdate(e),t?this.update(e):this._markUpdated()}catch(i){throw t=!1,this._markUpdated(),i}t&&(1&this._updateState||(this._updateState=1|this._updateState,this.firstUpdated(e)),this.updated(e))}_markUpdated(){this._changedProperties=new Map,this._updateState=-5&this._updateState}get updateComplete(){return this._getUpdateComplete()}_getUpdateComplete(){return this._updatePromise}shouldUpdate(t){return!0}update(t){void 0!==this._reflectingProperties&&this._reflectingProperties.size>0&&(this._reflectingProperties.forEach(((t,e)=>this._propertyToAttribute(e,this[e],t))),this._reflectingProperties=void 0),this._markUpdated()}updated(t){}firstUpdated(t){}}G.finalized=!0;const Z=(t,e)=>"method"===e.kind&&e.descriptor&&!("value"in e.descriptor)?Object.assign(Object.assign({},e),{finisher(i){i.createProperty(e.key,t)}}):{kind:"field",key:Symbol(),placement:"own",descriptor:{},initializer(){"function"==typeof e.initializer&&(this[e.key]=e.initializer.call(this))},finisher(i){i.createProperty(e.key,t)}};function J(t){return(e,i)=>void 0!==i?((t,e,i)=>{e.constructor.createProperty(i,t)})(t,e,i):Z(t,e)}const X=(t,e,i)=>{Object.defineProperty(e,i,t)},K=(t,e)=>({kind:"method",placement:"prototype",key:e.key,descriptor:t});const Q=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,tt=Symbol();class et{constructor(t,e){if(e!==tt)throw new Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t}get styleSheet(){return void 0===this._styleSheet&&(Q?(this._styleSheet=new CSSStyleSheet,this._styleSheet.replaceSync(this.cssText)):this._styleSheet=null),this._styleSheet}toString(){return this.cssText}}(window.litElementVersions||(window.litElementVersions=[])).push("2.4.0");const it={};class nt extends G{static getStyles(){return this.styles}static _getUniqueStyles(){if(this.hasOwnProperty(JSCompiler_renameProperty("_styles",this)))return;const t=this.getStyles();if(Array.isArray(t)){const e=(t,i)=>t.reduceRight(((t,i)=>Array.isArray(i)?e(i,t):(t.add(i),t)),i),i=e(t,new Set),n=[];i.forEach((t=>n.unshift(t))),this._styles=n}else this._styles=void 0===t?[]:[t];this._styles=this._styles.map((t=>{if(t instanceof CSSStyleSheet&&!Q){const e=Array.prototype.slice.call(t.cssRules).reduce(((t,e)=>t+e.cssText),"");return new et(String(e),tt)}return t}))}initialize(){super.initialize(),this.constructor._getUniqueStyles(),this.renderRoot=this.createRenderRoot(),window.ShadowRoot&&this.renderRoot instanceof window.ShadowRoot&&this.adoptStyles()}createRenderRoot(){return this.attachShadow({mode:"open"})}adoptStyles(){const t=this.constructor._styles;0!==t.length&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow?Q?this.renderRoot.adoptedStyleSheets=t.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):this._needsShimAdoptedStyleSheets=!0:window.ShadyCSS.ScopingShim.prepareAdoptedCssText(t.map((t=>t.cssText)),this.localName))}connectedCallback(){super.connectedCallback(),this.hasUpdated&&void 0!==window.ShadyCSS&&window.ShadyCSS.styleElement(this)}update(t){const e=this.render();super.update(t),e!==it&&this.constructor.render(e,this.renderRoot,{scopeName:this.localName,eventContext:this}),this._needsShimAdoptedStyleSheets&&(this._needsShimAdoptedStyleSheets=!1,this.constructor._styles.forEach((t=>{const e=document.createElement("style");e.textContent=t.cssText,this.renderRoot.appendChild(e)})))}render(){return it}}nt.finalized=!0,nt.render=(t,e,n)=>{if(!n||"object"!=typeof n||!n.scopeName)throw new Error("The `scopeName` option is required.");const r=n.scopeName,s=R.has(e),a=F&&11===e.nodeType&&!!e.host,o=a&&!U.has(r),l=o?document.createDocumentFragment():e;if(((t,e,n)=>{let r=R.get(e);void 0===r&&(i(e,e.firstChild),R.set(e,r=new M(Object.assign({templateFactory:$},n))),r.appendInto(e)),r.setValue(t),r.commit()})(t,l,Object.assign({templateFactory:I(r)},n)),o){const t=R.get(l);R.delete(l);const n=t.value instanceof y?t.value.template:void 0;q(r,l,n),i(e,e.firstChild),e.appendChild(l),R.set(e,t)}!s&&a&&window.ShadyCSS.styleElement(e.host)};class rt{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()}))}}}const st=new WeakMap,at=g((t=>e=>{if(!(e instanceof T)||e instanceof A||"style"!==e.committer.name||e.committer.parts.length>1)throw new Error("The `styleMap` directive must be used in the style attribute and must be the only part in the attribute.");const{committer:i}=e,{style:n}=i.element;let r=st.get(e);void 0===r&&(n.cssText=i.strings.join(" "),st.set(e,r=new Set)),r.forEach((e=>{e in t||(r.delete(e),-1===e.indexOf("-")?n[e]=null:n.removeProperty(e))}));for(const s in t)r.add(s),-1===s.indexOf("-")?n[s]=t[s]:n.setProperty(s,t[s])})),ot=new WeakMap,lt=g((t=>e=>{if(!(e instanceof M))throw new Error("unsafeHTML can only be used in text bindings");const i=ot.get(e);if(void 0!==i&&x(t)&&t===i.value&&e.value===i.fragment)return;const n=document.createElement("template");n.innerHTML=t;const r=document.importNode(n.content,!0);e.setValue(r),ot.set(e,{value:t,fragment:r})}));class ct{constructor(t){this.classes=new Set,this.changed=!1,this.element=t;const e=(t.getAttribute("class")||"").split(/\s+/);for(const i of e)this.classes.add(i)}add(t){this.classes.add(t),this.changed=!0}remove(t){this.classes.delete(t),this.changed=!0}commit(){if(this.changed){let t="";this.classes.forEach((e=>t+=e+" ")),this.element.setAttribute("class",t)}}}const ht=new WeakMap,ut=g((t=>e=>{if(!(e instanceof T)||e instanceof A||"class"!==e.committer.name||e.committer.parts.length>1)throw new Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.");const{committer:i}=e,{element:n}=i;let r=ht.get(e);void 0===r&&(n.setAttribute("class",i.strings.join(" ")),ht.set(e,r=new Set));const s=n.classList||new ct(n);r.forEach((e=>{e in t||(s.remove(e),r.delete(e))}));for(const a in t){const e=t[a];e!=r.has(a)&&(e?(s.add(a),r.add(a)):(s.remove(a),r.delete(a)))}"function"==typeof s.commit&&s.commit()}));var dt=/d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|Z|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g,pt="[^\\s]+",ft=/\[([^]*?)\]/gm;function gt(t,e){for(var i=[],n=0,r=t.length;n-1?n:null}};function _t(t){for(var e=[],i=1;i3?0:(t-t%10!=10?1:0)*t%10]}},St=_t({},wt),xt=function(t,e){for(void 0===e&&(e=2),t=String(t);t.length0?"-":"+")+xt(100*Math.floor(Math.abs(e)/60)+Math.abs(e)%60,4)},Z:function(t){var e=t.getTimezoneOffset();return(e>0?"-":"+")+xt(Math.floor(Math.abs(e)/60),2)+":"+xt(Math.abs(e)%60,2)}},Ot=function(t){return+t-1},Tt=[null,"[1-9]\\d?"],Mt=[null,pt],Ct=["isPm",pt,function(t,e){var i=t.toLowerCase();return i===e.amPm[0]?0:i===e.amPm[1]?1:null}],Et=["timezoneOffset","[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z?",function(t){var e=(t+"").match(/([+-]|\d\d)/gi);if(e){var i=60*+e[1]+parseInt(e[2],10);return"+"===e[0]?i:-i}return 0}],At=(mt("monthNamesShort"),mt("monthNames"),{default:"ddd MMM DD YYYY HH:mm:ss",shortDate:"M/D/YY",mediumDate:"MMM D, YYYY",longDate:"MMMM D, YYYY",fullDate:"dddd, MMMM D, YYYY",isoDate:"YYYY-MM-DD",isoDateTime:"YYYY-MM-DDTHH:mm:ssZ",shortTime:"HH:mm",mediumTime:"HH:mm:ss",longTime:"HH:mm:ss.SSS"});var Nt=function(t,e,i){if(void 0===e&&(e=At.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 n=[];e=(e=At[e]||e).replace(ft,(function(t,e){return n.push(e),"@@@"}));var r=_t(_t({},St),i);return(e=e.replace(dt,(function(e){return kt[e](t,r)}))).replace(/@@@/g,(function(){return n.shift()}))};function Pt(t){var e=t.split(":").map(Number);return 3600*e[0]+60*e[1]+e[2]}var jt=function(){try{(new Date).toLocaleDateString("i")}catch(t){return"RangeError"===t.name}return!1}()?function(t,e){return t.toLocaleDateString(e,{year:"numeric",month:"long",day:"numeric"})}:function(t){return Nt(t,"mediumDate")},$t=function(){try{(new Date).toLocaleString("i")}catch(t){return"RangeError"===t.name}return!1}()?function(t,e){return t.toLocaleString(e,{year:"numeric",month:"long",day:"numeric",hour:"numeric",minute:"2-digit"})}:function(t){return Nt(t,"haDateTime")},Ht=function(){try{(new Date).toLocaleTimeString("i")}catch(t){return"RangeError"===t.name}return!1}()?function(t,e){return t.toLocaleTimeString(e,{hour:"numeric",minute:"2-digit"})}:function(t){return Nt(t,"shortTime")},Rt=function(t){return t<10?"0"+t:t};function Vt(t){return t.substr(0,t.indexOf("."))}var Dt="hass:bookmark",Lt=["closed","locked","off"],Ft=new Set(["fan","input_boolean","light","switch","group","automation"]),It=function(t,e,i,n){n=n||{},i=null==i?{}:i;var r=new Event(e,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed});return r.detail=i,t.dispatchEvent(r),r},zt=new Set(["call-service","divider","section","weblink","cast","select"]),Ut={alert:"toggle",automation:"toggle",climate:"climate",cover:"cover",fan:"toggle",group:"group",input_boolean:"toggle",input_number:"input-number",input_select:"input-select",input_text:"input-text",light:"toggle",lock:"lock",media_player:"media-player",remote:"toggle",scene:"scene",script:"script",sensor:"sensor",timer:"timer",switch:"toggle",vacuum:"toggle",water_heater:"climate",input_datetime:"input-datetime"},qt={alert:"hass:alert",automation:"hass:playlist-play",calendar:"hass:calendar",camera:"hass:video",climate:"hass:thermostat",configurator:"hass:settings",conversation:"hass:text-to-speech",device_tracker:"hass:account",fan:"hass:fan",group:"hass:google-circles-communities",history_graph:"hass:chart-line",homeassistant:"hass:home-assistant",homekit:"hass:home-automation",image_processing:"hass:image-filter-frames",input_boolean:"hass:drawing",input_datetime:"hass:calendar-clock",input_number:"hass:ray-vertex",input_select:"hass:format-list-bulleted",input_text:"hass:textbox",light:"hass:lightbulb",mailbox:"hass:mailbox",notify:"hass:comment-alert",person:"hass:account",plant:"hass:flower",proximity:"hass:apple-safari",remote:"hass:remote",scene:"hass:google-pages",script:"hass:file-document",sensor:"hass:eye",simple_alarm:"hass:bell",sun:"hass:white-balance-sunny",switch:"hass:flash",timer:"hass:timer",updater:"hass:cloud-upload",vacuum:"hass:robot-vacuum",water_heater:"hass:thermometer",weblink:"hass:open-in-new"};function Yt(t,e){if(t in qt)return qt[t];switch(t){case"alarm_control_panel":switch(e){case"armed_home":return"hass:bell-plus";case"armed_night":return"hass:bell-sleep";case"disarmed":return"hass:bell-outline";case"triggered":return"hass:bell-ring";default:return"hass:bell"}case"binary_sensor":return e&&"off"===e?"hass:radiobox-blank":"hass:checkbox-marked-circle";case"cover":return"closed"===e?"hass:window-closed":"hass:window-open";case"lock":return e&&"unlocked"===e?"hass:lock-open":"hass:lock";case"media_player":return e&&"off"!==e&&"idle"!==e?"hass:cast-connected":"hass:cast";case"zwave":switch(e){case"dead":return"hass:emoticon-dead";case"sleeping":return"hass:sleep";case"initializing":return"hass:timer-sand";default:return"hass:z-wave"}default:return console.warn("Unable to find icon for domain "+t+" ("+e+")"),Dt}}var Bt=function(t){It(window,"haptic",t)},Wt=function(t,e){return function(t,e,i){void 0===i&&(i=!0);var n,r=Vt(e),s="group"===r?"homeassistant":r;switch(r){case"lock":n=i?"unlock":"lock";break;case"cover":n=i?"open_cover":"close_cover";break;default:n=i?"turn_on":"turn_off"}return t.callService(s,n,{entity_id:e})}(t,e,Lt.includes(t.states[e].state))},Gt=function(t,e,i,n,r){var s;if(r&&i.double_tap_action?s=i.double_tap_action:n&&i.hold_action?s=i.hold_action:!n&&i.tap_action&&(s=i.tap_action),s||(s={action:"more-info"}),!s.confirmation||s.confirmation.exemptions&&s.confirmation.exemptions.some((function(t){return t.user===e.user.id}))||confirm(s.confirmation.text||"Are you sure you want to "+s.action+"?"))switch(s.action){case"more-info":(s.entity||i.entity||i.camera_image)&&(It(t,"hass-more-info",{entityId:s.entity?s.entity:i.entity?i.entity:i.camera_image}),s.haptic&&Bt(s.haptic));break;case"navigate":s.navigation_path&&(function(t,e,i){void 0===i&&(i=!1),i?history.replaceState(null,"",e):history.pushState(null,"",e),It(window,"location-changed",{replace:i})}(0,s.navigation_path),s.haptic&&Bt(s.haptic));break;case"url":s.url_path&&window.open(s.url_path),s.haptic&&Bt(s.haptic);break;case"toggle":i.entity&&(Wt(e,i.entity),s.haptic&&Bt(s.haptic));break;case"call-service":if(!s.service)return;var a=s.service.split(".",2),o=a[0],l=a[1],c=Object.assign({},s.service_data);"entity"===c.entity_id&&(c.entity_id=i.entity),e.callService(o,l,c),s.haptic&&Bt(s.haptic)}},Zt={humidity:"hass:water-percent",illuminance:"hass:brightness-5",temperature:"hass:thermometer",pressure:"hass:gauge",power:"hass:flash",signal_strength:"hass:wifi"},Jt={binary_sensor:function(t){var e=t.state&&"off"===t.state;switch(t.attributes.device_class){case"battery":return e?"hass:battery":"hass:battery-outline";case"cold":return e?"hass:thermometer":"hass:snowflake";case"connectivity":return e?"hass:server-network-off":"hass:server-network";case"door":return e?"hass:door-closed":"hass:door-open";case"garage_door":return e?"hass:garage":"hass:garage-open";case"gas":case"power":case"problem":case"safety":case"smoke":return e?"hass:shield-check":"hass:alert";case"heat":return e?"hass:thermometer":"hass:fire";case"light":return e?"hass:brightness-5":"hass:brightness-7";case"lock":return e?"hass:lock":"hass:lock-open";case"moisture":return e?"hass:water-off":"hass:water";case"motion":return e?"hass:walk":"hass:run";case"occupancy":return e?"hass:home-outline":"hass:home";case"opening":return e?"hass:square":"hass:square-outline";case"plug":return e?"hass:power-plug-off":"hass:power-plug";case"presence":return e?"hass:home-outline":"hass:home";case"sound":return e?"hass:music-note-off":"hass:music-note";case"vibration":return e?"hass:crop-portrait":"hass:vibrate";case"window":return e?"hass:window-closed":"hass:window-open";default:return e?"hass:radiobox-blank":"hass:checkbox-marked-circle"}},cover:function(t){var e="closed"!==t.state;switch(t.attributes.device_class){case"garage":return e?"hass:garage-open":"hass:garage";case"door":return e?"hass:door-open":"hass:door-closed";case"shutter":return e?"hass:window-shutter-open":"hass:window-shutter";case"blind":return e?"hass:blinds-open":"hass:blinds";case"window":return e?"hass:window-open":"hass:window-closed";default:return Yt("cover",t.state)}},sensor:function(t){var e=t.attributes.device_class;if(e&&e in Zt)return Zt[e];if("battery"===e){var i=Number(t.state);if(isNaN(i))return"hass:battery-unknown";var n=10*Math.round(i/10);return n>=100?"hass:battery":n<=0?"hass:battery-alert":"hass:battery-"+n}var r=t.attributes.unit_of_measurement;return"°C"===r||"°F"===r?"hass:thermometer":Yt("sensor")},input_datetime:function(t){return t.attributes.has_date?t.attributes.has_time?Yt("input_datetime"):"hass:calendar":"hass:clock"}};const Xt=(t,e,i,n)=>{n=n||{},i=null==i?{}:i;const r=new Event(e,{bubbles:void 0===n.bubbles||n.bubbles,cancelable:Boolean(n.cancelable),composed:void 0===n.composed||n.composed});return r.detail=i,t.dispatchEvent(r),r},Kt=(t,e)=>{if(t===e)return!0;if(t&&e&&"object"==typeof t&&"object"==typeof e){if(t.constructor!==e.constructor)return!1;let i,n;if(Array.isArray(t)){if(n=t.length,n!==e.length)return!1;for(i=n;0!=i--;)if(!Kt(t[i],e[i]))return!1;return!0}if(t instanceof Map&&e instanceof Map){if(t.size!==e.size)return!1;for(i of t.entries())if(!e.has(i[0]))return!1;for(i of t.entries())if(!Kt(i[1],e.get(i[0])))return!1;return!0}if(t instanceof Set&&e instanceof Set){if(t.size!==e.size)return!1;for(i of t.entries())if(!e.has(i[0]))return!1;return!0}if(ArrayBuffer.isView(t)&&ArrayBuffer.isView(e)){if(n=t.length,n!==e.length)return!1;for(i=n;0!=i--;)if(t[i]!==e[i])return!1;return!0}if(t.constructor===RegExp)return t.source===e.source&&t.flags===e.flags;if(t.valueOf!==Object.prototype.valueOf)return t.valueOf()===e.valueOf();if(t.toString!==Object.prototype.toString)return t.toString()===e.toString();const r=Object.keys(t);if(n=r.length,n!==Object.keys(e).length)return!1;for(i=n;0!=i--;)if(!Object.prototype.hasOwnProperty.call(e,r[i]))return!1;for(i=n;0!=i--;){const n=r[i];if(!Kt(t[n],e[n]))return!1}return!0}return t!=t&&e!=e},Qt="ontouchstart"in window||navigator.maxTouchPoints>0||navigator.msMaxTouchPoints>0;class te extends HTMLElement{constructor(){super(),this.holdTime=500,this.held=!1,this.cancelled=!1,this.isRepeating=!1,this.ripple=document.createElement("mwc-ripple")}connectedCallback(){Object.assign(this.style,{position:"absolute",width:Qt?"100px":"50px",height:Qt?"100px":"50px",transform:"translate(-50%, -50%)",pointerEvents:"none",zIndex:"999"}),this.appendChild(this.ripple),this.ripple.primary=!0,["touchcancel","mouseout","mouseup","touchmove","mousewheel","wheel","scroll"].forEach((t=>{document.addEventListener(t,(()=>{this.cancelled=!0,this.timer&&(this.stopAnimation(),clearTimeout(this.timer),this.timer=void 0,this.isRepeating&&this.repeatTimeout&&(clearInterval(this.repeatTimeout),this.isRepeating=!1))}),{passive:!0})}))}bind(t,e){t.actionHandler&&Kt(e,t.actionHandler.options)||(t.actionHandler?(t.removeEventListener("touchstart",t.actionHandler.start),t.removeEventListener("touchend",t.actionHandler.end),t.removeEventListener("touchcancel",t.actionHandler.end),t.removeEventListener("mousedown",t.actionHandler.start),t.removeEventListener("click",t.actionHandler.end),t.removeEventListener("keyup",t.actionHandler.handleEnter)):t.addEventListener("contextmenu",(t=>{const e=t||window.event;return e.preventDefault&&e.preventDefault(),e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0,e.returnValue=!1,!1})),t.actionHandler={options:e},e.disabled||(t.actionHandler.start=i=>{let n,r;this.cancelled=!1,i.touches?(n=i.touches[0].pageX,r=i.touches[0].pageY):(n=i.pageX,r=i.pageY),e.hasHold&&(this.held=!1,this.timer=window.setTimeout((()=>{this.startAnimation(n,r),this.held=!0,e.repeat&&!this.isRepeating&&(this.isRepeating=!0,this.repeatTimeout=setInterval((()=>{Xt(t,"action",{action:"hold"})}),e.repeat))}),this.holdTime))},t.actionHandler.end=t=>{if(["touchend","touchcancel"].includes(t.type)&&this.cancelled)return void(this.isRepeating&&this.repeatTimeout&&(clearInterval(this.repeatTimeout),this.isRepeating=!1));const i=t.target;t.cancelable&&t.preventDefault(),e.hasHold&&(clearTimeout(this.timer),this.isRepeating&&this.repeatTimeout&&clearInterval(this.repeatTimeout),this.isRepeating=!1,this.stopAnimation(),this.timer=void 0),e.hasHold&&this.held?e.repeat||Xt(i,"action",{action:"hold"}):e.hasDoubleClick?"click"===t.type&&t.detail<2||!this.dblClickTimeout?this.dblClickTimeout=window.setTimeout((()=>{this.dblClickTimeout=void 0,Xt(i,"action",{action:"tap"})}),250):(clearTimeout(this.dblClickTimeout),this.dblClickTimeout=void 0,Xt(i,"action",{action:"double_tap"})):Xt(i,"action",{action:"tap"})},t.actionHandler.handleEnter=t=>{13===t.keyCode&&t.currentTarget.actionHandler.end(t)},t.addEventListener("touchstart",t.actionHandler.start,{passive:!0}),t.addEventListener("touchend",t.actionHandler.end),t.addEventListener("touchcancel",t.actionHandler.end),t.addEventListener("mousedown",t.actionHandler.start,{passive:!0}),t.addEventListener("click",t.actionHandler.end),t.addEventListener("keyup",t.actionHandler.handleEnter)))}startAnimation(t,e){Object.assign(this.style,{left:t+"px",top:e+"px",display:null}),this.ripple.disabled=!1,this.ripple.startPress(),this.ripple.unbounded=!0}stopAnimation(){this.ripple.endPress(),this.ripple.disabled=!0,this.style.display="none"}}customElements.define("button-card-action-handler",te);const ee=(t,e)=>{const i=(()=>{const t=document.body;if(t.querySelector("button-card-action-handler"))return t.querySelector("button-card-action-handler");const e=document.createElement("button-card-action-handler");return t.appendChild(e),e})();i&&i.bind(t,e)},ie=g(((t={})=>e=>{ee(e.committer.element,t)}));function ne(t,e){(function(t){return"string"==typeof t&&t.includes(".")&&1===parseFloat(t)})(t)&&(t="100%");var i=function(t){return"string"==typeof t&&t.includes("%")}(t);return t=360===e?t:Math.min(e,Math.max(0,parseFloat(t))),i&&(t=parseInt(String(t*e),10)/100),Math.abs(t-e)<1e-6?1:t=360===e?(t<0?t%e+e:t%e)/parseFloat(String(e)):t%e/parseFloat(String(e))}function re(t){return Math.min(1,Math.max(0,t))}function se(t){return t=parseFloat(t),(isNaN(t)||t<0||t>1)&&(t=1),t}function ae(t){return t<=1?100*Number(t)+"%":t}function oe(t){return 1===t.length?"0"+t:String(t)}function le(t,e,i){t=ne(t,255),e=ne(e,255),i=ne(i,255);var n=Math.max(t,e,i),r=Math.min(t,e,i),s=0,a=0,o=(n+r)/2;if(n===r)a=0,s=0;else{var l=n-r;switch(a=o>.5?l/(2-n-r):l/(n+r),n){case t:s=(e-i)/l+(e1&&(i-=1),i<1/6?t+6*i*(e-t):i<.5?e:i<2/3?t+(e-t)*(2/3-i)*6:t}function he(t,e,i){t=ne(t,255),e=ne(e,255),i=ne(i,255);var n=Math.max(t,e,i),r=Math.min(t,e,i),s=0,a=n,o=n-r,l=0===n?0:o/n;if(n===r)s=0;else{switch(n){case t:s=(e-i)/o+(e>16,g:(65280&t)>>8,b:255&t}}(e)),this.originalInput=e;var r=me(e);this.originalInput=e,this.r=r.r,this.g=r.g,this.b=r.b,this.a=r.a,this.roundA=Math.round(100*this.a)/100,this.format=null!==(n=i.format)&&void 0!==n?n:r.format,this.gradientType=i.gradientType,this.r<1&&(this.r=Math.round(this.r)),this.g<1&&(this.g=Math.round(this.g)),this.b<1&&(this.b=Math.round(this.b)),this.isValid=r.ok}return t.prototype.isDark=function(){return this.getBrightness()<128},t.prototype.isLight=function(){return!this.isDark()},t.prototype.getBrightness=function(){var t=this.toRgb();return(299*t.r+587*t.g+114*t.b)/1e3},t.prototype.getLuminance=function(){var t=this.toRgb(),e=t.r/255,i=t.g/255,n=t.b/255;return.2126*(e<=.03928?e/12.92:Math.pow((e+.055)/1.055,2.4))+.7152*(i<=.03928?i/12.92:Math.pow((i+.055)/1.055,2.4))+.0722*(n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4))},t.prototype.getAlpha=function(){return this.a},t.prototype.setAlpha=function(t){return this.a=se(t),this.roundA=Math.round(100*this.a)/100,this},t.prototype.toHsv=function(){var t=he(this.r,this.g,this.b);return{h:360*t.h,s:t.s,v:t.v,a:this.a}},t.prototype.toHsvString=function(){var t=he(this.r,this.g,this.b),e=Math.round(360*t.h),i=Math.round(100*t.s),n=Math.round(100*t.v);return 1===this.a?"hsv("+e+", "+i+"%, "+n+"%)":"hsva("+e+", "+i+"%, "+n+"%, "+this.roundA+")"},t.prototype.toHsl=function(){var t=le(this.r,this.g,this.b);return{h:360*t.h,s:t.s,l:t.l,a:this.a}},t.prototype.toHslString=function(){var t=le(this.r,this.g,this.b),e=Math.round(360*t.h),i=Math.round(100*t.s),n=Math.round(100*t.l);return 1===this.a?"hsl("+e+", "+i+"%, "+n+"%)":"hsla("+e+", "+i+"%, "+n+"%, "+this.roundA+")"},t.prototype.toHex=function(t){return void 0===t&&(t=!1),ue(this.r,this.g,this.b,t)},t.prototype.toHexString=function(t){return void 0===t&&(t=!1),"#"+this.toHex(t)},t.prototype.toHex8=function(t){return void 0===t&&(t=!1),function(t,e,i,n,r){var s=[oe(Math.round(t).toString(16)),oe(Math.round(e).toString(16)),oe(Math.round(i).toString(16)),oe(de(n))];return r&&s[0].startsWith(s[0].charAt(1))&&s[1].startsWith(s[1].charAt(1))&&s[2].startsWith(s[2].charAt(1))&&s[3].startsWith(s[3].charAt(1))?s[0].charAt(0)+s[1].charAt(0)+s[2].charAt(0)+s[3].charAt(0):s.join("")}(this.r,this.g,this.b,this.a,t)},t.prototype.toHex8String=function(t){return void 0===t&&(t=!1),"#"+this.toHex8(t)},t.prototype.toRgb=function(){return{r:Math.round(this.r),g:Math.round(this.g),b:Math.round(this.b),a:this.a}},t.prototype.toRgbString=function(){var t=Math.round(this.r),e=Math.round(this.g),i=Math.round(this.b);return 1===this.a?"rgb("+t+", "+e+", "+i+")":"rgba("+t+", "+e+", "+i+", "+this.roundA+")"},t.prototype.toPercentageRgb=function(){var t=function(t){return Math.round(100*ne(t,255))+"%"};return{r:t(this.r),g:t(this.g),b:t(this.b),a:this.a}},t.prototype.toPercentageRgbString=function(){var t=function(t){return Math.round(100*ne(t,255))};return 1===this.a?"rgb("+t(this.r)+"%, "+t(this.g)+"%, "+t(this.b)+"%)":"rgba("+t(this.r)+"%, "+t(this.g)+"%, "+t(this.b)+"%, "+this.roundA+")"},t.prototype.toName=function(){if(0===this.a)return"transparent";if(this.a<1)return!1;for(var t="#"+ue(this.r,this.g,this.b,!1),e=0,i=Object.entries(ge);e=0;return e||!n||!t.startsWith("hex")&&"name"!==t?("rgb"===t&&(i=this.toRgbString()),"prgb"===t&&(i=this.toPercentageRgbString()),"hex"!==t&&"hex6"!==t||(i=this.toHexString()),"hex3"===t&&(i=this.toHexString(!0)),"hex4"===t&&(i=this.toHex8String(!0)),"hex8"===t&&(i=this.toHex8String()),"name"===t&&(i=this.toName()),"hsl"===t&&(i=this.toHslString()),"hsv"===t&&(i=this.toHsvString()),i||this.toHexString()):"name"===t&&0===this.a?this.toName():this.toRgbString()},t.prototype.toNumber=function(){return(Math.round(this.r)<<16)+(Math.round(this.g)<<8)+Math.round(this.b)},t.prototype.clone=function(){return new t(this.toString())},t.prototype.lighten=function(e){void 0===e&&(e=10);var i=this.toHsl();return i.l+=e/100,i.l=re(i.l),new t(i)},t.prototype.brighten=function(e){void 0===e&&(e=10);var i=this.toRgb();return i.r=Math.max(0,Math.min(255,i.r-Math.round(-e/100*255))),i.g=Math.max(0,Math.min(255,i.g-Math.round(-e/100*255))),i.b=Math.max(0,Math.min(255,i.b-Math.round(-e/100*255))),new t(i)},t.prototype.darken=function(e){void 0===e&&(e=10);var i=this.toHsl();return i.l-=e/100,i.l=re(i.l),new t(i)},t.prototype.tint=function(t){return void 0===t&&(t=10),this.mix("white",t)},t.prototype.shade=function(t){return void 0===t&&(t=10),this.mix("black",t)},t.prototype.desaturate=function(e){void 0===e&&(e=10);var i=this.toHsl();return i.s-=e/100,i.s=re(i.s),new t(i)},t.prototype.saturate=function(e){void 0===e&&(e=10);var i=this.toHsl();return i.s+=e/100,i.s=re(i.s),new t(i)},t.prototype.greyscale=function(){return this.desaturate(100)},t.prototype.spin=function(e){var i=this.toHsl(),n=(i.h+e)%360;return i.h=n<0?360+n:n,new t(i)},t.prototype.mix=function(e,i){void 0===i&&(i=50);var n=this.toRgb(),r=new t(e).toRgb(),s=i/100;return new t({r:(r.r-n.r)*s+n.r,g:(r.g-n.g)*s+n.g,b:(r.b-n.b)*s+n.b,a:(r.a-n.a)*s+n.a})},t.prototype.analogous=function(e,i){void 0===e&&(e=6),void 0===i&&(i=30);var n=this.toHsl(),r=360/i,s=[this];for(n.h=(n.h-(r*e>>1)+720)%360;--e;)n.h=(n.h+r)%360,s.push(new t(n));return s},t.prototype.complement=function(){var e=this.toHsl();return e.h=(e.h+180)%360,new t(e)},t.prototype.monochromatic=function(e){void 0===e&&(e=6);for(var i=this.toHsv(),n=i.h,r=i.s,s=i.v,a=[],o=1/e;e--;)a.push(new t({h:n,s:r,v:s})),s=(s+o)%1;return a},t.prototype.splitcomplement=function(){var e=this.toHsl(),i=e.h;return[this,new t({h:(i+72)%360,s:e.s,l:e.l}),new t({h:(i+216)%360,s:e.s,l:e.l})]},t.prototype.triad=function(){return this.polyad(3)},t.prototype.tetrad=function(){return this.polyad(4)},t.prototype.polyad=function(e){for(var i=this.toHsl(),n=i.h,r=[this],s=360/e,a=1;at&&"object"==typeof t;return t.reduce(((t,i)=>(Object.keys(i).forEach((n=>{const r=t[n],s=i[n];Array.isArray(r)&&Array.isArray(s)?t[n]=r.concat(...s):e(r)&&e(s)?t[n]=Me(r,s):t[n]=s})),t)),{})}function Ce(t,e){let i=[];return t&&t.forEach((t=>{let n=t;e&&e.forEach((e=>{e.id&&t.id&&e.id==t.id&&(n=Me(n,e))})),i.push(n)})),e&&(i=i.concat(e.filter((e=>!t||!t.find((t=>!(!t.id||!e.id)&&t.id==e.id)))))),i}const Ee=((t,...e)=>{const i=e.reduce(((e,i,n)=>e+(t=>{if(t instanceof et)return t.cssText;if("number"==typeof t)return t;throw new Error(`Value passed to 'css' function must be a 'css' function result: ${t}. Use 'unsafeCSS' to pass non-literal values, but\n take care to ensure page security.`)})(i)+t[n+1]),t[0]);return new et(i,tt)})` + :host { + position: relative; + display: block; } -}, - a = {}, - o = {}, - l = `{{lit-${String(Math.random()).slice(2)}}}`, - c = `\x3c!--${l}--\x3e`, - h = new RegExp(`${l}|${c}`);class u { - constructor(t, e) { - this.parts = [], this.element = e;const n = [], - i = [], - s = document.createTreeWalker(e.content, 133, null, !1);let r = 0, - a = -1, - o = 0;const { strings: c, values: { length: u } } = t;for (; o < u;) { - const t = s.nextNode();if (null !== t) { - if (a++, 1 === t.nodeType) { - if (t.hasAttributes()) { - const e = t.attributes, - { length: n } = e;let i = 0;for (let t = 0; t < n; t++) d(e[t].name, "$lit$") && i++;for (; i-- > 0;) { - const e = c[o], - n = m.exec(e)[2], - i = n.toLowerCase() + "$lit$", - s = t.getAttribute(i);t.removeAttribute(i);const r = s.split(h);this.parts.push({ type: "attribute", index: a, name: n, strings: r }), o += r.length - 1; - } - }"TEMPLATE" === t.tagName && (i.push(t), s.currentNode = t.content); - } else if (3 === t.nodeType) { - const e = t.data;if (e.indexOf(l) >= 0) { - const i = t.parentNode, - s = e.split(h), - r = s.length - 1;for (let e = 0; e < r; e++) { - let n, - r = s[e];if ("" === r) n = p();else { - const t = m.exec(r);null !== t && d(t[2], "$lit$") && (r = r.slice(0, t.index) + t[1] + t[2].slice(0, -"$lit$".length) + t[3]), n = document.createTextNode(r); - }i.insertBefore(n, t), this.parts.push({ type: "node", index: ++a }); - }"" === s[r] ? (i.insertBefore(p(), t), n.push(t)) : t.data = s[r], o += r; - } - } else if (8 === t.nodeType) if (t.data === l) { - const e = t.parentNode;null !== t.previousSibling && a !== r || (a++, e.insertBefore(p(), t)), r = a, this.parts.push({ type: "node", index: a }), null === t.nextSibling ? t.data = "" : (n.push(t), a--), o++; - } else { - let e = -1;for (; -1 !== (e = t.data.indexOf(l, e + 1));) this.parts.push({ type: "node", index: -1 }), o++; - } - } else s.currentNode = i.pop(); - }for (const l of n) l.parentNode.removeChild(l); - } -}const d = (t, e) => { - const n = t.length - e.length;return n >= 0 && t.slice(n) === e; -}, - f = t => -1 !== t.index, - p = () => document.createComment(""), - m = /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/; -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -class g { - constructor(t, e, n) { - this.__parts = [], this.template = t, this.processor = e, this.options = n; - }update(t) { - let e = 0;for (const n of this.__parts) void 0 !== n && n.setValue(t[e]), e++;for (const n of this.__parts) void 0 !== n && n.commit(); - }_clone() { - const t = s ? this.template.element.content.cloneNode(!0) : document.importNode(this.template.element.content, !0), - e = [], - n = this.template.parts, - i = document.createTreeWalker(t, 133, null, !1);let r, - a = 0, - o = 0, - l = i.nextNode();for (; a < n.length;) if (r = n[a], f(r)) { - for (; o < r.index;) o++, "TEMPLATE" === l.nodeName && (e.push(l), i.currentNode = l.content), null === (l = i.nextNode()) && (i.currentNode = e.pop(), l = i.nextNode());if ("node" === r.type) { - const t = this.processor.handleTextExpression(this.options);t.insertAfterNode(l.previousSibling), this.__parts.push(t); - } else this.__parts.push(...this.processor.handleAttributeExpressions(l, r.name, r.strings, this.options));a++; - } else this.__parts.push(void 0), a++;return s && (document.adoptNode(t), customElements.upgrade(t)), t; - } -} -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */const b = ` ${l} `;class _ { - constructor(t, e, n, i) { - this.strings = t, this.values = e, this.type = n, this.processor = i; - }getHTML() { - const t = this.strings.length - 1;let e = "", - n = !1;for (let i = 0; i < t; i++) { - const t = this.strings[i], - s = t.lastIndexOf("\x3c!--");n = (s > -1 || n) && -1 === t.indexOf("--\x3e", s + 1);const r = m.exec(t);e += null === r ? t + (n ? b : c) : t.substr(0, r.index) + r[1] + r[2] + "$lit$" + r[3] + l; - }return e += this.strings[t], e; - }getTemplateElement() { - const t = document.createElement("template");return t.innerHTML = this.getHTML(), t; - } -} -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */const y = t => null === t || !("object" == typeof t || "function" == typeof t), - v = t => Array.isArray(t) || !(!t || !t[Symbol.iterator]);class w { - constructor(t, e, n) { - this.dirty = !0, this.element = t, this.name = e, this.strings = n, this.parts = [];for (let i = 0; i < n.length - 1; i++) this.parts[i] = this._createPart(); - }_createPart() { - return new S(this); - }_getValue() { - const t = this.strings, - e = t.length - 1;let n = "";for (let i = 0; i < e; i++) { - n += t[i];const e = this.parts[i];if (void 0 !== e) { - const t = e.value;if (y(t) || !v(t)) n += "string" == typeof t ? t : String(t);else for (const e of t) n += "string" == typeof e ? e : String(e); - } - }return n += t[e], n; - }commit() { - this.dirty && (this.dirty = !1, this.element.setAttribute(this.name, this._getValue())); - } -}class S { - constructor(t) { - this.value = void 0, this.committer = t; - }setValue(t) { - t === a || y(t) && t === this.value || (this.value = t, i(t) || (this.committer.dirty = !0)); - }commit() { - for (; i(this.value);) { - const t = this.value;this.value = a, t(this); - }this.value !== a && this.committer.commit(); - } -}class x { - constructor(t) { - this.value = void 0, this.__pendingValue = void 0, this.options = t; - }appendInto(t) { - this.startNode = t.appendChild(p()), this.endNode = t.appendChild(p()); - }insertAfterNode(t) { - this.startNode = t, this.endNode = t.nextSibling; - }appendIntoPart(t) { - t.__insert(this.startNode = p()), t.__insert(this.endNode = p()); - }insertAfterPart(t) { - t.__insert(this.startNode = p()), this.endNode = t.endNode, t.endNode = this.startNode; - }setValue(t) { - this.__pendingValue = t; - }commit() { - for (; i(this.__pendingValue);) { - const t = this.__pendingValue;this.__pendingValue = a, t(this); - }const t = this.__pendingValue;t !== a && (y(t) ? t !== this.value && this.__commitText(t) : t instanceof _ ? this.__commitTemplateResult(t) : t instanceof Node ? this.__commitNode(t) : v(t) ? this.__commitIterable(t) : t === o ? (this.value = o, this.clear()) : this.__commitText(t)); - }__insert(t) { - this.endNode.parentNode.insertBefore(t, this.endNode); - }__commitNode(t) { - this.value !== t && (this.clear(), this.__insert(t), this.value = t); - }__commitText(t) { - const e = this.startNode.nextSibling, - n = "string" == typeof (t = null == t ? "" : t) ? t : String(t);e === this.endNode.previousSibling && 3 === e.nodeType ? e.data = n : this.__commitNode(document.createTextNode(n)), this.value = t; - }__commitTemplateResult(t) { - const e = this.options.templateFactory(t);if (this.value instanceof g && this.value.template === e) this.value.update(t.values);else { - const n = new g(e, t.processor, this.options), - i = n._clone();n.update(t.values), this.__commitNode(i), this.value = n; - } - }__commitIterable(t) { - Array.isArray(this.value) || (this.value = [], this.clear());const e = this.value;let n, - i = 0;for (const s of t) n = e[i], void 0 === n && (n = new x(this.options), e.push(n), 0 === i ? n.appendIntoPart(this) : n.insertAfterPart(e[i - 1])), n.setValue(s), n.commit(), i++;i < e.length && (e.length = i, this.clear(n && n.endNode)); - }clear(t = this.startNode) { - r(this.startNode.parentNode, t.nextSibling, this.endNode); - } -}class k { - constructor(t, e, n) { - if (this.value = void 0, this.__pendingValue = void 0, 2 !== n.length || "" !== n[0] || "" !== n[1]) throw new Error("Boolean attributes can only contain a single expression");this.element = t, this.name = e, this.strings = n; - }setValue(t) { - this.__pendingValue = t; - }commit() { - for (; i(this.__pendingValue);) { - const t = this.__pendingValue;this.__pendingValue = a, t(this); - }if (this.__pendingValue === a) return;const t = !!this.__pendingValue;this.value !== t && (t ? this.element.setAttribute(this.name, "") : this.element.removeAttribute(this.name), this.value = t), this.__pendingValue = a; - } -}class M extends w { - constructor(t, e, n) { - super(t, e, n), this.single = 2 === n.length && "" === n[0] && "" === n[1]; - }_createPart() { - return new T(this); - }_getValue() { - return this.single ? this.parts[0].value : super._getValue(); - }commit() { - this.dirty && (this.dirty = !1, this.element[this.name] = this._getValue()); - } -}class T extends S {}let C = !1;try { - const t = { get capture() { - return C = !0, !1; - } };window.addEventListener("test", t, t), window.removeEventListener("test", t, t); -} catch (oe) {}class N { - constructor(t, e, n) { - this.value = void 0, this.__pendingValue = void 0, this.element = t, this.eventName = e, this.eventContext = n, this.__boundHandleEvent = t => this.handleEvent(t); - }setValue(t) { - this.__pendingValue = t; - }commit() { - for (; i(this.__pendingValue);) { - const t = this.__pendingValue;this.__pendingValue = a, t(this); - }if (this.__pendingValue === a) return;const t = this.__pendingValue, - e = this.value, - n = null == t || null != e && (t.capture !== e.capture || t.once !== e.once || t.passive !== e.passive), - s = null != t && (null == e || n);n && this.element.removeEventListener(this.eventName, this.__boundHandleEvent, this.__options), s && (this.__options = E(t), this.element.addEventListener(this.eventName, this.__boundHandleEvent, this.__options)), this.value = t, this.__pendingValue = a; - }handleEvent(t) { - "function" == typeof this.value ? this.value.call(this.eventContext || this.element, t) : this.value.handleEvent(t); - } -}const E = t => t && (C ? { capture: t.capture, passive: t.passive, once: t.once } : t.capture) -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */;const O = new class { - handleAttributeExpressions(t, e, n, i) { - const s = e[0];if ("." === s) { - return new M(t, e.slice(1), n).parts; - }return "@" === s ? [new N(t, e.slice(1), i.eventContext)] : "?" === s ? [new k(t, e.slice(1), n)] : new w(t, e, n).parts; - }handleTextExpression(t) { - return new x(t); - } -}(); -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */function P(t) { - let e = A.get(t.type);void 0 === e && (e = { stringsArray: new WeakMap(), keyString: new Map() }, A.set(t.type, e));let n = e.stringsArray.get(t.strings);if (void 0 !== n) return n;const i = t.strings.join(l);return n = e.keyString.get(i), void 0 === n && (n = new u(t, t.getTemplateElement()), e.keyString.set(i, n)), e.stringsArray.set(t.strings, n), n; -}const A = new Map(), - $ = new WeakMap(); -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -(window.litHtmlVersions || (window.litHtmlVersions = [])).push("1.1.2");const j = (t, ...e) => new _(t, e, "html", O) -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */;function V(t, e) { - const { element: { content: n }, parts: i } = t, - s = document.createTreeWalker(n, 133, null, !1);let r = H(i), - a = i[r], - o = -1, - l = 0;const c = [];let h = null;for (; s.nextNode();) { - o++;const t = s.currentNode;for (t.previousSibling === h && (h = null), e.has(t) && (c.push(t), null === h && (h = t)), null !== h && l++; void 0 !== a && a.index === o;) a.index = null !== h ? -1 : a.index - l, r = H(i, r), a = i[r]; - }c.forEach(t => t.parentNode.removeChild(t)); -}const R = t => { - let e = 11 === t.nodeType ? 0 : 1;const n = document.createTreeWalker(t, 133, null, !1);for (; n.nextNode();) e++;return e; -}, - H = (t, e = -1) => { - for (let n = e + 1; n < t.length; n++) { - const e = t[n];if (f(e)) return n; - }return -1; -}; -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -const D = (t, e) => `${t}--${e}`;let F = !0;void 0 === window.ShadyCSS ? F = !1 : void 0 === window.ShadyCSS.prepareTemplateDom && (console.warn("Incompatible ShadyCSS version detected. Please update to at least @webcomponents/webcomponentsjs@2.0.2 and @webcomponents/shadycss@1.3.1."), F = !1);const I = t => e => { - const n = D(e.type, t);let i = A.get(n);void 0 === i && (i = { stringsArray: new WeakMap(), keyString: new Map() }, A.set(n, i));let s = i.stringsArray.get(e.strings);if (void 0 !== s) return s;const r = e.strings.join(l);if (s = i.keyString.get(r), void 0 === s) { - const n = e.getTemplateElement();F && window.ShadyCSS.prepareTemplateDom(n, t), s = new u(e, n), i.keyString.set(r, s); - }return i.stringsArray.set(e.strings, s), s; -}, - L = ["html", "svg"], - z = new Set(), - U = (t, e, n) => { - z.add(t);const i = n ? n.element : document.createElement("template"), - s = e.querySelectorAll("style"), - { length: r } = s;if (0 === r) return void window.ShadyCSS.prepareTemplateStyles(i, t);const a = document.createElement("style");for (let c = 0; c < r; c++) { - const t = s[c];t.parentNode.removeChild(t), a.textContent += t.textContent; - }(t => { - L.forEach(e => { - const n = A.get(D(e, t));void 0 !== n && n.keyString.forEach(t => { - const { element: { content: e } } = t, - n = new Set();Array.from(e.querySelectorAll("style")).forEach(t => { - n.add(t); - }), V(t, n); - }); - }); - })(t);const o = i.content;n ? function (t, e, n = null) { - const { element: { content: i }, parts: s } = t;if (null == n) return void i.appendChild(e);const r = document.createTreeWalker(i, 133, null, !1);let a = H(s), - o = 0, - l = -1;for (; r.nextNode();) { - for (l++, r.currentNode === n && (o = R(e), n.parentNode.insertBefore(e, n)); -1 !== a && s[a].index === l;) { - if (o > 0) { - for (; -1 !== a;) s[a].index += o, a = H(s, a);return; - }a = H(s, a); - } - } - }(n, a, o.firstChild) : o.insertBefore(a, o.firstChild), window.ShadyCSS.prepareTemplateStyles(i, t);const l = o.querySelector("style");if (window.ShadyCSS.nativeShadow && null !== l) e.insertBefore(l.cloneNode(!0), e.firstChild);else if (n) { - o.insertBefore(a, o.firstChild);const t = new Set();t.add(a), V(n, t); - } -};window.JSCompiler_renameProperty = (t, e) => t;const Y = { toAttribute(t, e) { - switch (e) {case Boolean: - return t ? "" : null;case Object:case Array: - return null == t ? t : JSON.stringify(t);}return t; - }, fromAttribute(t, e) { - switch (e) {case Boolean: - return null !== t;case Number: - return null === t ? null : Number(t);case Object:case Array: - return JSON.parse(t);}return t; - } }, - q = (t, e) => e !== t && (e == e || t == t), - B = { attribute: !0, type: String, converter: Y, reflect: !1, hasChanged: q }, - W = Promise.resolve(!0);class G extends HTMLElement { - constructor() { - super(), this._updateState = 0, this._instanceProperties = void 0, this._updatePromise = W, this._hasConnectedResolver = void 0, this._changedProperties = new Map(), this._reflectingProperties = void 0, this.initialize(); - }static get observedAttributes() { - this.finalize();const t = [];return this._classProperties.forEach((e, n) => { - const i = this._attributeNameForProperty(n, e);void 0 !== i && (this._attributeToPropertyMap.set(i, n), t.push(i)); - }), t; - }static _ensureClassProperties() { - if (!this.hasOwnProperty(JSCompiler_renameProperty("_classProperties", this))) { - this._classProperties = new Map();const t = Object.getPrototypeOf(this)._classProperties;void 0 !== t && t.forEach((t, e) => this._classProperties.set(e, t)); - } - }static createProperty(t, e = B) { - if (this._ensureClassProperties(), this._classProperties.set(t, e), e.noAccessor || this.prototype.hasOwnProperty(t)) return;const n = "symbol" == typeof t ? Symbol() : `__${t}`;Object.defineProperty(this.prototype, t, { get() { - return this[n]; - }, set(e) { - const i = this[t];this[n] = e, this._requestUpdate(t, i); - }, configurable: !0, enumerable: !0 }); - }static finalize() { - const t = Object.getPrototypeOf(this);if (t.hasOwnProperty("finalized") || t.finalize(), this.finalized = !0, this._ensureClassProperties(), this._attributeToPropertyMap = new Map(), this.hasOwnProperty(JSCompiler_renameProperty("properties", this))) { - const t = this.properties, - e = [...Object.getOwnPropertyNames(t), ...("function" == typeof Object.getOwnPropertySymbols ? Object.getOwnPropertySymbols(t) : [])];for (const n of e) this.createProperty(n, t[n]); - } - }static _attributeNameForProperty(t, e) { - const n = e.attribute;return !1 === n ? void 0 : "string" == typeof n ? n : "string" == typeof t ? t.toLowerCase() : void 0; - }static _valueHasChanged(t, e, n = q) { - return n(t, e); - }static _propertyValueFromAttribute(t, e) { - const n = e.type, - i = e.converter || Y, - s = "function" == typeof i ? i : i.fromAttribute;return s ? s(t, n) : t; - }static _propertyValueToAttribute(t, e) { - if (void 0 === e.reflect) return;const n = e.type, - i = e.converter;return (i && i.toAttribute || Y.toAttribute)(t, n); - }initialize() { - this._saveInstanceProperties(), this._requestUpdate(); - }_saveInstanceProperties() { - this.constructor._classProperties.forEach((t, e) => { - if (this.hasOwnProperty(e)) { - const t = this[e];delete this[e], this._instanceProperties || (this._instanceProperties = new Map()), this._instanceProperties.set(e, t); - } - }); - }_applyInstanceProperties() { - this._instanceProperties.forEach((t, e) => this[e] = t), this._instanceProperties = void 0; - }connectedCallback() { - this._updateState = 32 | this._updateState, this._hasConnectedResolver && (this._hasConnectedResolver(), this._hasConnectedResolver = void 0); - }disconnectedCallback() {}attributeChangedCallback(t, e, n) { - e !== n && this._attributeToProperty(t, n); - }_propertyToAttribute(t, e, n = B) { - const i = this.constructor, - s = i._attributeNameForProperty(t, n);if (void 0 !== s) { - const t = i._propertyValueToAttribute(e, n);if (void 0 === t) return;this._updateState = 8 | this._updateState, null == t ? this.removeAttribute(s) : this.setAttribute(s, t), this._updateState = -9 & this._updateState; - } - }_attributeToProperty(t, e) { - if (8 & this._updateState) return;const n = this.constructor, - i = n._attributeToPropertyMap.get(t);if (void 0 !== i) { - const t = n._classProperties.get(i) || B;this._updateState = 16 | this._updateState, this[i] = n._propertyValueFromAttribute(e, t), this._updateState = -17 & this._updateState; - } - }_requestUpdate(t, e) { - let n = !0;if (void 0 !== t) { - const i = this.constructor, - s = i._classProperties.get(t) || B;i._valueHasChanged(this[t], e, s.hasChanged) ? (this._changedProperties.has(t) || this._changedProperties.set(t, e), !0 !== s.reflect || 16 & this._updateState || (void 0 === this._reflectingProperties && (this._reflectingProperties = new Map()), this._reflectingProperties.set(t, s))) : n = !1; - }!this._hasRequestedUpdate && n && this._enqueueUpdate(); - }requestUpdate(t, e) { - return this._requestUpdate(t, e), this.updateComplete; - }async _enqueueUpdate() { - let t, e;this._updateState = 4 | this._updateState;const n = this._updatePromise;this._updatePromise = new Promise((n, i) => { - t = n, e = i; - });try { - await n; - } catch (i) {}this._hasConnected || (await new Promise(t => this._hasConnectedResolver = t));try { - const t = this.performUpdate();null != t && (await t); - } catch (i) { - e(i); - }t(!this._hasRequestedUpdate); - }get _hasConnected() { - return 32 & this._updateState; - }get _hasRequestedUpdate() { - return 4 & this._updateState; - }get hasUpdated() { - return 1 & this._updateState; - }performUpdate() { - this._instanceProperties && this._applyInstanceProperties();let t = !1;const e = this._changedProperties;try { - t = this.shouldUpdate(e), t && this.update(e); - } catch (n) { - throw t = !1, n; - } finally { - this._markUpdated(); - }t && (1 & this._updateState || (this._updateState = 1 | this._updateState, this.firstUpdated(e)), this.updated(e)); - }_markUpdated() { - this._changedProperties = new Map(), this._updateState = -5 & this._updateState; - }get updateComplete() { - return this._getUpdateComplete(); - }_getUpdateComplete() { - return this._updatePromise; - }shouldUpdate(t) { - return !0; - }update(t) { - void 0 !== this._reflectingProperties && this._reflectingProperties.size > 0 && (this._reflectingProperties.forEach((t, e) => this._propertyToAttribute(e, this[e], t)), this._reflectingProperties = void 0); - }updated(t) {}firstUpdated(t) {} -}G.finalized = !0; -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -const J = (t, e) => "method" !== e.kind || !e.descriptor || "value" in e.descriptor ? { kind: "field", key: Symbol(), placement: "own", descriptor: {}, initializer() { - "function" == typeof e.initializer && (this[e.key] = e.initializer.call(this)); - }, finisher(n) { - n.createProperty(e.key, t); - } } : Object.assign({}, e, { finisher(n) { - n.createProperty(e.key, t); - } });function Z(t) { - return (e, n) => void 0 !== n ? ((t, e, n) => { - e.constructor.createProperty(n, t); - })(t, e, n) : J(t, e); -} -/** -@license -Copyright (c) 2019 The Polymer Project Authors. All rights reserved. -This code may only be used under the BSD style license found at -http://polymer.github.io/LICENSE.txt The complete set of authors may be found at -http://polymer.github.io/AUTHORS.txt The complete set of contributors may be -found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as -part of the polymer project is also subject to an additional IP rights grant -found at http://polymer.github.io/PATENTS.txt -*/const X = "adoptedStyleSheets" in Document.prototype && "replace" in CSSStyleSheet.prototype, - K = Symbol();class Q { - constructor(t, e) { - if (e !== K) throw new Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText = t; - }get styleSheet() { - return void 0 === this._styleSheet && (X ? (this._styleSheet = new CSSStyleSheet(), this._styleSheet.replaceSync(this.cssText)) : this._styleSheet = null), this._styleSheet; - }toString() { - return this.cssText; - } -} -/** - * @license - * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -(window.litElementVersions || (window.litElementVersions = [])).push("2.2.1");const tt = t => t.flat ? t.flat(1 / 0) : function t(e, n = []) { - for (let i = 0, s = e.length; i < s; i++) { - const s = e[i];Array.isArray(s) ? t(s, n) : n.push(s); - }return n; -}(t);class et extends G { - static finalize() { - super.finalize.call(this), this._styles = this.hasOwnProperty(JSCompiler_renameProperty("styles", this)) ? this._getUniqueStyles() : this._styles || []; - }static _getUniqueStyles() { - const t = this.styles, - e = [];if (Array.isArray(t)) { - tt(t).reduceRight((t, e) => (t.add(e), t), new Set()).forEach(t => e.unshift(t)); - } else t && e.push(t);return e; - }initialize() { - super.initialize(), this.renderRoot = this.createRenderRoot(), window.ShadowRoot && this.renderRoot instanceof window.ShadowRoot && this.adoptStyles(); - }createRenderRoot() { - return this.attachShadow({ mode: "open" }); - }adoptStyles() { - const t = this.constructor._styles;0 !== t.length && (void 0 === window.ShadyCSS || window.ShadyCSS.nativeShadow ? X ? this.renderRoot.adoptedStyleSheets = t.map(t => t.styleSheet) : this._needsShimAdoptedStyleSheets = !0 : window.ShadyCSS.ScopingShim.prepareAdoptedCssText(t.map(t => t.cssText), this.localName)); - }connectedCallback() { - super.connectedCallback(), this.hasUpdated && void 0 !== window.ShadyCSS && window.ShadyCSS.styleElement(this); - }update(t) { - super.update(t);const e = this.render();e instanceof _ && this.constructor.render(e, this.renderRoot, { scopeName: this.localName, eventContext: this }), this._needsShimAdoptedStyleSheets && (this._needsShimAdoptedStyleSheets = !1, this.constructor._styles.forEach(t => { - const e = document.createElement("style");e.textContent = t.cssText, this.renderRoot.appendChild(e); - })); - }render() {} -}et.finalized = !0, et.render = (t, e, n) => { - if (!n || "object" != typeof n || !n.scopeName) throw new Error("The `scopeName` option is required.");const i = n.scopeName, - s = $.has(e), - a = F && 11 === e.nodeType && !!e.host, - o = a && !z.has(i), - l = o ? document.createDocumentFragment() : e;if (((t, e, n) => { - let i = $.get(e);void 0 === i && (r(e, e.firstChild), $.set(e, i = new x(Object.assign({ templateFactory: P }, n))), i.appendInto(e)), i.setValue(t), i.commit(); - })(t, l, Object.assign({ templateFactory: I(i) }, n)), o) { - const t = $.get(l);$.delete(l);const n = t.value instanceof g ? t.value.template : void 0;U(i, l, n), r(e, e.firstChild), e.appendChild(l), $.set(e, t); - }!s && a && window.ShadyCSS.styleElement(e.host); -}; -/** - * @license - * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. - * This code may only be used under the BSD style license found at - * http://polymer.github.io/LICENSE.txt - * The complete set of authors may be found at - * http://polymer.github.io/AUTHORS.txt - * The complete set of contributors may be found at - * http://polymer.github.io/CONTRIBUTORS.txt - * Code distributed by Google as part of the polymer project is also - * subject to an additional IP rights grant found at - * http://polymer.github.io/PATENTS.txt - */ -const nt = new WeakMap(), - it = n(t => e => { - if (!(e instanceof S) || e instanceof T || "style" !== e.committer.name || e.committer.parts.length > 1) throw new Error("The `styleMap` directive must be used in the style attribute and must be the only part in the attribute.");const { committer: n } = e, - { style: i } = n.element;nt.has(e) || (i.cssText = n.strings.join(" "));const s = nt.get(e);for (const r in s) r in t || (-1 === r.indexOf("-") ? i[r] = null : i.removeProperty(r));for (const r in t) -1 === r.indexOf("-") ? i[r] = t[r] : i.setProperty(r, t[r]);nt.set(e, t); -}), - st = new WeakMap(), - rt = n(t => e => { - if (!(e instanceof x)) throw new Error("unsafeHTML can only be used in text bindings");const n = st.get(e);if (void 0 !== n && y(t) && t === n.value && e.value === n.fragment) return;const i = document.createElement("template");i.innerHTML = t;const s = document.importNode(i.content, !0);e.setValue(s), st.set(e, { value: t, fragment: s }); -}), - at = n(t => e => { - if (void 0 === t && e instanceof S) { - if (t !== e.value) { - const t = e.committer.name;e.committer.element.removeAttribute(t); - } - } else e.setValue(t); -}), - ot = new WeakMap(), - lt = n(t => e => { - if (!(e instanceof S) || e instanceof T || "class" !== e.committer.name || e.committer.parts.length > 1) throw new Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.");const { committer: n } = e, - { element: i } = n;ot.has(e) || (i.className = n.strings.join(" "));const { classList: s } = i, - r = ot.get(e);for (const a in r) a in t || s.remove(a);for (const a in t) { - const e = t[a];if (!r || e !== r[a]) { - s[e ? "add" : "remove"](a); - } - }ot.set(e, t); -});var ct = {}, - ht = /d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g, - ut = "[^\\s]+", - dt = /\[([^]*?)\]/gm, - ft = function () {};function pt(t, e) { - for (var n = [], i = 0, s = t.length; i < s; i++) n.push(t[i].substr(0, e));return n; -}function mt(t) { - return function (e, n, i) { - var s = i[t].indexOf(n.charAt(0).toUpperCase() + n.substr(1).toLowerCase());~s && (e.month = s); - }; -}function gt(t, e) { - for (t = String(t), e = e || 2; t.length < e;) t = "0" + t;return t; -}var bt = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], - _t = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], - yt = pt(_t, 3), - vt = pt(bt, 3);ct.i18n = { dayNamesShort: vt, dayNames: bt, monthNamesShort: yt, monthNames: _t, amPm: ["am", "pm"], DoFn: function (t) { - return t + ["th", "st", "nd", "rd"][t % 10 > 3 ? 0 : (t - t % 10 != 10) * t % 10]; - } };var wt = { D: function (t) { - return t.getDate(); - }, DD: function (t) { - return gt(t.getDate()); - }, Do: function (t, e) { - return e.DoFn(t.getDate()); - }, d: function (t) { - return t.getDay(); - }, dd: function (t) { - return gt(t.getDay()); - }, ddd: function (t, e) { - return e.dayNamesShort[t.getDay()]; - }, dddd: function (t, e) { - return e.dayNames[t.getDay()]; - }, M: function (t) { - return t.getMonth() + 1; - }, MM: function (t) { - return gt(t.getMonth() + 1); - }, MMM: function (t, e) { - return e.monthNamesShort[t.getMonth()]; - }, MMMM: function (t, e) { - return e.monthNames[t.getMonth()]; - }, YY: function (t) { - return gt(String(t.getFullYear()), 4).substr(2); - }, YYYY: function (t) { - return gt(t.getFullYear(), 4); - }, h: function (t) { - return t.getHours() % 12 || 12; - }, hh: function (t) { - return gt(t.getHours() % 12 || 12); - }, H: function (t) { - return t.getHours(); - }, HH: function (t) { - return gt(t.getHours()); - }, m: function (t) { - return t.getMinutes(); - }, mm: function (t) { - return gt(t.getMinutes()); - }, s: function (t) { - return t.getSeconds(); - }, ss: function (t) { - return gt(t.getSeconds()); - }, S: function (t) { - return Math.round(t.getMilliseconds() / 100); - }, SS: function (t) { - return gt(Math.round(t.getMilliseconds() / 10), 2); - }, SSS: function (t) { - return gt(t.getMilliseconds(), 3); - }, a: function (t, e) { - return t.getHours() < 12 ? e.amPm[0] : e.amPm[1]; - }, A: function (t, e) { - return t.getHours() < 12 ? e.amPm[0].toUpperCase() : e.amPm[1].toUpperCase(); - }, ZZ: function (t) { - var e = t.getTimezoneOffset();return (e > 0 ? "-" : "+") + gt(100 * Math.floor(Math.abs(e) / 60) + Math.abs(e) % 60, 4); - } }, - St = { D: ["\\d\\d?", function (t, e) { - t.day = e; - }], Do: ["\\d\\d?" + ut, function (t, e) { - t.day = parseInt(e, 10); - }], M: ["\\d\\d?", function (t, e) { - t.month = e - 1; - }], YY: ["\\d\\d?", function (t, e) { - var n = +("" + new Date().getFullYear()).substr(0, 2);t.year = "" + (e > 68 ? n - 1 : n) + e; - }], h: ["\\d\\d?", function (t, e) { - t.hour = e; - }], m: ["\\d\\d?", function (t, e) { - t.minute = e; - }], s: ["\\d\\d?", function (t, e) { - t.second = e; - }], YYYY: ["\\d{4}", function (t, e) { - t.year = e; - }], S: ["\\d", function (t, e) { - t.millisecond = 100 * e; - }], SS: ["\\d{2}", function (t, e) { - t.millisecond = 10 * e; - }], SSS: ["\\d{3}", function (t, e) { - t.millisecond = e; - }], d: ["\\d\\d?", ft], ddd: [ut, ft], MMM: [ut, mt("monthNamesShort")], MMMM: [ut, mt("monthNames")], a: [ut, function (t, e, n) { - var i = e.toLowerCase();i === n.amPm[0] ? t.isPm = !1 : i === n.amPm[1] && (t.isPm = !0); - }], ZZ: ["[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z", function (t, e) { - var n, - i = (e + "").match(/([+-]|\d\d)/gi);i && (n = 60 * i[1] + parseInt(i[2], 10), t.timezoneOffset = "+" === i[0] ? n : -n); - }] };function xt(t) { - var e = t.split(":").map(Number);return 3600 * e[0] + 60 * e[1] + e[2]; -}St.dd = St.d, St.dddd = St.ddd, St.DD = St.D, St.mm = St.m, St.hh = St.H = St.HH = St.h, St.MM = St.M, St.ss = St.s, St.A = St.a, ct.masks = { 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", shortTime: "HH:mm", mediumTime: "HH:mm:ss", longTime: "HH:mm:ss.SSS" }, ct.format = function (t, e, n) { - var i = n || ct.i18n;if ("number" == typeof t && (t = new Date(t)), "[object Date]" !== Object.prototype.toString.call(t) || isNaN(t.getTime())) throw new Error("Invalid Date in fecha.format");e = ct.masks[e] || e || ct.masks.default;var s = [];return (e = (e = e.replace(dt, function (t, e) { - return s.push(e), "@@@"; - })).replace(ht, function (e) { - return e in wt ? wt[e](t, i) : e.slice(1, e.length - 1); - })).replace(/@@@/g, function () { - return s.shift(); - }); -}, ct.parse = function (t, e, n) { - var i = n || ct.i18n;if ("string" != typeof e) throw new Error("Invalid format in fecha.parse");if (e = ct.masks[e] || e, t.length > 1e3) return null;var s = {}, - r = [], - a = [];e = e.replace(dt, function (t, e) { - return a.push(e), "@@@"; - });var o, - l = (o = e, o.replace(/[|\\{()[^$+*?.-]/g, "\\$&")).replace(ht, function (t) { - if (St[t]) { - var e = St[t];return r.push(e[1]), "(" + e[0] + ")"; - }return t; - });l = l.replace(/@@@/g, function () { - return a.shift(); - });var c = t.match(new RegExp(l, "i"));if (!c) return null;for (var h = 1; h < c.length; h++) r[h - 1](s, c[h], i);var u, - d = new Date();return !0 === s.isPm && null != s.hour && 12 != +s.hour ? s.hour = +s.hour + 12 : !1 === s.isPm && 12 == +s.hour && (s.hour = 0), null != s.timezoneOffset ? (s.minute = +(s.minute || 0) - +s.timezoneOffset, u = new Date(Date.UTC(s.year || d.getFullYear(), s.month || 0, s.day || 1, s.hour || 0, s.minute || 0, s.second || 0, s.millisecond || 0))) : u = new Date(s.year || d.getFullYear(), s.month || 0, s.day || 1, s.hour || 0, s.minute || 0, s.second || 0, s.millisecond || 0), u; -};(function () { - try { - new Date().toLocaleDateString("i"); - } catch (t) { - return "RangeError" === t.name; - } -})(), function () { - try { - new Date().toLocaleString("i"); - } catch (t) { - return "RangeError" === t.name; - } -}(), function () { - try { - new Date().toLocaleTimeString("i"); - } catch (t) { - return "RangeError" === t.name; - } -}();var kt = function (t) { - return t < 10 ? "0" + t : t; -};var Mt = ["closed", "locked", "off"], - Tt = function (t, e, n, i) { - i = i || {}, n = null == n ? {} : n;var s = new Event(e, { bubbles: void 0 === i.bubbles || i.bubbles, cancelable: Boolean(i.cancelable), composed: void 0 === i.composed || i.composed });return s.detail = n, t.dispatchEvent(s), s; -}, - Ct = { alert: "hass:alert", automation: "hass:playlist-play", calendar: "hass:calendar", camera: "hass:video", climate: "hass:thermostat", configurator: "hass:settings", conversation: "hass:text-to-speech", device_tracker: "hass:account", fan: "hass:fan", group: "hass:google-circles-communities", history_graph: "hass:chart-line", homeassistant: "hass:home-assistant", homekit: "hass:home-automation", image_processing: "hass:image-filter-frames", input_boolean: "hass:drawing", input_datetime: "hass:calendar-clock", input_number: "hass:ray-vertex", input_select: "hass:format-list-bulleted", input_text: "hass:textbox", light: "hass:lightbulb", mailbox: "hass:mailbox", notify: "hass:comment-alert", person: "hass:account", plant: "hass:flower", proximity: "hass:apple-safari", remote: "hass:remote", scene: "hass:google-pages", script: "hass:file-document", sensor: "hass:eye", simple_alarm: "hass:bell", sun: "hass:white-balance-sunny", switch: "hass:flash", timer: "hass:timer", updater: "hass:cloud-upload", vacuum: "hass:robot-vacuum", water_heater: "hass:thermometer", weblink: "hass:open-in-new" };var Nt = function (t, e) { - Tt(t, "haptic", e); -}, - Et = function (t, e, n, i, s) { - var r;if (s && n.double_tap_action ? r = n.double_tap_action : i && n.hold_action ? r = n.hold_action : !i && n.tap_action && (r = n.tap_action), r || (r = { action: "more-info" }), !r.confirmation || r.confirmation.exemptions && r.confirmation.exemptions.some(function (t) { - return t.user === e.user.id; - }) || confirm(r.confirmation.text || "Are you sure you want to " + r.action + "?")) switch (r.action) {case "more-info": - (n.entity || n.camera_image) && (Tt(t, "hass-more-info", { entityId: r.entity ? r.entity : n.entity ? n.entity : n.camera_image }), r.haptic && Nt(t, r.haptic));break;case "navigate": - r.navigation_path && (function (t, e, n) { - void 0 === n && (n = !1), n ? history.replaceState(null, "", e) : history.pushState(null, "", e), Tt(window, "location-changed", { replace: n }); - }(0, r.navigation_path), r.haptic && Nt(t, r.haptic));break;case "url": - r.url_path && window.open(r.url_path), r.haptic && Nt(t, r.haptic);break;case "toggle": - n.entity && (function (t, e) { - (function (t, e, n) { - void 0 === n && (n = !0);var i, - s = function (t) { - return t.substr(0, t.indexOf(".")); - }(e), - r = "group" === s ? "homeassistant" : s;switch (s) {case "lock": - i = n ? "unlock" : "lock";break;case "cover": - i = n ? "open_cover" : "close_cover";break;default: - i = n ? "turn_on" : "turn_off";}t.callService(r, i, { entity_id: e }); - })(t, e, Mt.includes(t.states[e].state)); - }(e, n.entity), r.haptic && Nt(t, r.haptic));break;case "call-service": - if (!r.service) return;var a = r.service.split(".", 2), - o = a[0], - l = a[1], - c = Object.assign({}, r.service_data);"entity" === c.entity_id && (c.entity_id = n.entity), e.callService(o, l, c), r.haptic && Nt(t, r.haptic);} -};const Ot = "ontouchstart" in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;class Pt extends HTMLElement { - constructor() { - super(), this.holdTime = 500, this.ripple = document.createElement("paper-ripple"), this.timer = void 0, this.held = !1, this.cooldownStart = !1, this.cooldownEnd = !1, this.nbClicks = 0; - }connectedCallback() { - Object.assign(this.style, { borderRadius: "50%", position: "absolute", width: Ot ? "100px" : "50px", height: Ot ? "100px" : "50px", transform: "translate(-50%, -50%)", pointerEvents: "none" }), this.appendChild(this.ripple), this.ripple.style.color = "#03a9f4", this.ripple.style.color = "var(--primary-color)", ["touchcancel", "mouseout", "mouseup", "touchmove", "mousewheel", "wheel", "scroll"].forEach(t => { - document.addEventListener(t, () => { - clearTimeout(this.timer), this.stopAnimation(), this.timer = void 0; - }, { passive: !0 }); - }); - }bind(t) { - if (t.longPress) return;t.longPress = !0, t.addEventListener("contextmenu", t => { - const e = t || window.event;return e.preventDefault && e.preventDefault(), e.stopPropagation && e.stopPropagation(), e.cancelBubble = !0, e.returnValue = !1, !1; - });const e = e => { - if (this.cooldownStart) return;let n, i;this.held = !1, e.touches ? (n = e.touches[0].pageX, i = e.touches[0].pageY) : (n = e.pageX, i = e.pageY), this.timer = window.setTimeout(() => { - this.startAnimation(n, i), this.held = !0, t.repeat && !t.isRepeating && (t.isRepeating = !0, this.repeatTimeout = setInterval(() => { - t.dispatchEvent(new Event("ha-hold")); - }, t.repeat)); - }, this.holdTime), this.cooldownStart = !0, window.setTimeout(() => this.cooldownStart = !1, 100); - }, - n = e => { - this.cooldownEnd || ["touchend", "touchcancel"].includes(e.type) && void 0 === this.timer ? t.isRepeating && this.repeatTimeout && (clearInterval(this.repeatTimeout), t.isRepeating = !1) : (clearTimeout(this.timer), t.isRepeating && this.repeatTimeout && clearInterval(this.repeatTimeout), t.isRepeating = !1, this.stopAnimation(), this.timer = void 0, this.held ? t.repeat || t.dispatchEvent(new Event("ha-hold")) : t.hasDblClick ? 0 === this.nbClicks ? (this.nbClicks += 1, this.dblClickTimeout = window.setTimeout(() => { - 1 === this.nbClicks && (this.nbClicks = 0, t.dispatchEvent(new Event("ha-click"))); - }, 250)) : (this.nbClicks = 0, clearTimeout(this.dblClickTimeout), t.dispatchEvent(new Event("ha-dblclick"))) : t.dispatchEvent(new Event("ha-click")), this.cooldownEnd = !0, window.setTimeout(() => this.cooldownEnd = !1, 100)); - };t.addEventListener("touchstart", e, { passive: !0 }), t.addEventListener("touchend", n), t.addEventListener("touchcancel", n);const i = "MacIntel" === navigator.platform && navigator.maxTouchPoints > 1 && !window.MSStream, - s = /iPad|iPhone|iPod/.test(navigator.platform);i || s || (t.addEventListener("mousedown", e, { passive: !0 }), t.addEventListener("click", n)); - }startAnimation(t, e) { - Object.assign(this.style, { left: `${t}px`, top: `${e}px`, display: null }), this.ripple.holdDown = !0, this.ripple.simulatedRipple(); - }stopAnimation() { - this.ripple.holdDown = !1, this.style.display = "none"; - } -}customElements.define("long-press-button-card", Pt);const At = t => { - const e = (() => { - const t = document.body;if (t.querySelector("long-press-button-card")) return t.querySelector("long-press-button-card");const e = document.createElement("long-press-button-card");return t.appendChild(e), e; - })();e && e.bind(t); -}, - $t = n(() => t => { - At(t.committer.element); -});function jt(t, e) { - (function (t) { - return "string" == typeof t && t.includes(".") && 1 === parseFloat(t); - })(t) && (t = "100%");var n = function (t) { - return "string" == typeof t && t.includes("%"); - }(t);return t = 360 === e ? t : Math.min(e, Math.max(0, parseFloat(t))), n && (t = parseInt(String(t * e), 10) / 100), Math.abs(t - e) < 1e-6 ? 1 : t = 360 === e ? (t < 0 ? t % e + e : t % e) / parseFloat(String(e)) : t % e / parseFloat(String(e)); -}function Vt(t) { - return Math.min(1, Math.max(0, t)); -}function Rt(t) { - return t = parseFloat(t), (isNaN(t) || t < 0 || t > 1) && (t = 1), t; -}function Ht(t) { - return t <= 1 ? 100 * Number(t) + "%" : t; -}function Dt(t) { - return 1 === t.length ? "0" + t : String(t); -}function Ft(t, e, n) { - t = jt(t, 255), e = jt(e, 255), n = jt(n, 255);var i = Math.max(t, e, n), - s = Math.min(t, e, n), - r = 0, - a = 0, - o = (i + s) / 2;if (i === s) a = 0, r = 0;else { - var l = i - s;switch (a = o > .5 ? l / (2 - i - s) : l / (i + s), i) {case t: - r = (e - n) / l + (e < n ? 6 : 0);break;case e: - r = (n - t) / l + 2;break;case n: - r = (t - e) / l + 4;}r /= 6; - }return { h: r, s: a, l: o }; -}function It(t, e, n) { - t = jt(t, 255), e = jt(e, 255), n = jt(n, 255);var i = Math.max(t, e, n), - s = Math.min(t, e, n), - r = 0, - a = i, - o = i - s, - l = 0 === i ? 0 : o / i;if (i === s) r = 0;else { - switch (i) {case t: - r = (e - n) / o + (e < n ? 6 : 0);break;case e: - r = (n - t) / o + 2;break;case n: - r = (t - e) / o + 4;}r /= 6; - }return { h: r, s: l, v: a }; -}function Lt(t, e, n, i) { - var s = [Dt(Math.round(t).toString(16)), Dt(Math.round(e).toString(16)), Dt(Math.round(n).toString(16))];return i && s[0].charAt(0) === s[0].charAt(1) && s[1].charAt(0) === s[1].charAt(1) && s[2].charAt(0) === s[2].charAt(1) ? s[0].charAt(0) + s[1].charAt(0) + s[2].charAt(0) : s.join(""); -}function zt(t) { - return Ut(t) / 255; -}function Ut(t) { - return parseInt(t, 16); -}var Yt = { aliceblue: "#f0f8ff", antiquewhite: "#faebd7", aqua: "#00ffff", aquamarine: "#7fffd4", azure: "#f0ffff", beige: "#f5f5dc", bisque: "#ffe4c4", black: "#000000", blanchedalmond: "#ffebcd", blue: "#0000ff", blueviolet: "#8a2be2", brown: "#a52a2a", burlywood: "#deb887", cadetblue: "#5f9ea0", chartreuse: "#7fff00", chocolate: "#d2691e", coral: "#ff7f50", cornflowerblue: "#6495ed", cornsilk: "#fff8dc", crimson: "#dc143c", cyan: "#00ffff", darkblue: "#00008b", darkcyan: "#008b8b", darkgoldenrod: "#b8860b", darkgray: "#a9a9a9", darkgreen: "#006400", darkgrey: "#a9a9a9", darkkhaki: "#bdb76b", darkmagenta: "#8b008b", darkolivegreen: "#556b2f", darkorange: "#ff8c00", darkorchid: "#9932cc", darkred: "#8b0000", darksalmon: "#e9967a", darkseagreen: "#8fbc8f", darkslateblue: "#483d8b", darkslategray: "#2f4f4f", darkslategrey: "#2f4f4f", darkturquoise: "#00ced1", darkviolet: "#9400d3", deeppink: "#ff1493", deepskyblue: "#00bfff", dimgray: "#696969", dimgrey: "#696969", dodgerblue: "#1e90ff", firebrick: "#b22222", floralwhite: "#fffaf0", forestgreen: "#228b22", fuchsia: "#ff00ff", gainsboro: "#dcdcdc", ghostwhite: "#f8f8ff", gold: "#ffd700", goldenrod: "#daa520", gray: "#808080", green: "#008000", greenyellow: "#adff2f", grey: "#808080", honeydew: "#f0fff0", hotpink: "#ff69b4", indianred: "#cd5c5c", indigo: "#4b0082", ivory: "#fffff0", khaki: "#f0e68c", lavender: "#e6e6fa", lavenderblush: "#fff0f5", lawngreen: "#7cfc00", lemonchiffon: "#fffacd", lightblue: "#add8e6", lightcoral: "#f08080", lightcyan: "#e0ffff", lightgoldenrodyellow: "#fafad2", lightgray: "#d3d3d3", lightgreen: "#90ee90", lightgrey: "#d3d3d3", lightpink: "#ffb6c1", lightsalmon: "#ffa07a", lightseagreen: "#20b2aa", lightskyblue: "#87cefa", lightslategray: "#778899", lightslategrey: "#778899", lightsteelblue: "#b0c4de", lightyellow: "#ffffe0", lime: "#00ff00", limegreen: "#32cd32", linen: "#faf0e6", magenta: "#ff00ff", maroon: "#800000", mediumaquamarine: "#66cdaa", mediumblue: "#0000cd", mediumorchid: "#ba55d3", mediumpurple: "#9370db", mediumseagreen: "#3cb371", mediumslateblue: "#7b68ee", mediumspringgreen: "#00fa9a", mediumturquoise: "#48d1cc", mediumvioletred: "#c71585", midnightblue: "#191970", mintcream: "#f5fffa", mistyrose: "#ffe4e1", moccasin: "#ffe4b5", navajowhite: "#ffdead", navy: "#000080", oldlace: "#fdf5e6", olive: "#808000", olivedrab: "#6b8e23", orange: "#ffa500", orangered: "#ff4500", orchid: "#da70d6", palegoldenrod: "#eee8aa", palegreen: "#98fb98", paleturquoise: "#afeeee", palevioletred: "#db7093", papayawhip: "#ffefd5", peachpuff: "#ffdab9", peru: "#cd853f", pink: "#ffc0cb", plum: "#dda0dd", powderblue: "#b0e0e6", purple: "#800080", rebeccapurple: "#663399", red: "#ff0000", rosybrown: "#bc8f8f", royalblue: "#4169e1", saddlebrown: "#8b4513", salmon: "#fa8072", sandybrown: "#f4a460", seagreen: "#2e8b57", seashell: "#fff5ee", sienna: "#a0522d", silver: "#c0c0c0", skyblue: "#87ceeb", slateblue: "#6a5acd", slategray: "#708090", slategrey: "#708090", snow: "#fffafa", springgreen: "#00ff7f", steelblue: "#4682b4", tan: "#d2b48c", teal: "#008080", thistle: "#d8bfd8", tomato: "#ff6347", turquoise: "#40e0d0", violet: "#ee82ee", wheat: "#f5deb3", white: "#ffffff", whitesmoke: "#f5f5f5", yellow: "#ffff00", yellowgreen: "#9acd32" };function qt(t) { - var e = { r: 0, g: 0, b: 0 }, - n = 1, - i = null, - s = null, - r = null, - a = !1, - o = !1;return "string" == typeof t && (t = function (t) { - if (0 === (t = t.trim().toLowerCase()).length) return !1;var e = !1;if (Yt[t]) t = Yt[t], e = !0;else if ("transparent" === t) return { r: 0, g: 0, b: 0, a: 0, format: "name" };var n = Jt.rgb.exec(t);if (n) return { r: n[1], g: n[2], b: n[3] };if (n = Jt.rgba.exec(t)) return { r: n[1], g: n[2], b: n[3], a: n[4] };if (n = Jt.hsl.exec(t)) return { h: n[1], s: n[2], l: n[3] };if (n = Jt.hsla.exec(t)) return { h: n[1], s: n[2], l: n[3], a: n[4] };if (n = Jt.hsv.exec(t)) return { h: n[1], s: n[2], v: n[3] };if (n = Jt.hsva.exec(t)) return { h: n[1], s: n[2], v: n[3], a: n[4] };if (n = Jt.hex8.exec(t)) return { r: Ut(n[1]), g: Ut(n[2]), b: Ut(n[3]), a: zt(n[4]), format: e ? "name" : "hex8" };if (n = Jt.hex6.exec(t)) return { r: Ut(n[1]), g: Ut(n[2]), b: Ut(n[3]), format: e ? "name" : "hex" };if (n = Jt.hex4.exec(t)) return { r: Ut(n[1] + n[1]), g: Ut(n[2] + n[2]), b: Ut(n[3] + n[3]), a: zt(n[4] + n[4]), format: e ? "name" : "hex8" };if (n = Jt.hex3.exec(t)) return { r: Ut(n[1] + n[1]), g: Ut(n[2] + n[2]), b: Ut(n[3] + n[3]), format: e ? "name" : "hex" };return !1; - }(t)), "object" == typeof t && (Zt(t.r) && Zt(t.g) && Zt(t.b) ? (e = function (t, e, n) { - return { r: 255 * jt(t, 255), g: 255 * jt(e, 255), b: 255 * jt(n, 255) }; - }(t.r, t.g, t.b), a = !0, o = "%" === String(t.r).substr(-1) ? "prgb" : "rgb") : Zt(t.h) && Zt(t.s) && Zt(t.v) ? (i = Ht(t.s), s = Ht(t.v), e = function (t, e, n) { - t = 6 * jt(t, 360), e = jt(e, 100), n = jt(n, 100);var i = Math.floor(t), - s = t - i, - r = n * (1 - e), - a = n * (1 - s * e), - o = n * (1 - (1 - s) * e), - l = i % 6;return { r: 255 * [n, a, r, r, o, n][l], g: 255 * [o, n, n, a, r, r][l], b: 255 * [r, r, o, n, n, a][l] }; - }(t.h, i, s), a = !0, o = "hsv") : Zt(t.h) && Zt(t.s) && Zt(t.l) && (i = Ht(t.s), r = Ht(t.l), e = function (t, e, n) { - var i, s, r;function a(t, e, n) { - return n < 0 && (n += 1), n > 1 && (n -= 1), n < 1 / 6 ? t + 6 * n * (e - t) : n < .5 ? e : n < 2 / 3 ? t + (e - t) * (2 / 3 - n) * 6 : t; - }if (t = jt(t, 360), e = jt(e, 100), n = jt(n, 100), 0 === e) s = n, r = n, i = n;else { - var o = n < .5 ? n * (1 + e) : n + e - n * e, - l = 2 * n - o;i = a(l, o, t + 1 / 3), s = a(l, o, t), r = a(l, o, t - 1 / 3); - }return { r: 255 * i, g: 255 * s, b: 255 * r }; - }(t.h, i, r), a = !0, o = "hsl"), Object.prototype.hasOwnProperty.call(t, "a") && (n = t.a)), n = Rt(n), { ok: a, format: t.format || o, r: Math.min(255, Math.max(e.r, 0)), g: Math.min(255, Math.max(e.g, 0)), b: Math.min(255, Math.max(e.b, 0)), a: n }; -}var Bt = "(?:[-\\+]?\\d*\\.\\d+%?)|(?:[-\\+]?\\d+%?)", - Wt = "[\\s|\\(]+(" + Bt + ")[,|\\s]+(" + Bt + ")[,|\\s]+(" + Bt + ")\\s*\\)?", - Gt = "[\\s|\\(]+(" + Bt + ")[,|\\s]+(" + Bt + ")[,|\\s]+(" + Bt + ")[,|\\s]+(" + Bt + ")\\s*\\)?", - Jt = { CSS_UNIT: new RegExp(Bt), rgb: new RegExp("rgb" + Wt), rgba: new RegExp("rgba" + Gt), hsl: new RegExp("hsl" + Wt), hsla: new RegExp("hsla" + Gt), hsv: new RegExp("hsv" + Wt), hsva: new RegExp("hsva" + Gt), hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, hex4: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ };function Zt(t) { - return Boolean(Jt.CSS_UNIT.exec(String(t))); -}var Xt = function () { - function t(e, n) { - if (void 0 === e && (e = ""), void 0 === n && (n = {}), e instanceof t) return e;this.originalInput = e;var i = qt(e);this.originalInput = e, this.r = i.r, this.g = i.g, this.b = i.b, this.a = i.a, this.roundA = Math.round(100 * this.a) / 100, this.format = n.format || i.format, this.gradientType = n.gradientType, this.r < 1 && (this.r = Math.round(this.r)), this.g < 1 && (this.g = Math.round(this.g)), this.b < 1 && (this.b = Math.round(this.b)), this.isValid = i.ok; - }return t.prototype.isDark = function () { - return this.getBrightness() < 128; - }, t.prototype.isLight = function () { - return !this.isDark(); - }, t.prototype.getBrightness = function () { - var t = this.toRgb();return (299 * t.r + 587 * t.g + 114 * t.b) / 1e3; - }, t.prototype.getLuminance = function () { - var t = this.toRgb(), - e = t.r / 255, - n = t.g / 255, - i = t.b / 255;return .2126 * (e <= .03928 ? e / 12.92 : Math.pow((e + .055) / 1.055, 2.4)) + .7152 * (n <= .03928 ? n / 12.92 : Math.pow((n + .055) / 1.055, 2.4)) + .0722 * (i <= .03928 ? i / 12.92 : Math.pow((i + .055) / 1.055, 2.4)); - }, t.prototype.getAlpha = function () { - return this.a; - }, t.prototype.setAlpha = function (t) { - return this.a = Rt(t), this.roundA = Math.round(100 * this.a) / 100, this; - }, t.prototype.toHsv = function () { - var t = It(this.r, this.g, this.b);return { h: 360 * t.h, s: t.s, v: t.v, a: this.a }; - }, t.prototype.toHsvString = function () { - var t = It(this.r, this.g, this.b), - e = Math.round(360 * t.h), - n = Math.round(100 * t.s), - i = Math.round(100 * t.v);return 1 === this.a ? "hsv(" + e + ", " + n + "%, " + i + "%)" : "hsva(" + e + ", " + n + "%, " + i + "%, " + this.roundA + ")"; - }, t.prototype.toHsl = function () { - var t = Ft(this.r, this.g, this.b);return { h: 360 * t.h, s: t.s, l: t.l, a: this.a }; - }, t.prototype.toHslString = function () { - var t = Ft(this.r, this.g, this.b), - e = Math.round(360 * t.h), - n = Math.round(100 * t.s), - i = Math.round(100 * t.l);return 1 === this.a ? "hsl(" + e + ", " + n + "%, " + i + "%)" : "hsla(" + e + ", " + n + "%, " + i + "%, " + this.roundA + ")"; - }, t.prototype.toHex = function (t) { - return void 0 === t && (t = !1), Lt(this.r, this.g, this.b, t); - }, t.prototype.toHexString = function (t) { - return void 0 === t && (t = !1), "#" + this.toHex(t); - }, t.prototype.toHex8 = function (t) { - return void 0 === t && (t = !1), function (t, e, n, i, s) { - var r, - a = [Dt(Math.round(t).toString(16)), Dt(Math.round(e).toString(16)), Dt(Math.round(n).toString(16)), Dt((r = i, Math.round(255 * parseFloat(r)).toString(16)))];return s && a[0].charAt(0) === a[0].charAt(1) && a[1].charAt(0) === a[1].charAt(1) && a[2].charAt(0) === a[2].charAt(1) && a[3].charAt(0) === a[3].charAt(1) ? a[0].charAt(0) + a[1].charAt(0) + a[2].charAt(0) + a[3].charAt(0) : a.join(""); - }(this.r, this.g, this.b, this.a, t); - }, t.prototype.toHex8String = function (t) { - return void 0 === t && (t = !1), "#" + this.toHex8(t); - }, t.prototype.toRgb = function () { - return { r: Math.round(this.r), g: Math.round(this.g), b: Math.round(this.b), a: this.a }; - }, t.prototype.toRgbString = function () { - var t = Math.round(this.r), - e = Math.round(this.g), - n = Math.round(this.b);return 1 === this.a ? "rgb(" + t + ", " + e + ", " + n + ")" : "rgba(" + t + ", " + e + ", " + n + ", " + this.roundA + ")"; - }, t.prototype.toPercentageRgb = function () { - var t = function (t) { - return Math.round(100 * jt(t, 255)) + "%"; - };return { r: t(this.r), g: t(this.g), b: t(this.b), a: this.a }; - }, t.prototype.toPercentageRgbString = function () { - var t = function (t) { - return Math.round(100 * jt(t, 255)); - };return 1 === this.a ? "rgb(" + t(this.r) + "%, " + t(this.g) + "%, " + t(this.b) + "%)" : "rgba(" + t(this.r) + "%, " + t(this.g) + "%, " + t(this.b) + "%, " + this.roundA + ")"; - }, t.prototype.toName = function () { - if (0 === this.a) return "transparent";if (this.a < 1) return !1;for (var t = "#" + Lt(this.r, this.g, this.b, !1), e = 0, n = Object.keys(Yt); e < n.length; e++) { - var i = n[e];if (Yt[i] === t) return i; - }return !1; - }, t.prototype.toString = function (t) { - var e = Boolean(t);t = t || this.format;var n = !1, - i = this.a < 1 && this.a >= 0;return e || !i || !t.startsWith("hex") && "name" !== t ? ("rgb" === t && (n = this.toRgbString()), "prgb" === t && (n = this.toPercentageRgbString()), "hex" !== t && "hex6" !== t || (n = this.toHexString()), "hex3" === t && (n = this.toHexString(!0)), "hex4" === t && (n = this.toHex8String(!0)), "hex8" === t && (n = this.toHex8String()), "name" === t && (n = this.toName()), "hsl" === t && (n = this.toHslString()), "hsv" === t && (n = this.toHsvString()), n || this.toHexString()) : "name" === t && 0 === this.a ? this.toName() : this.toRgbString(); - }, t.prototype.clone = function () { - return new t(this.toString()); - }, t.prototype.lighten = function (e) { - void 0 === e && (e = 10);var n = this.toHsl();return n.l += e / 100, n.l = Vt(n.l), new t(n); - }, t.prototype.brighten = function (e) { - void 0 === e && (e = 10);var n = this.toRgb();return n.r = Math.max(0, Math.min(255, n.r - Math.round(-e / 100 * 255))), n.g = Math.max(0, Math.min(255, n.g - Math.round(-e / 100 * 255))), n.b = Math.max(0, Math.min(255, n.b - Math.round(-e / 100 * 255))), new t(n); - }, t.prototype.darken = function (e) { - void 0 === e && (e = 10);var n = this.toHsl();return n.l -= e / 100, n.l = Vt(n.l), new t(n); - }, t.prototype.tint = function (t) { - return void 0 === t && (t = 10), this.mix("white", t); - }, t.prototype.shade = function (t) { - return void 0 === t && (t = 10), this.mix("black", t); - }, t.prototype.desaturate = function (e) { - void 0 === e && (e = 10);var n = this.toHsl();return n.s -= e / 100, n.s = Vt(n.s), new t(n); - }, t.prototype.saturate = function (e) { - void 0 === e && (e = 10);var n = this.toHsl();return n.s += e / 100, n.s = Vt(n.s), new t(n); - }, t.prototype.greyscale = function () { - return this.desaturate(100); - }, t.prototype.spin = function (e) { - var n = this.toHsl(), - i = (n.h + e) % 360;return n.h = i < 0 ? 360 + i : i, new t(n); - }, t.prototype.mix = function (e, n) { - void 0 === n && (n = 50);var i = this.toRgb(), - s = new t(e).toRgb(), - r = n / 100;return new t({ r: (s.r - i.r) * r + i.r, g: (s.g - i.g) * r + i.g, b: (s.b - i.b) * r + i.b, a: (s.a - i.a) * r + i.a }); - }, t.prototype.analogous = function (e, n) { - void 0 === e && (e = 6), void 0 === n && (n = 30);var i = this.toHsl(), - s = 360 / n, - r = [this];for (i.h = (i.h - (s * e >> 1) + 720) % 360; --e;) i.h = (i.h + s) % 360, r.push(new t(i));return r; - }, t.prototype.complement = function () { - var e = this.toHsl();return e.h = (e.h + 180) % 360, new t(e); - }, t.prototype.monochromatic = function (e) { - void 0 === e && (e = 6);for (var n = this.toHsv(), i = n.h, s = n.s, r = n.v, a = [], o = 1 / e; e--;) a.push(new t({ h: i, s: s, v: r })), r = (r + o) % 1;return a; - }, t.prototype.splitcomplement = function () { - var e = this.toHsl(), - n = e.h;return [this, new t({ h: (n + 72) % 360, s: e.s, l: e.l }), new t({ h: (n + 216) % 360, s: e.s, l: e.l })]; - }, t.prototype.triad = function () { - return this.polyad(3); - }, t.prototype.tetrad = function () { - return this.polyad(4); - }, t.prototype.polyad = function (e) { - for (var n = this.toHsl(), i = n.h, s = [this], r = 360 / e, a = 1; a < e; a++) s.push(new t({ h: (i + a * r) % 360, s: n.s, l: n.l }));return s; - }, t.prototype.equals = function (e) { - return this.toRgbString() === new t(e).toRgbString(); - }, t; -}();function Kt(t, e) { - return void 0 === t && (t = ""), void 0 === e && (e = {}), new Xt(t, e); -}function Qt(t) { - return t.substr(0, t.indexOf(".")); -}function te(t) { - return "var" === t.substring(0, 3) ? window.getComputedStyle(document.documentElement).getPropertyValue(t.substring(4).slice(0, -1)).trim() : t; -}function ee(t, e) { - const n = new Xt(te(t));if (n.isValid) { - const t = n.mix("black", 100 - e).toString();if (t) return t; - }return t; -}function ne(...t) { - const e = t => t && "object" == typeof t;return t.reduce((t, n) => (Object.keys(n).forEach(i => { - const s = t[i], - r = n[i];Array.isArray(s) && Array.isArray(r) ? t[i] = s.concat(...r) : e(s) && e(r) ? t[i] = ne(s, r) : t[i] = r; - }), t), {}); -}function ie(t, e) { - let n = [];return t && t.forEach(t => { - let i = t;e && e.forEach(e => { - e.id && t.id && e.id == t.id && (i = ne(i, e)); - }), n.push(i); - }), e && (n = n.concat(e.filter(e => !t || !t.find(t => !(!t.id || !e.id) && t.id == e.id)))), n; -}const se = ((t, ...e) => { - const n = e.reduce((e, n, i) => e + (t => { - if (t instanceof Q) return t.cssText;if ("number" == typeof t) return t;throw new Error(`Value passed to 'css' function must be a 'css' function result: ${t}. Use 'unsafeCSS' to pass non-literal values, but\n take care to ensure page security.`); - })(n) + t[i + 1], t[0]);return new Q(n, K); -})` ha-card { cursor: pointer; overflow: hidden; @@ -1053,6 +11,7 @@ const nt = new WeakMap(), display: flex; justify-content: center; align-items: center; + line-height: normal; -webkit-touch-callout: none; /* iOS Safari */ -webkit-user-select: none; /* Safari */ @@ -1066,9 +25,33 @@ const nt = new WeakMap(), pointer-events: none; cursor: default; } + .tooltip .tooltiptext { + opacity: 0; + text-align: center; + padding: 4px; + border-radius: var(--ha-card-border-radius, 4px); + box-shadow: var( + --ha-card-box-shadow, + 0px 2px 1px -1px rgba(0, 0, 0, 0.2), + 0px 1px 1px 0px rgba(0, 0, 0, 0.14), + 0px 1px 3px 0px rgba(0, 0, 0, 0.12) + ); + background: var(--ha-card-background, var(--card-background-color, white)); + border: 1px solid var(--primary-text-color); + color: var(--primary-text-color); + position: absolute; + z-index: 9999; + } + .tooltip:hover span.tooltiptext { + opacity: 1; + transition-delay: 1.5s; + } ha-icon { display: inline-block; margin: auto; + --mdc-icon-size: 100%; + --iron-icon-width: 100%; + --iron-icon-height: 100%; } ha-card.button-card-main { padding: 4% 0px; @@ -1096,13 +79,14 @@ const nt = new WeakMap(), right: 0; top: 0; bottom: 0; - z-index: 1; + z-index: 50; display: flex; } #lock { -webkit-animation-fill-mode: both; animation-fill-mode: both; margin: unset; + width: 24px; } .invalid { animation: blink 1s cubic-bezier(0.68, -0.55, 0.27, 1.55) infinite; @@ -1113,9 +97,15 @@ const nt = new WeakMap(), transition: visibility 0s 1s, opacity 1s linear; } @keyframes blink { - 0%{opacity:0;} - 50%{opacity:1;} - 100%{opacity:0;} + 0% { + opacity: 0; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0; + } } @-webkit-keyframes rotating /* Safari and Chrome */ { from { @@ -1210,13 +200,13 @@ const nt = new WeakMap(), } #container.vertical { - grid-template-areas: "i" "n" "s" "l"; + grid-template-areas: 'i' 'n' 's' 'l'; grid-template-columns: 1fr; grid-template-rows: 1fr min-content min-content min-content; } /* Vertical No Icon */ #container.vertical.no-icon { - grid-template-areas: "n" "s" "l"; + grid-template-areas: 'n' 's' 'l'; grid-template-columns: 1fr; grid-template-rows: 1fr min-content 1fr; } @@ -1232,7 +222,7 @@ const nt = new WeakMap(), /* Vertical No Icon No Name */ #container.vertical.no-icon.no-name { - grid-template-areas: "s" "l"; + grid-template-areas: 's' 'l'; grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; } @@ -1245,7 +235,7 @@ const nt = new WeakMap(), /* Vertical No Icon No State */ #container.vertical.no-icon.no-state { - grid-template-areas: "n" "l"; + grid-template-areas: 'n' 'l'; grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; } @@ -1258,7 +248,7 @@ const nt = new WeakMap(), /* Vertical No Icon No Label */ #container.vertical.no-icon.no-label { - grid-template-areas: "n" "s"; + grid-template-areas: 'n' 's'; grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; } @@ -1271,7 +261,7 @@ const nt = new WeakMap(), /* Vertical No Icon No Label No Name */ #container.vertical.no-icon.no-label.no-name { - grid-template-areas: "s"; + grid-template-areas: 's'; grid-template-columns: 1fr; grid-template-rows: 1fr; } @@ -1280,7 +270,7 @@ const nt = new WeakMap(), } /* Vertical No Icon No Label No State */ #container.vertical.no-icon.no-label.no-state { - grid-template-areas: "n"; + grid-template-areas: 'n'; grid-template-columns: 1fr; grid-template-rows: 1fr; } @@ -1290,7 +280,7 @@ const nt = new WeakMap(), /* Vertical No Icon No Name No State */ #container.vertical.no-icon.no-name.no-state { - grid-template-areas: "l"; + grid-template-areas: 'l'; grid-template-columns: 1fr; grid-template-rows: 1fr; } @@ -1299,52 +289,52 @@ const nt = new WeakMap(), } #container.icon_name_state { - grid-template-areas: "i n" "l l"; + grid-template-areas: 'i n' 'l l'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr min-content; } #container.icon_name { - grid-template-areas: "i n" "s s" "l l"; + grid-template-areas: 'i n' 's s' 'l l'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr min-content min-content; } #container.icon_state { - grid-template-areas: "i s" "n n" "l l"; + grid-template-areas: 'i s' 'n n' 'l l'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr min-content min-content; } #container.name_state { - grid-template-areas: "i" "n" "l"; + grid-template-areas: 'i' 'n' 'l'; grid-template-columns: 1fr; grid-template-rows: 1fr min-content min-content; } #container.name_state.no-icon { - grid-template-areas: "n" "l"; + grid-template-areas: 'n' 'l'; grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; } #container.name_state.no-icon #name { - align-self: end + align-self: end; } #container.name_state.no-icon #label { - align-self: start + align-self: start; } #container.name_state.no-icon.no-label { - grid-template-areas: "n"; + grid-template-areas: 'n'; grid-template-columns: 1fr; grid-template-rows: 1fr; } #container.name_state.no-icon.no-label #name { - align-self: center + align-self: center; } /* icon_name_state2nd default */ #container.icon_name_state2nd { - grid-template-areas: "i n" "i s" "i l"; + grid-template-areas: 'i n' 'i s' 'i l'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr min-content 1fr; } @@ -1360,7 +350,7 @@ const nt = new WeakMap(), /* icon_name_state2nd No Label */ #container.icon_name_state2nd.no-label { - grid-template-areas: "i n" "i s"; + grid-template-areas: 'i n' 'i s'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr 1fr; } @@ -1373,7 +363,7 @@ const nt = new WeakMap(), /* icon_state_name2nd Default */ #container.icon_state_name2nd { - grid-template-areas: "i s" "i n" "i l"; + grid-template-areas: 'i s' 'i n' 'i l'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr min-content 1fr; } @@ -1389,7 +379,7 @@ const nt = new WeakMap(), /* icon_state_name2nd No Label */ #container.icon_state_name2nd.no-label { - grid-template-areas: "i s" "i n"; + grid-template-areas: 'i s' 'i n'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr 1fr; } @@ -1401,349 +391,149 @@ const nt = new WeakMap(), } #container.icon_label { - grid-template-areas: "i l" "n n" "s s"; + grid-template-areas: 'i l' 'n n' 's s'; grid-template-columns: 40% 1fr; grid-template-rows: 1fr min-content min-content; } - [style*="--aspect-ratio"] > :first-child { + [style*='--aspect-ratio'] > :first-child { width: 100%; } - [style*="--aspect-ratio"] > img { + [style*='--aspect-ratio'] > img { height: auto; } - @supports (--custom:property) { - [style*="--aspect-ratio"] { + @supports (--custom: property) { + [style*='--aspect-ratio'] { position: relative; } - [style*="--aspect-ratio"]::before { - content: ""; + [style*='--aspect-ratio']::before { + content: ''; display: block; padding-bottom: calc(100% / (var(--aspect-ratio))); } - [style*="--aspect-ratio"] > :first-child { + [style*='--aspect-ratio'] > :first-child { position: absolute; top: 0; left: 0; height: 100%; } } -`;console.info("%c BUTTON-CARD \n%c Version 3.1.1 ", "color: orange; font-weight: bold; background: black", "color: white; font-weight: bold; background: dimgray");let re = class extends et { - disconnectedCallback() { - super.disconnectedCallback(), this._clearInterval(); - }connectedCallback() { - if (super.connectedCallback(), this.config && this.config.entity && "timer" === Qt(this.config.entity)) { - const t = this.hass.states[this.config.entity];this._startInterval(t); - } - }static get styles() { - return se; - }render() { - return this._stateObj = this.config.entity ? this.hass.states[this.config.entity] : void 0, this.config && this.hass ? this._cardHtml() : j``; - }shouldUpdate(t) { - const e = !!(this._hasTemplate || this.config.state && this.config.state.find(t => "template" === t.operator) || t.has("_timeRemaining"));return function (t, e, n) { - if (e.has("config") || n) return !0;if (t.config.entity) { - const n = e.get("hass");return !n || n.states[t.config.entity] !== t.hass.states[t.config.entity]; - }return !1; - }(this, t, e); - }updated(t) { - if (super.updated(t), this.config && this.config.entity && "timer" === Qt(this.config.entity) && t.has("hass")) { - const e = this.hass.states[this.config.entity], - n = t.get("hass");(n ? n.states[this.config.entity] : void 0) !== e ? this._startInterval(e) : e || this._clearInterval(); - } - }_clearInterval() { - this._interval && (window.clearInterval(this._interval), this._interval = void 0); - }_startInterval(t) { - this._clearInterval(), this._calculateRemaining(t), "active" === t.state && (this._interval = window.setInterval(() => this._calculateRemaining(t), 1e3)); - }_calculateRemaining(t) { - this._timeRemaining = function (t) { - var e = xt(t.attributes.remaining);if ("active" === t.state) { - var n = new Date().getTime(), - i = new Date(t.last_changed).getTime();e = Math.max(e - (n - i) / 1e3, 0); - }return e; - }(t); - }_computeTimeDisplay(t) { - if (t) return function (t) { - var e = Math.floor(t / 3600), - n = Math.floor(t % 3600 / 60), - i = Math.floor(t % 3600 % 60);return e > 0 ? e + ":" + kt(n) + ":" + kt(i) : n > 0 ? n + ":" + kt(i) : i > 0 ? "" + i : null; - }(this._timeRemaining || xt(t.attributes.duration)); - }_getMatchingConfigState(t) { - if (!this.config.state) return;const e = this.config.state.find(t => "template" === t.operator);if (!t && !e) return;let n;const i = this.config.state.find(e => { - if (!e.operator) return t && this._getTemplateOrValue(t, e.value) == t.state;switch (e.operator) {case "==": - return t && t.state == this._getTemplateOrValue(t, e.value);case "<=": - return t && t.state <= this._getTemplateOrValue(t, e.value);case "<": - return t && t.state < this._getTemplateOrValue(t, e.value);case ">=": - return t && t.state >= this._getTemplateOrValue(t, e.value);case ">": - return t && t.state > this._getTemplateOrValue(t, e.value);case "!=": - return t && t.state != this._getTemplateOrValue(t, e.value);case "regex": - return !(!t || !t.state.match(this._getTemplateOrValue(t, e.value)));case "template": - return this._getTemplateOrValue(t, e.value);case "default": - return n = e, !1;default: - return !1;} - });return !i && n ? n : i; - }_evalTemplate(t, e) { - return new Function("states", "entity", "user", "hass", "variables", `'use strict'; ${e}`).call(this, this.hass.states, t, this.hass.user, this.hass, this.config.variables); - }_objectEvalTemplate(t, e) { - const n = JSON.parse(JSON.stringify(e));return this._getTemplateOrValue(t, n); - }_getTemplateOrValue(t, e) { - if (["number", "boolean"].includes(typeof e)) return e;if (!e) return e;if (["object"].includes(typeof e)) return Object.keys(e).forEach(n => { - e[n] = this._getTemplateOrValue(t, e[n]); - }), e;const n = e.trim();return "[[[" === n.substring(0, 3) && "]]]" === n.slice(-3) ? this._evalTemplate(t, n.slice(3, -3)) : e; - }_getDefaultColorForState(t) { - switch (t.state) {case "on": - return this.config.color_on;case "off": - return this.config.color_off;default: - return this.config.default_color;} - }_getColorForLightEntity(t, e) { - let n = this.config.default_color;return t && (t.attributes.rgb_color ? (n = `rgb(${t.attributes.rgb_color.join(",")})`, t.attributes.brightness && (n = ee(n, (t.attributes.brightness + 245) / 5))) : e && t.attributes.color_temp && t.attributes.min_mireds && t.attributes.max_mireds ? (n = function (t, e, n) { - const i = new Xt("rgb(255, 160, 0)"), - s = new Xt("rgb(166, 209, 255)"), - r = new Xt("white"), - a = (t - e) / (n - e) * 100;return a < 50 ? Kt(s).mix(r, 2 * a).toRgbString() : Kt(r).mix(i, 2 * (a - 50)).toRgbString(); - }(t.attributes.color_temp, t.attributes.min_mireds, t.attributes.max_mireds), t.attributes.brightness && (n = ee(n, (t.attributes.brightness + 245) / 5))) : n = t.attributes.brightness ? ee(this._getDefaultColorForState(t), (t.attributes.brightness + 245) / 5) : this._getDefaultColorForState(t)), n; - }_buildCssColorAttribute(t, e) { - let n, - i = "";return e && e.color ? i = e.color : "auto" !== this.config.color && t && "off" === t.state ? i = this.config.color_off : this.config.color && (i = this.config.color), n = "auto" == i || "auto-no-temperature" == i ? this._getColorForLightEntity(t, "auto-no-temperature" !== i) : i || (t ? this._getDefaultColorForState(t) : this.config.default_color), n; - }_buildIcon(t, e) { - if (!this.config.show_icon) return;let n;return e && e.icon ? n = e.icon : this.config.icon ? n = this.config.icon : t && t.attributes && (n = t.attributes.icon ? t.attributes.icon : function (t, e) { - if (t in Ct) return Ct[t];switch (t) {case "alarm_control_panel": - switch (e) {case "armed_home": - return "hass:bell-plus";case "armed_night": - return "hass:bell-sleep";case "disarmed": - return "hass:bell-outline";case "triggered": - return "hass:bell-ring";default: - return "hass:bell";}case "binary_sensor": - return e && "off" === e ? "hass:radiobox-blank" : "hass:checkbox-marked-circle";case "cover": - return "closed" === e ? "hass:window-closed" : "hass:window-open";case "lock": - return e && "unlocked" === e ? "hass:lock-open" : "hass:lock";case "media_player": - return e && "off" !== e && "idle" !== e ? "hass:cast-connected" : "hass:cast";case "zwave": - switch (e) {case "dead": - return "hass:emoticon-dead";case "sleeping": - return "hass:sleep";case "initializing": - return "hass:timer-sand";default: - return "hass:z-wave";}default: - return console.warn("Unable to find icon for domain " + t + " (" + e + ")"), "hass:bookmark";} - }(Qt(t.entity_id), t.state)), this._getTemplateOrValue(t, n); - }_buildEntityPicture(t, e) { - if (!this.config.show_entity_picture || !t && !e && !this.config.entity_picture) return;let n;return e && e.entity_picture ? n = e.entity_picture : this.config.entity_picture ? n = this.config.entity_picture : t && (n = t.attributes && t.attributes.entity_picture ? t.attributes.entity_picture : void 0), this._getTemplateOrValue(t, n); - }_buildStyleGeneric(t, e, n) { - let i = {};if (this.config.styles && this.config.styles[n] && (i = Object.assign(i, ...this.config.styles[n])), e && e.styles && e.styles[n]) { - let t = {};t = Object.assign(t, ...e.styles[n]), i = Object.assign(Object.assign({}, i), t); - }return Object.keys(i).forEach(e => { - i[e] = this._getTemplateOrValue(t, i[e]); - }), i; - }_buildCustomStyleGeneric(t, e, n) { - let i = {};if (this.config.styles && this.config.styles.custom_fields && this.config.styles.custom_fields[n] && (i = Object.assign(i, ...this.config.styles.custom_fields[n])), e && e.styles && e.styles.custom_fields && e.styles.custom_fields[n]) { - let t = {};t = Object.assign(t, ...e.styles.custom_fields[n]), i = Object.assign(Object.assign({}, i), t); - }return Object.keys(i).forEach(e => { - i[e] = this._getTemplateOrValue(t, i[e]); - }), i; - }_buildName(t, e) { - if (!1 === this.config.show_name) return;let n;var i;return e && e.name ? n = e.name : this.config.name ? n = this.config.name : t && (n = t.attributes && t.attributes.friendly_name ? t.attributes.friendly_name : (i = t.entity_id).substr(i.indexOf(".") + 1)), this._getTemplateOrValue(t, n); - }_buildStateString(t) { - let e;if (this.config.show_state && t && t.state) { - const n = ((t, e) => { - let n;const i = Qt(e.entity_id);return "binary_sensor" === i ? (e.attributes.device_class && (n = t(`state.${i}.${e.attributes.device_class}.${e.state}`)), n || (n = t(`state.${i}.default.${e.state}`))) : n = e.attributes.unit_of_measurement && !["unknown", "unavailable"].includes(e.state) ? e.state : "zwave" === i ? ["initializing", "dead"].includes(e.state) ? t(`state.zwave.query_stage.${e.state}`, "query_stage", e.attributes.query_stage) : t(`state.zwave.default.${e.state}`) : t(`state.${i}.${e.state}`), n || (n = t(`state.default.${e.state}`) || t(`component.${i}.state.${e.state}`) || e.state), n; - })(this.hass.localize, t), - i = this._buildUnits(t);i ? e = `${t.state} ${i}` : "timer" === Qt(t.entity_id) ? "idle" === t.state || 0 === this._timeRemaining ? e = n : (e = this._computeTimeDisplay(t), "paused" === t.state && (e += ` (${n})`)) : e = n; - }return e; - }_buildUnits(t) { - let e;return t && this.config.show_units && (e = t.attributes && t.attributes.unit_of_measurement && !this.config.units ? t.attributes.unit_of_measurement : this.config.units ? this.config.units : void 0), e; - }_buildLastChanged(t, e) { - return this.config.show_last_changed && t ? j` - ` : void 0; - }_buildLabel(t, e) { - if (!this.config.show_label) return;let n;return n = e && e.label ? e.label : this.config.label, this._getTemplateOrValue(t, n); - }_buildCustomFields(t, e) { - let n = j``;const i = {}, - s = {};return this.config.custom_fields && Object.keys(this.config.custom_fields).forEach(e => { - const n = this.config.custom_fields[e];n.card ? s[e] = this._objectEvalTemplate(t, n.card) : i[e] = this._getTemplateOrValue(t, n); - }), e && e.custom_fields && Object.keys(e.custom_fields).forEach(n => { - const r = e.custom_fields[n];r.card ? s[n] = this._objectEvalTemplate(t, r.card) : i[n] = this._getTemplateOrValue(t, r); - }), Object.keys(i).forEach(s => { - if (null != i[s]) { - const r = Object.assign(Object.assign({}, this._buildCustomStyleGeneric(t, e, s)), { "grid-area": s });n = j`${n} -
${rt(i[s])}
`; - } - }), Object.keys(s).forEach(i => { - if (null != s[i]) { - const r = Object.assign(Object.assign({}, this._buildCustomStyleGeneric(t, e, i)), { "grid-area": i }), - a = function (t) { - var e = function (t, e) { - return n("hui-error-card", { type: "error", error: t, config: e }); - }, - n = function (t, n) { - var i = window.document.createElement(t);try { - i.setConfig(n); - } catch (i) { - return console.error(t, i), e(i.message, n); - }return i; - };if (!t || "object" != typeof t || !t.type) return e("No type defined", t);var i = t.type;if (i = i.startsWith("custom:") ? i.substr("custom:".length) : "hui-" + i + "-card", customElements.get(i)) return n(i, t);var s = e("Custom element doesn't exist: " + t.type + ".", t);s.style.display = "None";var r = setTimeout(function () { - s.style.display = ""; - }, 2e3);return customElements.whenDefined(t.type).then(function () { - clearTimeout(r), Tt(s, "ll-rebuild", {}, s); - }), s; - }(s[i]);a.hass = this.hass, n = j`${n} -
${a}
`; - } - }), n; - }_isClickable(t) { - let e = !0;if ("toggle" === this.config.tap_action.action && "none" === this.config.hold_action.action && "none" === this.config.double_tap_action.action || "toggle" === this.config.hold_action.action && "none" === this.config.tap_action.action && "none" === this.config.double_tap_action.action || "toggle" === this.config.double_tap_action.action && "none" === this.config.tap_action.action && "none" === this.config.hold_action.action) { - if (t) switch (Qt(t.entity_id)) {case "sensor":case "binary_sensor":case "device_tracker": - e = !1;break;default: - e = !0;} else e = !1; - } else e = "none" != this.config.tap_action.action || "none" != this.config.hold_action.action || "none" != this.config.double_tap_action.action;return e; - }_rotate(t) { - return !(!t || !t.spin); - }_blankCardColoredHtml(t) { - const e = Object.assign({ background: "none", "box-shadow": "none" }, t);return j` - +`;const Ae=(t,e,i,n)=>{if(!((t,e,i)=>{const[n,r]=t.split(".",2);return Number(n)>e||Number(n)===e&&Number(r)>=i})(t.config.version,0,109))return function(t,e){let i;const n=ke(e.entity_id);return"binary_sensor"===n?(e.attributes.device_class&&(i=t(`state.${n}.${e.attributes.device_class}.${e.state}`)),i||(i=t(`state.${n}.default.${e.state}`))):i=e.attributes.unit_of_measurement&&!["unknown","unavailable"].includes(e.state)?e.state:"zwave"===n?["initializing","dead"].includes(e.state)?t("state.zwave.query_stage."+e.state,"query_stage",e.attributes.query_stage):t("state.zwave.default."+e.state):t(`state.${n}.${e.state}`),i||(i=t("state.default."+e.state)||t(`component.${n}.state.${e.state}`)||e.state),i}(e,i);if("unknown"===i.state||"unavailable"===i.state)return e("state.default."+i.state);if(i.attributes.unit_of_measurement)return`${i.state} ${i.attributes.unit_of_measurement}`;const r=ke(i.entity_id);if("input_datetime"===r){let t;if(!i.attributes.has_time)return t=new Date(i.attributes.year,i.attributes.month-1,i.attributes.day),jt(t,n);if(!i.attributes.has_date){const e=new Date;return t=new Date(e.getFullYear(),e.getMonth(),e.getDay(),i.attributes.hour,i.attributes.minute),Ht(t,n)}return t=new Date(i.attributes.year,i.attributes.month-1,i.attributes.day,i.attributes.hour,i.attributes.minute),$t(t,n)}return i.attributes.device_class&&e(`component.${r}.state.${i.attributes.device_class}.${i.state}`)||e(`component.${r}.state._.${i.state}`)||i.state};var Ne=Function.prototype.toString,Pe=Object.create,je=Object.defineProperty,$e=Object.getOwnPropertyDescriptor,He=Object.getOwnPropertyNames,Re=Object.getOwnPropertySymbols,Ve=Object.getPrototypeOf,De=Object.prototype,Le=De.hasOwnProperty,Fe=De.propertyIsEnumerable,Ie="function"==typeof Re,ze="function"==typeof WeakMap,Ue=function(t,e){if(!t.constructor)return Pe(null);var i=t.constructor,n=t.__proto__||Ve(t);if(i===e.Object)return n===e.Object.prototype?{}:Pe(n);if(~Ne.call(i).indexOf("[native code]"))try{return new i}catch(r){}return Pe(n)},qe=function(t,e,i,n){var r=Ue(t,e);for(var s in n.set(t,r),t)Le.call(t,s)&&(r[s]=i(t[s],n));if(Ie){var a=Re(t),o=a.length;if(o)for(var l=0,c=void 0;l{Ze&&t(),window.loadCardHelpers&&(Ze=await window.loadCardHelpers(),window.cardHelpers=Ze,t())}));console.info("%c BUTTON-CARD \n%c Version 3.4.0 ","color: orange; font-weight: bold; background: black","color: white; font-weight: bold; background: dimgray");let Xe=class extends nt{constructor(){super(...arguments),this._cards={},this._cardsConfig={},this._entities=[],this._initial_setup_complete=!1,this._rippleHandlers=new rt((()=>this._ripple))}set hass(t){this._hass=t,Object.keys(this._cards).forEach((t=>{this._cards[t].hass=this._hass})),this._initial_setup_complete||this._initConnected()}disconnectedCallback(){super.disconnectedCallback(),this._clearInterval()}connectedCallback(){super.connectedCallback(),this._initial_setup_complete?this._startTimerCountdown():this._initConnected()}_initConnected(){void 0!==this._hass&&void 0!==this._config&&this.isConnected&&(this._initial_setup_complete=!0,this._startTimerCountdown())}_startTimerCountdown(){if(this._config&&this._config.entity&&"timer"===ke(this._config.entity)){const t=this._hass.states[this._config.entity];this._startInterval(t)}}_createCard(t){if(Ze)return Ze.createCardElement(t);{const e=function(t,e){void 0===e&&(e=!1);var i=function(t,e){return n("hui-error-card",{type:"error",error:t,config:e})},n=function(t,e){var n=window.document.createElement(t);try{n.setConfig(e)}catch(n){return console.error(t,n),i(n.message,e)}return n};if(!t||"object"!=typeof t||!e&&!t.type)return i("No type defined",t);var r=t.type;if(r&&r.startsWith("custom:"))r=r.substr("custom:".length);else if(e)if(zt.has(r))r="hui-"+r+"-row";else{if(!t.entity)return i("Invalid config given.",t);var s=t.entity.split(".",1)[0];r="hui-"+(Ut[s]||"text")+"-entity-row"}else r="hui-"+r+"-card";if(customElements.get(r))return n(r,t);var a=i("Custom element doesn't exist: "+t.type+".",t);a.style.display="None";var o=setTimeout((function(){a.style.display=""}),2e3);return customElements.whenDefined(t.type).then((function(){clearTimeout(o),It(a,"ll-rebuild",{},a)})),a}(t);return Je.then((()=>{It(e,"ll-rebuild",{})})),e}}static get styles(){return Ee}render(){if(!this._config||!this._hass)return D``;this._stateObj=this._config.entity?this._hass.states[this._config.entity]:void 0;try{return this._evaledVariables=this._config.variables?this._objectEvalTemplate(this._stateObj,this._config.variables):void 0,this._cardHtml()}catch(t){t.stack?console.error(t.stack):console.error(t);const e=document.createElement("hui-error-card");return e.setConfig({type:"error",error:t.toString(),origConfig:this._config}),D` ${e} `}}shouldUpdate(t){return!(!this._hasTemplate&&!t.has("_timeRemaining")&&!function(t,e){if(e.has("_config"))return!0;const i=e.get("_hass");if(i)return t._entities.some((function(e){return(null==i?void 0:i.states[e])!==t._hass.states[e]}));return!1}(this,t))&&(this._expandTriggerGroups(),!0)}updated(t){if(super.updated(t),this._config&&this._config.entity&&"timer"===ke(this._config.entity)&&t.has("_hass")){const e=this._hass.states[this._config.entity],i=t.get("_hass");(i?i.states[this._config.entity]:void 0)!==e?this._startInterval(e):e||this._clearInterval()}}_clearInterval(){this._interval&&(window.clearInterval(this._interval),this._interval=void 0)}_startInterval(t){this._clearInterval(),this._calculateRemaining(t),"active"===t.state&&(this._interval=window.setInterval((()=>this._calculateRemaining(t)),1e3))}_calculateRemaining(t){t.attributes.remaining&&(this._timeRemaining=function(t){var e=Pt(t.attributes.remaining);if("active"===t.state){var i=(new Date).getTime(),n=new Date(t.last_changed).getTime();e=Math.max(e-(i-n)/1e3,0)}return e}(t))}_computeTimeDisplay(t){if(t)return function(t){var e=Math.floor(t/3600),i=Math.floor(t%3600/60),n=Math.floor(t%3600%60);return e>0?e+":"+Rt(i)+":"+Rt(n):i>0?i+":"+Rt(n):n>0?""+n:null}(this._timeRemaining||Pt(t.attributes.duration))}_getMatchingConfigState(t){if(!this._config.state)return;const e=this._config.state.find((t=>"template"===t.operator));if(!t&&!e)return;let i;const n=this._config.state.find((e=>{if(!e.operator)return t&&this._getTemplateOrValue(t,e.value)==t.state;switch(e.operator){case"==":return t&&t.state==this._getTemplateOrValue(t,e.value);case"<=":return t&&t.state<=this._getTemplateOrValue(t,e.value);case"<":return t&&t.state=":return t&&t.state>=this._getTemplateOrValue(t,e.value);case">":return t&&t.state>this._getTemplateOrValue(t,e.value);case"!=":return t&&t.state!=this._getTemplateOrValue(t,e.value);case"regex":return!(!t||!t.state.match(this._getTemplateOrValue(t,e.value)));case"template":return this._getTemplateOrValue(t,e.value);case"default":return i=e,!1;default:return!1}}));return!n&&i?i:n}_evalTemplate(t,e){try{return new Function("states","entity","user","hass","variables","html","'use strict'; "+e).call(this,this._hass.states,t,this._hass.user,this._hass,this._evaledVariables,D)}catch(i){const t=e.length<=100?e.trim():e.trim().substring(0,98)+"...";throw i.message=`${i.name}: ${i.message} in '${t}'`,i.name="ButtonCardJSTemplateError",i}}_objectEvalTemplate(t,e){const i=Ge(e);return this._getTemplateOrValue(t,i)}_getTemplateOrValue(t,e){if(["number","boolean"].includes(typeof e))return e;if(!e)return e;if("object"==typeof e)return Object.keys(e).forEach((i=>{e[i]=this._getTemplateOrValue(t,e[i])})),e;const i=e.trim();return"[[["===i.substring(0,3)&&"]]]"===i.slice(-3)?this._evalTemplate(t,i.slice(3,-3)):e}_getDefaultColorForState(t){switch(t.state){case"on":return this._config.color_on;case"off":return this._config.color_off;default:return this._config.default_color}}_getColorForLightEntity(t,e){let i=this._config.default_color;return t&&("on"===t.state?t.attributes.rgb_color?(i=`rgb(${t.attributes.rgb_color.join(",")})`,t.attributes.brightness&&(i=Te(i,(t.attributes.brightness+245)/5))):e&&t.attributes.color_temp&&t.attributes.min_mireds&&t.attributes.max_mireds?(i=function(t,e,i){const n=new Se("rgb(255, 160, 0)"),r=new Se("rgb(166, 209, 255)"),s=new Se("white"),a=(t-e)/(i-e)*100;return a<50?xe(r).mix(s,2*a).toRgbString():xe(s).mix(n,2*(a-50)).toRgbString()}(t.attributes.color_temp,t.attributes.min_mireds,t.attributes.max_mireds),t.attributes.brightness&&(i=Te(i,(t.attributes.brightness+245)/5))):i=t.attributes.brightness?Te(this._getDefaultColorForState(t),(t.attributes.brightness+245)/5):this._getDefaultColorForState(t):i=this._getDefaultColorForState(t)),i}_buildCssColorAttribute(t,e){let i,n="";return(null==e?void 0:e.color)?n=e.color:"auto"!==this._config.color&&"off"===(null==t?void 0:t.state)?n=this._config.color_off:this._config.color&&(n=this._config.color),i="auto"==n||"auto-no-temperature"==n?this._getColorForLightEntity(t,"auto-no-temperature"!==n):n||(t?this._getDefaultColorForState(t):this._config.default_color),i}_buildIcon(t,e){if(!this._config.show_icon)return;let i;if(null==e?void 0:e.icon)i=e.icon;else if(this._config.icon)i=this._config.icon;else{if(!t)return;i=function(t){if(!t)return Dt;if(t.attributes.icon)return t.attributes.icon;var e=Vt(t.entity_id);return e in Jt?Jt[e](t):Yt(e,t.state)}(t)}return this._getTemplateOrValue(t,i)}_buildEntityPicture(t,e){if(!this._config.show_entity_picture||!t&&!e&&!this._config.entity_picture)return;let i;return(null==e?void 0:e.entity_picture)?i=e.entity_picture:this._config.entity_picture?i=this._config.entity_picture:t&&(i=t.attributes&&t.attributes.entity_picture?t.attributes.entity_picture:void 0),this._getTemplateOrValue(t,i)}_buildStyleGeneric(t,e,i){var n,r;let s={};if((null===(n=this._config.styles)||void 0===n?void 0:n[i])&&(s=Object.assign(s,...this._config.styles[i])),null===(r=null==e?void 0:e.styles)||void 0===r?void 0:r[i]){let t={};t=Object.assign(t,...e.styles[i]),s=Object.assign(Object.assign({},s),t)}return Object.keys(s).forEach((e=>{s[e]=this._getTemplateOrValue(t,s[e])})),s}_buildCustomStyleGeneric(t,e,i){var n,r,s,a;let o={};if((null===(r=null===(n=this._config.styles)||void 0===n?void 0:n.custom_fields)||void 0===r?void 0:r[i])&&(o=Object.assign(o,...this._config.styles.custom_fields[i])),null===(a=null===(s=null==e?void 0:e.styles)||void 0===s?void 0:s.custom_fields)||void 0===a?void 0:a[i]){let t={};t=Object.assign(t,...e.styles.custom_fields[i]),o=Object.assign(Object.assign({},o),t)}return Object.keys(o).forEach((e=>{o[e]=this._getTemplateOrValue(t,o[e])})),o}_buildName(t,e){if(!1===this._config.show_name)return;let i;var n;return(null==e?void 0:e.name)?i=e.name:this._config.name?i=this._config.name:t&&(i=t.attributes&&t.attributes.friendly_name?t.attributes.friendly_name:(n=t.entity_id).substr(n.indexOf(".")+1)),this._getTemplateOrValue(t,i)}_buildStateString(t){var e;let i;if(this._config.show_state&&t&&t.state){const n=this._buildUnits(t);n?i=`${t.state} ${n}`:"timer"===ke(t.entity_id)?"idle"===t.state||0===this._timeRemaining?i=Ae(this._hass,this._hass.localize,t,this._hass.language):(i=this._computeTimeDisplay(t),"paused"===t.state&&(i+=` (${Ae(this._hass,this._hass.localize,t,this._hass.language)})`)):i=(null===(e=this._config)||void 0===e?void 0:e.show_units)||"sensor"!==ke(t.entity_id)?Ae(this._hass,this._hass.localize,t,this._hass.language):t.state}return i}_buildUnits(t){var e;let i;return t&&this._config.show_units&&(i=(null===(e=t.attributes)||void 0===e?void 0:e.unit_of_measurement)&&!this._config.units?t.attributes.unit_of_measurement:this._config.units?this._config.units:void 0),i}_buildLastChanged(t,e){return this._config.show_last_changed&&t?D` + + `:void 0}_buildLabel(t,e){if(!this._config.show_label)return;let i;return i=(null==e?void 0:e.label)?e.label:this._config.label,this._getTemplateOrValue(t,i)}_buildCustomFields(t,e){let i=D``;const n={},r={};return this._config.custom_fields&&Object.keys(this._config.custom_fields).forEach((e=>{const i=this._config.custom_fields[e];i.card?r[e]=this._objectEvalTemplate(t,i.card):n[e]=this._getTemplateOrValue(t,i)})),(null==e?void 0:e.custom_fields)&&Object.keys(e.custom_fields).forEach((i=>{const s=e.custom_fields[i];s.card?r[i]=this._objectEvalTemplate(t,s.card):n[i]=this._getTemplateOrValue(t,s)})),Object.keys(n).forEach((r=>{if(null!=n[r]){const s=Object.assign(Object.assign({},this._buildCustomStyleGeneric(t,e,r)),{"grid-area":r});i=D` + ${i} +
+ ${n[r]&&"html"===n[r].type?n[r]:lt(n[r])} +
+ `}})),Object.keys(r).forEach((n=>{if(null!=r[n]){const s=Object.assign(Object.assign({},this._buildCustomStyleGeneric(t,e,n)),{"grid-area":n});let a;Kt(this._cardsConfig[n],r[n])?a=this._cards[n]:(a=this._createCard(r[n]),this._cards[n]=a,this._cardsConfig[n]=Ge(r[n])),a.hass=this._hass,i=D` + ${i} +
+ ${a} +
+ `}})),i}_isClickable(t){let e=!0;const i=this._getTemplateOrValue(t,this._config.tap_action.action),n=this._getTemplateOrValue(t,this._config.hold_action.action),r=this._getTemplateOrValue(t,this._config.double_tap_action.action);return e="none"!=i||"none"!=n||"none"!=r,e}_rotate(t){return!!(null==t?void 0:t.spin)}_blankCardColoredHtml(t){const e=Object.assign({background:"none","box-shadow":"none"},t);return D` +
- `; - }_cardHtml() { - const t = this._getMatchingConfigState(this._stateObj), - e = this._buildCssColorAttribute(this._stateObj, t);let n = e, - i = {}, - s = {};const r = {}, - a = this._buildStyleGeneric(this._stateObj, t, "lock"), - o = this._buildStyleGeneric(this._stateObj, t, "card"), - l = { "button-card-main": !0, disabled: !this._isClickable(this._stateObj) };switch (o.width && (this.style.setProperty("flex", "0 0 auto"), this.style.setProperty("max-width", "fit-content")), this.config.color_type) {case "blank-card": - return this._blankCardColoredHtml(o);case "card":case "label-card": - { - const t = function (t) { - const e = new Xt(te(t));return e.isValid && e.getLuminance() > .5 ? "rgb(62, 62, 62)" : "rgb(234, 234, 234)"; - }(e);i.color = t, s.color = t, i["background-color"] = e, i = Object.assign(Object.assign({}, i), o), n = "inherit";break; - }default: - i = o;}return this.config.aspect_ratio ? (r["--aspect-ratio"] = this.config.aspect_ratio, i.position = "absolute") : r.display = "inline", this.style.setProperty("--button-card-light-color", this._getColorForLightEntity(this._stateObj, !0)), this.style.setProperty("--button-card-light-color-no-temperature", this._getColorForLightEntity(this._stateObj, !1)), s = Object.assign(Object.assign({}, s), a), j` -
+ `}_cardHtml(){var t,e;const i=this._getMatchingConfigState(this._stateObj),n=this._buildCssColorAttribute(this._stateObj,i);let r=n,s={},a={};const o={},l=this._buildStyleGeneric(this._stateObj,i,"lock"),c=this._buildStyleGeneric(this._stateObj,i,"card"),h=this._buildStyleGeneric(this._stateObj,i,"tooltip"),u={"button-card-main":!0,tooltip:!!(null===(t=this._config)||void 0===t?void 0:t.tooltip),disabled:!this._isClickable(this._stateObj)};switch(c.width&&(this.style.setProperty("flex","0 0 auto"),this.style.setProperty("max-width","fit-content")),this._config.color_type){case"blank-card":return this._blankCardColoredHtml(c);case"card":case"label-card":{const t=function(t){const e=new Se(Oe(t));return e.isValid&&e.getLuminance()>.5?"rgb(62, 62, 62)":"rgb(234, 234, 234)"}(n);s.color=t,a.color=t,s["background-color"]=n,s=Object.assign(Object.assign({},s),c),r="inherit";break}default:s=c}this._config.aspect_ratio?(o["--aspect-ratio"]=this._config.aspect_ratio,s.position="absolute"):o.display="inline",this.style.setProperty("--button-card-light-color",this._getColorForLightEntity(this._stateObj,!0)),this.style.setProperty("--button-card-light-color-no-temperature",this._getColorForLightEntity(this._stateObj,!1)),a=Object.assign(Object.assign({},a),l);const d=this._config.extra_styles?D` + + `:D``;return D` + ${d} +
- ${this._buttonContent(this._stateObj, t, n)} - ${this._getLock(s)} + ${this._buttonContent(this._stateObj,i,r)} + ${(null===(e=this._config)||void 0===e?void 0:e.tooltip)?D` + ${this._getTemplateOrValue(this._stateObj,this._config.tooltip)} + `:""} +
- `; - }_getLock(t) { - return this.config.lock && this._getTemplateOrValue(this._stateObj, this.config.lock.enabled) ? j` -
this._handleUnlockType(t, "tap")} - @ha-hold=${t => this._handleUnlockType(t, "hold")} - @ha-dblclick=${t => this._handleUnlockType(t, "double_tap")} - .hasDblClick=${"double_tap" === this.config.lock.unlock} - .longpress=${$t()} - .config="${this.config}" + ${this._getLock(a)} + `}_getLock(t){return this._config.lock&&this._getTemplateOrValue(this._stateObj,this._config.lock.enabled)?D` +
- ` : j``; - }_buttonContent(t, e, n) { - const i = this._buildName(t, e), - s = this._buildStateString(t), - r = function (t, e) { - if (!t && !e) return;let n;return n = e ? t ? `${t}: ${e}` : e : t, n; - }(i, s);switch (this.config.layout) {case "icon_name_state":case "name_state": - return this._gridHtml(t, e, this.config.layout, n, r, void 0);default: - return this._gridHtml(t, e, this.config.layout, n, i, s);} - }_gridHtml(t, e, n, i, s, r) { - const a = this._getIconHtml(t, e, i), - o = [n], - l = this._buildLabel(t, e), - c = this._buildStyleGeneric(t, e, "name"), - h = this._buildStyleGeneric(t, e, "state"), - u = this._buildStyleGeneric(t, e, "label"), - d = this._buildLastChanged(t, u), - f = this._buildStyleGeneric(t, e, "grid");return a || o.push("no-icon"), s || o.push("no-name"), r || o.push("no-state"), l || d || o.push("no-label"), j` -
- ${a || ""} - ${s ? j`
${rt(s)}
` : ""} - ${r ? j`
${r}
` : ""} - ${l && !d ? j`
${rt(l)}
` : ""} - ${d || ""} - ${this._buildCustomFields(t, e)} + `:D``}_buttonContent(t,e,i){const n=this._buildName(t,e),r=this._config.show_state&&this._config.state_display?this._getTemplateOrValue(t,this._config.state_display):void 0,s=r||this._buildStateString(t),a=function(t,e){if(!t&&!e)return;let i;return i=e?t?`${t}: ${e}`:e:t,i}(n,s);switch(this._config.layout){case"icon_name_state":case"name_state":return this._gridHtml(t,e,this._config.layout,i,a,void 0);default:return this._gridHtml(t,e,this._config.layout,i,n,s)}}_gridHtml(t,e,i,n,r,s){const a=this._getIconHtml(t,e,n),o=[i],l=this._buildLabel(t,e),c=this._buildStyleGeneric(t,e,"name"),h=this._buildStyleGeneric(t,e,"state"),u=this._buildStyleGeneric(t,e,"label"),d=this._buildLastChanged(t,u),p=this._buildStyleGeneric(t,e,"grid");return a||o.push("no-icon"),r||o.push("no-name"),s||o.push("no-state"),l||d||o.push("no-label"),D` +
+ ${a||""} + ${r?D` +
+ ${"html"===r.type?r:lt(r)} +
+ `:""} + ${s?D` +
+ ${"html"===s.type?s:lt(s)} +
+ `:""} + ${l&&!d?D` +
+ ${"html"===l.type?l:lt(l)} +
+ `:""} + ${d||""} ${this._buildCustomFields(t,e)}
- `; - }_getIconHtml(t, e, n) { - const i = this._buildIcon(t, e), - s = this._buildEntityPicture(t, e), - r = this._buildStyleGeneric(t, e, "entity_picture"), - a = this._buildStyleGeneric(t, e, "icon"), - o = this._buildStyleGeneric(t, e, "img_cell"), - l = this._buildStyleGeneric(t, e, "card"), - c = Object.assign({ color: n, width: this.config.size, position: this.config.aspect_ratio || l.height ? "absolute" : "relative" }, a), - h = Object.assign(Object.assign({}, c), r);return i || s ? j` -
- ${i && !s ? j`` : ""} - ${s ? j`` : ""} + `}_getIconHtml(t,e,i){const n=this._buildIcon(t,e),r=this._buildEntityPicture(t,e),s=this._buildStyleGeneric(t,e,"entity_picture"),a=this._buildStyleGeneric(t,e,"icon"),o=this._buildStyleGeneric(t,e,"img_cell"),l=this._buildStyleGeneric(t,e,"card"),c=Object.assign({color:i,width:this._config.size,position:this._config.aspect_ratio||l.height?"absolute":"relative"},a),h=Object.assign(Object.assign({},c),s),u=this._buildLiveStream(h);return n||r?D` +
+ ${!n||r||u?"":D` + + `} + ${u||""} + ${r&&!u?D` + + `:""}
- ` : void 0; - }setConfig(t) { - if (!t) throw new Error("Invalid configuration");const e = 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; - }() || function () { - let t = document.querySelector("hc-main");if (t = t && t.shadowRoot, t = t && t.querySelector("hc-lovelace"), t = t && t.shadowRoot, t = t && t.querySelector("hui-view"), t) { - const e = t.lovelace;return e.current_view = t.___curView, e; - }return null; - }();let n = Object.assign({}, t), - i = n.template, - s = t.state;for (; i && e.config.button_card_templates && e.config.button_card_templates[i];) n = ne(e.config.button_card_templates[i], n), s = ie(e.config.button_card_templates[i].state, s), i = e.config.button_card_templates[i].template;n.state = s, this.config = Object.assign({ tap_action: { action: "toggle" }, hold_action: { action: "none" }, double_tap_action: { action: "none" }, layout: "vertical", size: "40%", color_type: "icon", show_name: !0, show_state: !1, show_icon: !0, show_units: !0, show_label: !1, show_entity_picture: !1 }, n), this.config.lock = Object.assign({ enabled: !1, duration: 5, unlock: "tap" }, this.config.lock), this.config.default_color = "var(--primary-text-color)", "icon" !== this.config.color_type ? this.config.color_off = "var(--paper-card-background-color)" : this.config.color_off = "var(--paper-item-icon-color)", this.config.color_on = "var(--paper-item-icon-active-color)";const r = JSON.stringify(this.config), - a = new RegExp("\\[\\[\\[.*\\]\\]\\]", "gm");this._hasTemplate = !!r.match(a); - }getCardSize() { - return 3; - }_evalActions(t, e) { - const n = this.config.entity ? this.hass.states[this.config.entity] : void 0, - i = JSON.parse(JSON.stringify(t)), - s = t => t ? (Object.keys(t).forEach(e => { - "object" == typeof t[e] ? t[e] = s(t[e]) : t[e] = this._getTemplateOrValue(n, t[e]); - }), t) : t;return i[e] = s(i[e]), !i[e].confirmation && i.confirmation && (i[e].confirmation = s(i.confirmation)), i; - }_handleTap(t) { - const e = t.target.config;Et(this, this.hass, this._evalActions(e, "tap_action"), !1, !1); - }_handleHold(t) { - const e = t.target.config;Et(this, this.hass, this._evalActions(e, "hold_action"), !0, !1); - }_handleDblTap(t) { - const e = t.target.config;Et(this, this.hass, this._evalActions(e, "double_tap_action"), !1, !0); - }_handleUnlockType(t, e) { - t.target.config.lock.unlock === e && this._handleLock(t); - }_handleLock(t) { - t.stopPropagation();const e = this.shadowRoot.getElementById("lock");if (!e) return;if (this.config.lock.exemptions) { - if (!this.hass.user.name || !this.hass.user.id) return;let t = !1;if (this.config.lock.exemptions.forEach(e => { - (!t && e.user === this.hass.user.id || e.username === this.hass.user.name) && (t = !0); - }), !t) return e.classList.add("invalid"), void window.setTimeout(() => { - e && e.classList.remove("invalid"); - }, 3e3); - }const n = this.shadowRoot.getElementById("overlay"), - i = this.shadowRoot.getElementById("card");n.style.setProperty("pointer-events", "none");const s = document.createElement("paper-ripple");if (e) { - i.appendChild(s);const t = document.createAttribute("icon");t.value = "mdi:lock-open-outline", e.attributes.setNamedItem(t), e.classList.add("hidden"); - }window.setTimeout(() => { - if (n.style.setProperty("pointer-events", ""), e) { - e.classList.remove("hidden");const t = document.createAttribute("icon");t.value = "mdi:lock-outline", e.attributes.setNamedItem(t), i.removeChild(s); - } - }, 1e3 * this.config.lock.duration); - }_stopPropagation(t) { - t.stopPropagation(); - } -};var ae;t([Z()], re.prototype, "hass", void 0), t([Z()], re.prototype, "config", void 0), t([Z()], re.prototype, "_timeRemaining", void 0), t([Z()], re.prototype, "_hasTemplate", void 0), t([Z()], re.prototype, "_stateObj", void 0), re = t([(ae = "button-card", t => "function" == typeof t ? ((t, e) => (window.customElements.define(t, e), e))(ae, t) : ((t, e) => { - const { kind: n, elements: i } = e;return { kind: n, elements: i, finisher(e) { - window.customElements.define(t, e); - } }; -})(ae, t))], re); + `:void 0}_buildLiveStream(t){return this._config.show_live_stream&&this._config.entity&&"camera"===ke(this._config.entity)?D` + + `:void 0}_configFromLLTemplates(t,e){const i=e.template;if(!i)return e;let n,r={};const s=i&&Array.isArray(i)?i:[i];return null==s||s.forEach((e=>{var i;if(!(null===(i=t.config.button_card_templates)||void 0===i?void 0:i[e]))throw new Error(`Button-card template '${e}' is missing!`);const s=this._configFromLLTemplates(t,t.config.button_card_templates[e]);r=Me(r,s),n=Ce(n,s.state)})),r=Me(r,e),r.state=Ce(n,e.state),r}setConfig(t){if(!t)throw new Error("Invalid configuration");this._cards={},this._cardsConfig={};const e=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}()||function(){let t=document.querySelector("hc-main");if(t=t&&t.shadowRoot,t=t&&t.querySelector("hc-lovelace"),t=t&&t.shadowRoot,t=t&&(t.querySelector("hui-view")||t.querySelector("hui-panel-view")),t){const e=t.lovelace;return e.current_view=t.___curView,e}return null}();let i=Ge(t);i=this._configFromLLTemplates(e,i),this._config=Object.assign(Object.assign({type:"custom:button-card",group_expand:!1,hold_action:{action:"none"},double_tap_action:{action:"none"},layout:"vertical",size:"40%",color_type:"icon",show_name:!0,show_state:!1,show_icon:!0,show_units:!0,show_label:!1,show_entity_picture:!1,show_live_stream:!1,card_size:3},i),{default_color:"DUMMY",color_off:"DUMMY",color_on:"DUMMY",lock:Object.assign({enabled:!1,duration:5,unlock:"tap"},i.lock)}),this._config.entity&&Ft.has(ke(this._config.entity))?this._config=Object.assign({tap_action:{action:"toggle"}},this._config):this._config=Object.assign({tap_action:{action:"more-info"}},this._config),this._config.default_color="var(--primary-text-color)","icon"!==this._config.color_type?this._config.color_off="var(--card-background-color)":this._config.color_off="var(--paper-item-icon-color)",this._config.color_on="var(--paper-item-icon-active-color)";const n=JSON.stringify(this._config);if(this._entities=[],Array.isArray(this._config.triggers_update)?this._entities=[...this._config.triggers_update]:"string"==typeof this._config.triggers_update&&"all"!==this._config.triggers_update&&this._entities.push(this._config.triggers_update),"all"!==this._config.triggers_update){const t=new RegExp(/states\[\s*('|\\")([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\1\s*\]/,"gm"),e=new RegExp(/states\[\s*('|\\")([a-zA-Z0-9_]+\.[a-zA-Z0-9_]+)\1\s*\]/,"m"),i=n.match(t);null==i||i.forEach((t=>{const i=t.match(e);i&&!this._entities.includes(i[2])&&this._entities.push(i[2])}))}this._config.entity&&!this._entities.includes(this._config.entity)&&this._entities.push(this._config.entity),this._expandTriggerGroups();const r=new RegExp("\\[\\[\\[.*\\]\\]\\]","m");this._hasTemplate=!("all"!==this._config.triggers_update||!n.match(r)),this._initial_setup_complete||this._initConnected()}_loopGroup(t){t&&t.forEach((t=>{var e,i;(null===(e=this._hass)||void 0===e?void 0:e.states[t])&&("group"===ke(t)&&(null===(i=this._hass.states[t].attributes)||void 0===i?void 0:i.entity_id)?this._loopGroup(this._hass.states[t].attributes.entity_id):this._entities.includes(t)||this._entities.push(t))}))}_expandTriggerGroups(){var t;this._hass&&(null===(t=this._config)||void 0===t?void 0:t.group_expand)&&this._entities&&this._entities.forEach((t=>{var e,i;"group"===ke(t)&&this._loopGroup(null===(i=null===(e=this._hass)||void 0===e?void 0:e.states[t].attributes)||void 0===i?void 0:i.entity_id)}))}getCardSize(){var t;return(null===(t=this._config)||void 0===t?void 0:t.card_size)||3}_evalActions(t,e){const i=Ge(t),n=t=>t?(Object.keys(t).forEach((e=>{"object"==typeof t[e]?t[e]=n(t[e]):t[e]=this._getTemplateOrValue(this._stateObj,t[e])})),t):t;return i[e]=n(i[e]),!i[e].confirmation&&i.confirmation&&(i[e].confirmation=n(i.confirmation)),i}handleRippleActivate(t){this._ripple.then((e=>e&&e.startPress&&this._rippleHandlers.startPress(t)))}handleRippleDeactivate(){this._ripple.then((t=>t&&t.endPress&&this._rippleHandlers.endPress()))}handleRippleFocus(){this._ripple.then((t=>t&&t.startFocus&&this._rippleHandlers.startFocus()))}handleRippleBlur(){this._ripple.then((t=>t&&t.endFocus&&this._rippleHandlers.endFocus()))}_handleAction(t){var e;if(null===(e=t.detail)||void 0===e?void 0:e.action)switch(t.detail.action){case"tap":this._handleTap();break;case"hold":this._handleHold();break;case"double_tap":this._handleDblTap()}}_handleTap(){const t=this._config;t&&Gt(this,this._hass,this._evalActions(t,"tap_action"),!1,!1)}_handleHold(){const t=this._config;t&&Gt(this,this._hass,this._evalActions(t,"hold_action"),!0,!1)}_handleDblTap(){const t=this._config;t&&Gt(this,this._hass,this._evalActions(t,"double_tap_action"),!1,!0)}_handleUnlockType(t){const e=this._config;e&&e.lock.unlock===t.detail.action&&this._handleLock()}_handleLock(){const t=this.shadowRoot.getElementById("lock");if(!t)return;if(this._config.lock.exemptions){if(!this._hass.user.name||!this._hass.user.id)return;let e=!1;if(this._config.lock.exemptions.forEach((t=>{(!e&&t.user===this._hass.user.id||t.username===this._hass.user.name)&&(e=!0)})),!e)return t.classList.add("invalid"),void window.setTimeout((()=>{t&&t.classList.remove("invalid")}),3e3)}const e=this.shadowRoot.getElementById("overlay");if(e.style.setProperty("pointer-events","none"),t){const e=document.createAttribute("icon");e.value="mdi:lock-open-outline",t.attributes.setNamedItem(e),t.classList.add("hidden")}window.setTimeout((()=>{if(e.style.setProperty("pointer-events",""),t){t.classList.remove("hidden");const e=document.createAttribute("icon");e.value="mdi:lock-outline",t.attributes.setNamedItem(e)}}),1e3*this._config.lock.duration)}_stopPropagation(t){t.stopPropagation()}};var Ke,Qe,ti;t([J()],Xe.prototype,"_hass",void 0),t([J()],Xe.prototype,"_config",void 0),t([J()],Xe.prototype,"_timeRemaining",void 0),t([(Ke="mwc-ripple",(t,e)=>{const i={async get(){return await this.updateComplete,this.renderRoot.querySelector(Ke)},enumerable:!0,configurable:!0};return void 0!==e?X(i,t,e):K(i,t)})],Xe.prototype,"_ripple",void 0),t([(Qe={passive:!0},(t,e)=>void 0!==e?((t,e,i)=>{Object.assign(e[i],t)})(Qe,t,e):((t,e)=>Object.assign(Object.assign({},e),{finisher(i){Object.assign(i.prototype[e.key],t)}}))(Qe,t))],Xe.prototype,"handleRippleActivate",null),Xe=t([(ti="button-card",t=>"function"==typeof t?((t,e)=>(window.customElements.define(t,e),e))(ti,t):((t,e)=>{const{kind:i,elements:n}=e;return{kind:i,elements:n,finisher(e){window.customElements.define(t,e)}}})(ti,t))],Xe); diff --git a/www/community/lovelace-darksky-card/lovelace-darksky-card.js b/www/community/lovelace-darksky-card/lovelace-darksky-card.js new file mode 100644 index 0000000..3897c99 --- /dev/null +++ b/www/community/lovelace-darksky-card/lovelace-darksky-card.js @@ -0,0 +1,787 @@ +// ##### +// ##### Get the LitElement and HTML classes from an already defined HA Lovelace class +// ##### +var LitElement = LitElement || Object.getPrototypeOf(customElements.get("home-assistant-main")); +var html = LitElement.prototype.html; + +// ##### +// ##### Custom Card Definition begins +// ##### + +class DarkSkyWeatherCard extends LitElement { + +// ##### +// ##### Define Render Template +// ##### + + render() { +// Handle Configuration Flags +// var icons = this.config.static_icons ? "static" : "animated"; + var currentText = this.config.entity_current_text ? html`${this._hass.states[this.config.entity_current_text].state}` : ``; + var apparentTemp = this.config.entity_apparent_temp ? html`${this.localeText.feelsLike} ${this.current.apparent} ${this.getUOM("temperature")}` : ``; + var summary = this.config.entity_daily_summary ? html`
${this._hass.states[this.config.entity_daily_summary].state}
` : ``; + var separator = this.config.show_separator ? html`
` : ``; + + + +// Build HTML + return html` + + + ${this.current.conditions} + ${this.current.temperature}${this.getUOM('temperature')} + ${currentText} + ${apparentTemp} + ${separator} + +
    +
  • + ${this.getSlot().l1} + ${this.getSlot().l2} + ${this.getSlot().l3} + ${this.getSlot().l4} + ${this.getSlot().l5} +
  • +
  • + ${this.getSlot().r1} + ${this.getSlot().r2} + ${this.getSlot().r3} + ${this.getSlot().r4} + ${this.getSlot().r5} +
  • +
+
+
+ ${this.forecast.map(daily => html` +
+ ${(daily.date).toLocaleDateString(this.config.locale,{weekday: 'short'})} +
+ ${this.config.old_daily_format ? html`
${Math.round(this._hass.states[daily.temphigh].state)}${this.getUOM("temperature")} +
${Math.round(this._hass.states[daily.templow].state)}${this.getUOM("temperature")}` : + html`
${Math.round(this._hass.states[daily.templow].state)} / ${Math.round(this._hass.states[daily.temphigh].state)}${this.getUOM("temperature")}`} + ${this.config.entity_pop_1 && this.config.entity_pop_2 && this.config.entity_pop_3 && this.config.entity_pop_4 && this.config.entity_pop_5 ? html`
${Math.round(this._hass.states[daily.pop].state)} %` : ``} +
${ this.config.tooltips ? this._hass.states[daily.summary].state : ""}
+
`)} +
+ ${summary} +
+ `; + } + + +// ##### +// ##### slots - returns the value to be displyed in a specific current condition slot +// ##### + + getSlot() { + return { + 'r1' : this.slotValue('r1',this.config.slot_r1), + 'r2' : this.slotValue('r2',this.config.slot_r2), + 'r3' : this.slotValue('r3',this.config.slot_r3), + 'r4' : this.slotValue('r4',this.config.slot_r4), + 'r5' : this.slotValue('r5',this.config.slot_r5), + 'l1' : this.slotValue('l1',this.config.slot_l1), + 'l2' : this.slotValue('l2',this.config.slot_l2), + 'l3' : this.slotValue('l3',this.config.slot_l3), + 'l4' : this.slotValue('l4',this.config.slot_l4), + 'l5' : this.slotValue('l5',this.config.slot_l5), + } + } + +// ##### +// ##### slots - calculates the specific slot value +// ##### + + slotValue(slot,value){ + var sunNext = this.config.alt_sun_next ? html`
  • ${this._hass.states[this.config.alt_sun_next].state}
  • ` : this.config.entity_sun ? this.sunSet.next : ""; + var sunFollowing = this.config.alt_sun_following ? html`
  • ${this._hass.states[this.config.alt_sun_following].state}
  • ` : this.config.entity_sun ? this.sunSet.following : ""; + var daytimeHigh = this.config.alt_daytime_high ? html`
  • ${this._hass.states[this.config.alt_daytime_high].state}
  • ` : this.config.entity_daytime_high ? html`
  • ${this.localeText.maxToday} ${Math.round(this._hass.states[this.config.entity_daytime_high].state)} ${this.getUOM('temperature')}
  • ` : ``; + var intensity = this.config.entity_pop_intensity ? html` - ${this._hass.states[this.config.entity_pop_intensity].state} ${this.getUOM('intensity')}` : ``; + var pop = this.config.alt_pop ? html`
  • ${this._hass.states[this.config.alt_pop].state}
  • ` : this.config.entity_pop ? html`
  • ${Math.round(this._hass.states[this.config.entity_pop].state)} %${intensity}
  • ` : ``; + var visibility = this.config.alt_visibility ? html`
  • ${this._hass.states[this.config.alt_visibility].state}
  • ` : this.config.entity_visibility ? html`
  • ${this.current.visibility} ${this.getUOM('length')}
  • ` : ``; + var wind = this.config.alt_wind ? html`
  • ${this._hass.states[this.config.alt_wind].state}
  • ` : this.config.entity_wind_bearing && this.config.entity_wind_speed ? html`
  • ${this.current.beaufort}${this.current.windBearing} ${this.current.windSpeed} ${this.getUOM('length')}/h
  • ` : ``; + var humidity = this.config.alt_humidity ? html`
  • ${this._hass.states[this.config.alt_humidity].state}
  • ` : this.config.entity_humidity ? html`
  • ${this.current.humidity} %
  • ` : ``; + var pressure = this.config.alt_pressure ? html`
  • ${this._hass.states[this.config.alt_pressure].state}
  • ` : this.config.entity_pressure ? html`
  • ${this.current.pressure} ${this.getUOM('air_pressure')}
  • ` : ``; + var precipIntensityCurrent = this.config.entity_precip_intensity_current ? html`
  • ${this._hass.states[this.config.entity_precip_intensity_current].state} ${this.getUOM('intensity')}` : ``; + var precipCurrent = this.config.entity_precip_current ? html`
  • ${this._hass.states[this.config.entity_precip_current].state} ${this.getUOM('precipitation')}` : ``; + + switch (value){ + case 'pop': return pop; + case 'humidity': return humidity; + case 'pressure': return pressure; + case 'sun_following': return sunFollowing; + case 'daytime_high': return daytimeHigh; + case 'wind': return wind; + case 'visibility': return visibility; + case 'sun_next': return sunNext; + case 'precip_rate_current': return precipIntensityCurrent; + case 'precip_current': return precipCurrent; + case 'empty': return html` `; + case 'remove': return ``; + } + + // If no value can be matched pass back a default for the slot + switch (slot){ + case 'l1': return daytimeHigh; + case 'l2': return wind; + case 'l3': return visibility; + case 'l4': return sunNext; + case 'l5': return precipCurrent; + case 'r1': return pop; + case 'r2': return humidity; + case 'r3': return pressure; + case 'r4': return sunFollowing; + case 'r5': return precipIntensityCurrent; + } + } + + +// ##### +// ##### windDirections - returns set of possible wind directions by specified language +// ##### + + get windDirections() { + const windDirections_en = ['N','NNE','NE','ENE','E','ESE','SE','SSE','S','SSW','SW','WSW','W','WNW','NW','NNW','N']; + const windDirections_fr = ['N','NNE','NE','ENE','E','ESE','SE','SSE','S','SSO','SO','OSO','O','ONO','NO','NNO','N']; + const windDirections_de = ['N','NNO','NO','ONO','O','OSO','SO','SSO','S','SSW','SW','WSW','W','WNW','NW','NNW','N']; + const windDirections_nl = ['N','NNO','NO','ONO','O','OZO','ZO','ZZO','Z','ZZW','ZW','WZW','W','WNW','NW','NNW','N']; + const windDirections_he = ['צפון','צ-צ-מז','צפון מזרח','מז-צ-מז','מזרח','מז-ד-מז','דרום מזרח','ד-ד-מז','דרום','ד-ד-מע','דרום מערב','מע-ד-מע','מערב','מע-צ-מע','צפון מערב','צ-צ-מע','צפון']; + const windDirections_da = ['N','NNØ','NØ','ØNØ','Ø','ØSØ','SØ','SSØ','S','SSV','SV','VSV','V','VNV','NV','NNV','N']; + + switch (this.config.locale) { + case "it" : + case "fr" : + return windDirections_fr; + case "de" : + return windDirections_de; + case "nl" : + return windDirections_nl; + case "he" : + return windDirections_he; + case "da" : + return windDirections_da; + default : + return windDirections_en; + } + } + +// ##### +// ##### feelsLikeText returns set of possible "Feels Like" text by specified language +// ##### + + get localeText() { + switch (this.config.locale) { + case "it" : + return { + feelsLike: "Percepito", + maxToday: "Max oggi:", + } + case "fr" : + return { + feelsLike: "Se sent comme", + maxToday: "Max aujourd'hui:", + } + case "de" : + return { + feelsLike: "Gefühlt", + maxToday: "Max heute:", + } + case "nl" : + return { + feelsLike: "Voelt als", + maxToday: "Max vandaag:", + } + case "pl" : + return { + feelsLike: "Odczuwalne", + maxToday: "Najwyższa dziś:", + } + case "he" : + return { + feelsLike: "מרגיש כמו:", + maxToday: "מקסימלי היום:", + } + case "da" : + return { + feelsLike: "Føles som", + maxToday: "Højeste i dag" + } + default : + return { + feelsLike: "Feels like", + maxToday: "Today's High", + } + } + } + +// ##### +// ##### dayOrNight : returns day or night depending on the position of the sun. +// ##### + + get dayOrNight() { + const transformDayNight = { "below_horizon": "night", "above_horizon": "day", }; + return this.config.entity_sun ? transformDayNight[this._hass.states[this.config.entity_sun].state] : 'day'; + } + + +// ##### +// ##### weatherIcons: returns icon names based on current conditions text +// ##### + + get weatherIcons() { + return { + 'clear-day': 'day', + 'clear-night': 'night', + 'rain': 'rainy-5', + 'snow': 'snowy-6', + 'sleet': 'rainy-6', + 'wind': 'cloudy', + 'fog': 'cloudy', + 'cloudy': 'cloudy', + 'partly-cloudy-day': 'cloudy-day-3', + 'partly-cloudy-night': 'cloudy-night-3', + 'hail': 'rainy-7', + 'lightning': 'thunder', + 'thunderstorm': 'thunder', + 'windy-variant': html`cloudy-${this.dayOrNight}-3`, + 'exceptional': '!!', + } + } + + +// ##### +// ##### forecast : returns forcasted weather information for the next 5 days +// ##### + + get forecast() { + var forecastDate1 = new Date(); + forecastDate1.setDate(forecastDate1.getDate()+1); + var forecastDate2 = new Date(); + forecastDate2.setDate(forecastDate2.getDate()+2); + var forecastDate3 = new Date(); + forecastDate3.setDate(forecastDate3.getDate()+3); + var forecastDate4 = new Date(); + forecastDate4.setDate(forecastDate4.getDate()+4); + var forecastDate5 = new Date(); + forecastDate5.setDate(forecastDate5.getDate()+5); + + + const forecast1 = { date: forecastDate1, + dayIndex: '1', + condition: this.config.entity_forecast_icon_1, + temphigh: this.config.entity_forecast_high_temp_1, + templow: this.config.entity_forecast_low_temp_1, + pop: this.config.entity_pop_1, + summary: this.config.entity_summary_1, }; + const forecast2 = { date: forecastDate2, + dayIndex: '2', + condition: this.config.entity_forecast_icon_2, + temphigh: this.config.entity_forecast_high_temp_2, + templow: this.config.entity_forecast_low_temp_2, + pop: this.config.entity_pop_2, + summary: this.config.entity_summary_2, }; + const forecast3 = { date: forecastDate3, + dayIndex: '3', + condition: this.config.entity_forecast_icon_3, + temphigh: this.config.entity_forecast_high_temp_3, + templow: this.config.entity_forecast_low_temp_3, + pop: this.config.entity_pop_3, + summary: this.config.entity_summary_3, }; + const forecast4 = { date: forecastDate4, + dayIndex: '4', + condition: this.config.entity_forecast_icon_4, + temphigh: this.config.entity_forecast_high_temp_4, + templow: this.config.entity_forecast_low_temp_4, + pop: this.config.entity_pop_4, + summary: this.config.entity_summary_4, }; + const forecast5 = { date: forecastDate5, + dayIndex: '5', + condition: this.config.entity_forecast_icon_5, + temphigh: this.config.entity_forecast_high_temp_5, + templow: this.config.entity_forecast_low_temp_5, + pop: this.config.entity_pop_5, + summary: this.config.entity_summary_5, }; + + return [forecast1, forecast2, forecast3, forecast4, forecast5]; + } + + +// ##### +// ##### current : Returns current weather information +// ##### + + get current() { + var conditions = this._hass.states[this.config.entity_current_conditions].state; + var humidity = this.config.entity_humidity ? this._hass.states[this.config.entity_humidity].state : 0; + var pressure = this.config.entity_pressure ? Math.round(this._hass.states[this.config.entity_pressure].state) : 0; + var temperature = Math.round(this._hass.states[this.config.entity_temperature].state); + var visibility = this.config.entity_visibility ? this._hass.states[this.config.entity_visibility].state : 0; + var windBearing = this.config.entity_wind_bearing ? isNaN(this._hass.states[this.config.entity_wind_bearing].state) ? this._hass.states[this.config.entity_wind_bearing].state : this.windDirections[(Math.round((this._hass.states[this.config.entity_wind_bearing].state / 360) * 16))] : 0; + var windSpeed = this.config.entity_wind_speed ? Math.round(this._hass.states[this.config.entity_wind_speed].state) : 0; + var apparent = this.config.entity_apparent_temp ? Math.round(this._hass.states[this.config.entity_apparent_temp].state) : 0; + var beaufort = this.config.show_beaufort ? html`Bft: ${this.beaufortWind} - ` : ``; + + return { + 'conditions': conditions, + 'humidity': humidity, + 'pressure': pressure, + 'temperature': temperature, + 'visibility': visibility, + 'windBearing': windBearing, + 'windSpeed': windSpeed, + 'apparent' : apparent, + 'beaufort' : beaufort, + } + } + +// ##### +// ##### sunSetAndRise: returns set and rise information +// ##### + +get sunSet() { + var nextSunSet ; + var nextSunRise; + if (this.config.time_format) { + nextSunSet = new Date(this._hass.states[this.config.entity_sun].attributes.next_setting).toLocaleTimeString(this.config.locale, {hour: '2-digit', minute:'2-digit',hour12: this.is12Hour}); + nextSunRise = new Date(this._hass.states[this.config.entity_sun].attributes.next_rising).toLocaleTimeString(this.config.locale, {hour: '2-digit', minute:'2-digit', hour12: this.is12Hour}); + } + else { + nextSunSet = new Date(this._hass.states[this.config.entity_sun].attributes.next_setting).toLocaleTimeString(this.config.locale, {hour: '2-digit', minute:'2-digit'}); + nextSunRise = new Date(this._hass.states[this.config.entity_sun].attributes.next_rising).toLocaleTimeString(this.config.locale, {hour: '2-digit', minute:'2-digit'}); + } + var nextDate = new Date(); + nextDate.setDate(nextDate.getDate() + 1); + if (this._hass.states[this.config.entity_sun].state == "above_horizon" ) { + nextSunRise = nextDate.toLocaleDateString(this.config.locale,{weekday: 'short'}) + " " + nextSunRise; + return { + 'next': html`
  • ${nextSunSet}
  • `, + 'following': html`
  • ${nextSunRise}
  • `, + 'nextText': nextSunSet, + 'followingText': nextSunRise, + }; + } else { + if (new Date().getDate() != new Date(this._hass.states[this.config.entity_sun].attributes.next_rising).getDate()) { + nextSunRise = nextDate.toLocaleDateString(this.config.locale,{weekday: 'short'}) + " " + nextSunRise; + nextSunSet = nextDate.toLocaleDateString(this.config.locale,{weekday: 'short'}) + " " + nextSunSet; + } + return { + 'next': html`
  • ${nextSunRise}
  • `, + 'following': html`
  • ${nextSunSet}
  • `, + 'nextText': nextSunRise, + 'followingText': nextSunSet, + }; + } +} + + +// ##### +// ##### beaufortWind - returns the wind speed on th beaufort scale +// ##### + +get beaufortWind() { + if (this.config.entity_wind_speed) { + switch (this._hass.states[this.config.entity_wind_speed].attributes.unit_of_measurement) { + case 'mph': + if (this._hass.states[this.config.entity_wind_speed].state >= 73) return 12; + if (this._hass.states[this.config.entity_wind_speed].state >= 64) return 11; + if (this._hass.states[this.config.entity_wind_speed].state >= 55) return 10; + if (this._hass.states[this.config.entity_wind_speed].state >= 47) return 9; + if (this._hass.states[this.config.entity_wind_speed].state >= 39) return 8; + if (this._hass.states[this.config.entity_wind_speed].state >= 31) return 7; + if (this._hass.states[this.config.entity_wind_speed].state >= 25) return 6; + if (this._hass.states[this.config.entity_wind_speed].state >= 18) return 5; + if (this._hass.states[this.config.entity_wind_speed].state >= 13) return 4; + if (this._hass.states[this.config.entity_wind_speed].state >= 8) return 3; + if (this._hass.states[this.config.entity_wind_speed].state >=3) return 2; + if (this._hass.states[this.config.entity_wind_speed].state >= 1) return 1; + default: // Assume m/s + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 118) return 12; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 103) return 11; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 89) return 10; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 75) return 9; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 62) return 8; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 50) return 7; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 39) return 6; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 29) return 5; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 20) return 4; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 12) return 3; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >=6) return 2; + if ((this._hass.states[this.config.entity_wind_speed].state * 3.6) >= 1) return 1; + } + } + return 0; +} + + +// ##### +// ##### is12Hour - returns true if 12 hour clock or false if 24 +// ##### + +get is12Hour() { + var hourFormat= this.config.time_format ? this.config.time_format : 12 + switch (hourFormat) { + case 24: + return false; + default: + return true; + } +} + + +// ##### +// ##### style: returns the CSS style classes for the card +// #### + +style() { + + // Get config flags or set defaults if not configured + var tooltipBGColor = this.config.tooltip_bg_color || "rgb( 75,155,239)"; + var tooltipFGColor = this.config.tooltip_fg_color || "#fff"; + var tooltipBorderColor = this.config.tooltip_border_color || "rgb(255,161,0)"; + var tooltipBorderWidth = this.config.tooltip_border_width || "1"; + var tooltipCaretSize = this.config.tooltip_caret_size || "5"; + var tooltipWidth = this.config.tooltip_width || "110"; + var tooltipLeftOffset = this.config.tooltip_left_offset || "-12"; + var tooltipVisible = this.config.tooltips ? "visible" : "hidden"; + var tempTopMargin = this.config.temp_top_margin || "0.05em"; + var tempFontWeight = this.config.temp_font_weight || "300"; + var tempFontSize = this.config.temp_font_size || "4em"; + var tempRightPos = this.config.temp_right_pos || "0.85em"; + var tempUOMTopMargin = this.config.temp_uom_top_margin || "-9px"; + var tempUOMRightMargin = this.config.temp_uom_right_margin || "7px"; + var apparentTopMargin = this.config.apparent_top_margin || "45px"; + var apparentRightPos = this.config.apparent_right_pos || "1em"; + var apparentRightMargin = this.config.apparent_right_margin || "1em"; + var currentTextTopMargin = this.config.current_text_top_margin || "39px"; + var currentTextLeftPos = this.config.current_text_left_pos || "5em"; + var currentTextFontSize = this.config.current_text_font_size || "1.5em"; + var largeIconTopMargin = this.config.large_icon_top_margin || "-3.5em"; + var largeIconLeftPos = this.config.large_icon_left_pos || "0em"; + var currentDataTopMargin = this.config.current_data_top_margin ? this.config.current_data_top_margin : this.config.show_separator ? "1em" : "7em"; + var separatorTopMargin = this.config.separator_top_margin || "6em"; + + return html` + .clear { + clear: both; + } + + .card { + margin: auto; + padding-top: 2em; + padding-bottom: 1em; + padding-left: 1em; + padding-right: 1em; + position: relative; + } + + .ha-icon { + height: 18px; + margin-right: 5px; + color: var(--paper-item-icon-color); + } + + .line { + margin-top: ${separatorTopMargin}; + margin-left: 1em; + margin-right: 1em; + } + + .temp { + font-weight: ${tempFontWeight}; + font-size: ${tempFontSize}; + color: var(--primary-text-color); + position: absolute; + right: ${tempRightPos}; + margin-top: ${tempTopMargin}; + } + + .tempc { + font-weight: ${tempFontWeight}; + font-size: 1.5em; + vertical-align: super; + color: var(--primary-text-color); + position: absolute; + right: 1em; + margin-top: ${tempUOMTopMargin}; + margin-right: ${tempUOMRightMargin}; + } + + .apparent { + color: var(--primary-text-color); + position: absolute; + right: ${apparentRightPos}; + margin-top: ${apparentTopMargin}; + margin-right: ${apparentRightMargin}; + } + + .currentText { + font-size: ${currentTextFontSize}; + color: var(--secondary-text-color); + position: absolute; + left: ${currentTextLeftPos}; + margin-top: ${currentTextTopMargin}; + } + + .pop { + font-weight: 400; + color: var(--primary-text-color); + } + + .variations { + display: flex; + flex-flow: row wrap; + justify-content: space-between; + font-weight: 300; + color: var(--primary-text-color); + list-style: none; + padding: 0.2em; + margin-top: ${currentDataTopMargin}; + } + + .unit { + font-size: 0.8em; + } + + .forecast { + width: 100%; + margin: 0 auto; + height: 9em; + } + + .day { + display: block; + width: 20%; + float: left; + text-align: center; + color: var(--primary-text-color); + border-right: .1em solid #d9d9d9; + line-height: 1.5; + box-sizing: border-box; + margin-top: 1em; + } + + .dayname { + text-transform: uppercase; + } + + .forecast .day:first-child { + margin-left: 20; + } + + .forecast .day:nth-last-child(1) { + border-right: none; + margin-right: 0; + } + + .highTemp { + font-weight: bold; + } + + .lowTemp { + color: var(--secondary-text-color); + } + + .icon.bigger { + width: 10em; + height: 10em; + margin-top: ${largeIconTopMargin}; + position: absolute; + left: ${largeIconLeftPos}; + } + + .icon { + width: 50px; + height: 50px; + margin-right: 5px; + display: inline-block; + vertical-align: middle; + background-size: contain; + background-position: center center; + background-repeat: no-repeat; + text-indent: -9999px; + } + + .weather { + font-weight: 300; + font-size: 1.5em; + color: var(--primary-text-color); + text-align: left; + position: absolute; + top: -0.5em; + left: 6em; + word-wrap: break-word; + width: 30%; + } + + .fcasttooltip { + position: relative; + display: inline-block; + } + + .fcasttooltip .fcasttooltiptext { + visibility: hidden; + width: ${tooltipWidth}px; + background-color: ${tooltipBGColor}; + color: ${tooltipFGColor}; + text-align: center; + border-radius: 6px; + border-style: solid; + border-color: ${tooltipBorderColor}; + border-width: ${tooltipBorderWidth}px; + padding: 5px 0; + + /* Position the tooltip */ + position: absolute; + z-index: 1; + bottom: 50%; + left: 0%; + margin-left: ${tooltipLeftOffset}px; + } + + .fcasttooltip .fcasttooltiptext:after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -${tooltipCaretSize}px; + border-width: ${tooltipCaretSize}px; + border-style: solid; + border-color: ${tooltipBorderColor} transparent transparent transparent; + } + + .fcasttooltip:hover .fcasttooltiptext { + visibility: ${tooltipVisible}; + } + ` +} + +// ##### +// ##### getUOM: gets UOM for specified measure in either metric or imperial +// ##### + + getUOM(measure) { + + const lengthUnit = this._hass.config.unit_system.length; + + switch (measure) { + case 'air_pressure': + return lengthUnit === 'km' ? 'hPa' : 'mbar'; + case 'length': + return lengthUnit; + case 'precipitation': + return lengthUnit === 'km' ? 'mm' : 'in'; + case 'intensity': + return lengthUnit === 'km' ? 'mm/h' : 'in/h' + default: + return this._hass.config.unit_system[measure] || ''; + } + } + +// ##### +// ##### Assign the external hass object to an internal class var. +// ##### This is called everytime a state change occurs in HA +// ##### + + set hass(hass) { + + var interval = this.config.refresh_interval || 30; + var doRefresh = false; + + // Make sure hass is assigned first time. + if (!this._initialized) { + this._initialized= true; + this._lasRefresh = new Date(); + doRefresh = true; + } + + var now = new Date(); + + // Check if refresh interval has been exceeded and refresh if necessary + if (Math.round((now - this._lastRefresh)/1000) > interval ) { doRefresh = true; } + + if (doRefresh) { + this._lastRefresh = new Date(); + this._hass = hass; + this.updateValues(); + } + } + + +// ##### +// updateValues - Updates card values as changes happen in the hass object +// ##### + + updateValues() { + const root = this.shadowRoot; + if (root.childElementCount > 0) { + +// Current Conditions + root.getElementById("temperature-text").textContent = `${this.current.temperature}`; + root.getElementById("icon-bigger").textContent = `${this.current.conditions}`; + root.getElementById("icon-bigger").style.backgroundImage = `none, url(/local/icons/weather_icons/${this.config.static_icons ? "static" : "animated"}/${this.weatherIcons[this.current.conditions]}.svg)`; + +// Forecast blocks + this.forecast.forEach((daily) => { + root.getElementById("fcast-dayName-" + daily.dayIndex).textContent = `${(daily.date).toLocaleDateString(this.config.locale,{weekday: 'short'})}`; + root.getElementById("fcast-icon-" + daily.dayIndex).style.backgroundImage = `none, url(/local/icons/weather_icons/${this.config.static_icons ? "static" : "animated"}/${this.weatherIcons[this._hass.states[daily.condition].state]}.svg`; + root.getElementById("fcast-high-" + daily.dayIndex).textContent = `${Math.round(this._hass.states[daily.temphigh].state)}${this.getUOM("temperature")}`; + root.getElementById("fcast-low-" + daily.dayIndex).textContent = `${Math.round(this._hass.states[daily.templow].state)}${this.config.old_daily_format ? this.getUOM("temperature") : ""}`; + if (this.config.entity_pop_1 && this.config.entity_pop_2 && this.config.entity_pop_3 && this.config.entity_pop_4 && this.config.entity_pop_5) { root.getElementById("fcast-pop-" + daily.dayIndex).textContent = `${Math.round(this._hass.states[daily.pop].state)} %` } + root.getElementById("fcast-summary-" + daily.dayIndex).textContent = `${this._hass.states[daily.summary].state}`; + }); + +// Optional Entities + if (this.config.entity_current_text) { root.getElementById("current-text").textContent = `${this._hass.states[this.config.entity_current_text].state}` } + if (this.config.entity_apparent_temp) { root.getElementById("apparent-text").textContent = `${this.current.apparent}` } + if (this.config.entity_pressure && !this.config.alt_pressure) { root.getElementById("pressure-text").textContent = `${this.current.pressure}` } + if (this.config.entity_humidity && !this.config.alt_humidity) { root.getElementById("humidity-text").textContent = `${this.current.humidity}` } + if (this.config.show_beaufort && !this.config.alt_wind) { root.getElementById("beaufort-text").textContent = `Bft: ${this.beaufortWind} - ` } + if (this.config.entity_wind_bearing && !this.config.alt_wind) { root.getElementById("wind-bearing-text").textContent = `${this.current.windBearing} ` } + if (this.config.entity_wind_speed && !this.config.alt_wind) { root.getElementById("wind-speed-text").textContent = `${this.current.windSpeed}` } + if (this.config.entity_visibility && !this.config.alt_visibility) { root.getElementById("visibility-text").textContent = `${this.current.visibility}` } + if (this.config.entity_pop && !this.config.alt_pop) { root.getElementById("pop-text").textContent = `${Math.round(this._hass.states[this.config.entity_pop].state)}` } + if (this.config.entity_pop_intensity && !this.config.alt_pop) { root.getElementById("pop-intensity-text").textContent = ` - ${this._hass.states[this.config.entity_pop_intensity].state} ${this.getUOM('intensity')}` } + if (this.config.entity_daytime_high && !this.config.alt_daytime_high) { root.getElementById("daytime-high-text").textContent = `${Math.round(this._hass.states[this.config.entity_daytime_high].state)}` } + if (this.config.entity_sun && !this.config.alt_sun_next) { root.getElementById("sun-next-text").textContent = `${this.sunSet.nextText}` } + if (this.config.entity_sun && !this.config.alt_sun_following) { root.getElementById("sun-following-text").textContent = `${this.sunSet.followingText}` } + if (this.config.entity_daily_summary) { root.getElementById("daily-summary-text").textContent = `${this._hass.states[this.config.entity_daily_summary].state}` } + if (this.config.entity_precip_intensity_current) { root.getElementById("intensity_current-text").textContent = `${this._hass.states[this.config.entity_precip_intensity_current].state}` } + if (this.config.entity_precip_current) { root.getElementById("precip_current-text").textContent = `${this._hass.states[this.config.entity_precip_current].state} ` } + + +// Alt Text + if (this.config.alt_sun_next) { root.getElementById("alt-sun-next").textContent = `${this._hass.states[this.config.alt_sun_next].state}` } + if (this.config.alt_sun_following) { root.getElementById("alt-sun-following").textContent = `${this._hass.states[this.config.alt_sun_following].state}` } + if (this.config.alt_pop) { root.getElementById("alt-pop").textContent = `${this._hass.states[this.config.alt_pop].state}` } + if (this.config.alt_wind) { root.getElementById("alt-wind").textContent = `${this._hass.states[this.config.alt_wind].state}` } + if (this.config.alt_pressure) { root.getElementById("alt-pressure").textContent = `${this._hass.states[this.config.alt_pressure].state}` } + if (this.config.alt_humidity) { root.getElementById("alt-humidity").textContent = `${this._hass.states[this.config.alt_humidity].state}` } + if (this.config.alt_daytime_high) { root.getElementById("alt-daytime-high").textContent = `${this._hass.states[this.config.alt_daytime_high].state}` } + if (this.config.alt_visibility) { root.getElementById("alt-visibility").textContent = `${this._hass.states[this.config.alt_visibility].state}` } + } + } + + +// ##### +// ##### Assigns the configuration vlaues to an internal class var +// ##### This is called everytime a config change is made +// ##### + + setConfig(config) { this.config = config; } + + +// ##### +// ##### Sets the card size so HA knows how to put in columns +// ##### + + getCardSize() { return 3 } + +} + +// ##### +// ##### Register the card as a customElement +// ##### +customElements.define('lovelace-darksky-card', DarkSkyWeatherCard); + diff --git a/www/community/lovelace-time-picker-card/time-picker-card.js b/www/community/lovelace-time-picker-card/time-picker-card.js new file mode 100644 index 0000000..958e417 --- /dev/null +++ b/www/community/lovelace-time-picker-card/time-picker-card.js @@ -0,0 +1,528 @@ +/*! ***************************************************************************** +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +this file except in compliance with the License. You may obtain a copy of the +License at http://www.apache.org/licenses/LICENSE-2.0 + +THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED +WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, +MERCHANTABLITY OR NON-INFRINGEMENT. + +See the Apache Version 2.0 License for specific language governing permissions +and limitations under the License. +***************************************************************************** */ +function e(e,t,i,n){var s,r=arguments.length,o=r<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,i):n;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)o=Reflect.decorate(e,t,i,n);else for(var a=e.length-1;a>=0;a--)(s=e[a])&&(o=(r<3?s(o):r>3?s(t,i,o):s(t,i))||o);return r>3&&o&&Object.defineProperty(t,i,o),o}var t=/d{1,4}|M{1,4}|YY(?:YY)?|S{1,3}|Do|ZZ|Z|([HhMsDm])\1?|[aA]|"[^"]*"|'[^']*'/g,i="[^\\s]+",n=/\[([^]*?)\]/gm;function s(e,t){for(var i=[],n=0,s=e.length;n-1?n:null}};function o(e){for(var t=[],i=1;i3?0:(e-e%10!=10?1:0)*e%10]}},h=o({},l),u=function(e,t){for(void 0===t&&(t=2),e=String(e);e.length0?"-":"+")+u(100*Math.floor(Math.abs(t)/60)+Math.abs(t)%60,4)},Z:function(e){var t=e.getTimezoneOffset();return(t>0?"-":"+")+u(Math.floor(Math.abs(t)/60),2)+":"+u(Math.abs(t)%60,2)}},m=function(e){return+e-1},g=[null,"[1-9]\\d?"],f=[null,i],v=["isPm",i,function(e,t){var i=e.toLowerCase();return i===t.amPm[0]?0:i===t.amPm[1]?1:null}],y=["timezoneOffset","[^\\s]*?[\\+\\-]\\d\\d:?\\d\\d|[^\\s]*?Z?",function(e){var t=(e+"").match(/([+-]|\d\d)/gi);if(t){var i=60*+t[1]+parseInt(t[2],10);return"+"===t[0]?i:-i}return 0}],_=(r("monthNamesShort"),r("monthNames"),{default:"ddd MMM DD YYYY HH:mm:ss",shortDate:"M/D/YY",mediumDate:"MMM D, YYYY",longDate:"MMMM D, YYYY",fullDate:"dddd, MMMM D, YYYY",isoDate:"YYYY-MM-DD",isoDateTime:"YYYY-MM-DDTHH:mm:ssZ",shortTime:"HH:mm",mediumTime:"HH:mm:ss",longTime:"HH:mm:ss.SSS"});var S=function(e,i,s){if(void 0===i&&(i=_.default),void 0===s&&(s={}),"number"==typeof e&&(e=new Date(e)),"[object Date]"!==Object.prototype.toString.call(e)||isNaN(e.getTime()))throw new Error("Invalid Date pass to format");var r=[];i=(i=_[i]||i).replace(n,(function(e,t){return r.push(t),"@@@"}));var a=o(o({},h),s);return(i=i.replace(t,(function(t){return p[t](e,a)}))).replace(/@@@/g,(function(){return r.shift()}))};(function(){try{(new Date).toLocaleDateString("i")}catch(e){return"RangeError"===e.name}})(),function(){try{(new Date).toLocaleString("i")}catch(e){return"RangeError"===e.name}}(),function(){try{(new Date).toLocaleTimeString("i")}catch(e){return"RangeError"===e.name}}();function b(e){return e.substr(0,e.indexOf("."))} +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */const w="undefined"!=typeof window&&null!=window.customElements&&void 0!==window.customElements.polyfillWrapFlushCallback,x=(e,t,i=null)=>{for(;t!==i;){const i=t.nextSibling;e.removeChild(t),t=i}},C=`{{lit-${String(Math.random()).slice(2)}}}`,E=`\x3c!--${C}--\x3e`,N=new RegExp(`${C}|${E}`);class P{constructor(e,t){this.parts=[],this.element=t;const i=[],n=[],s=document.createTreeWalker(t.content,133,null,!1);let r=0,o=-1,a=0;const{strings:c,values:{length:d}}=e;for(;a0;){const t=c[a],i=O.exec(t)[2],n=i.toLowerCase()+"$lit$",s=e.getAttribute(n);e.removeAttribute(n);const r=s.split(N);this.parts.push({type:"attribute",index:o,name:i,strings:r}),a+=r.length-1}}"TEMPLATE"===e.tagName&&(n.push(e),s.currentNode=e.content)}else if(3===e.nodeType){const t=e.data;if(t.indexOf(C)>=0){const n=e.parentNode,s=t.split(N),r=s.length-1;for(let t=0;t{const i=e.length-t.length;return i>=0&&e.slice(i)===t},T=e=>-1!==e.index,k=()=>document.createComment(""),O=/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;function $(e,t){const{element:{content:i},parts:n}=e,s=document.createTreeWalker(i,133,null,!1);let r=A(n),o=n[r],a=-1,c=0;const d=[];let l=null;for(;s.nextNode();){a++;const e=s.currentNode;for(e.previousSibling===l&&(l=null),t.has(e)&&(d.push(e),null===l&&(l=e)),null!==l&&c++;void 0!==o&&o.index===a;)o.index=null!==l?-1:o.index-c,r=A(n,r),o=n[r]}d.forEach(e=>e.parentNode.removeChild(e))}const V=e=>{let t=11===e.nodeType?0:1;const i=document.createTreeWalker(e,133,null,!1);for(;i.nextNode();)t++;return t},A=(e,t=-1)=>{for(let i=t+1;i"function"==typeof e&&H.has(e),I={},U={}; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +class D{constructor(e,t,i){this.__parts=[],this.template=e,this.processor=t,this.options=i}update(e){let t=0;for(const i of this.__parts)void 0!==i&&i.setValue(e[t]),t++;for(const e of this.__parts)void 0!==e&&e.commit()}_clone(){const e=w?this.template.element.content.cloneNode(!0):document.importNode(this.template.element.content,!0),t=[],i=this.template.parts,n=document.createTreeWalker(e,133,null,!1);let s,r=0,o=0,a=n.nextNode();for(;re}),R=` ${C} `;class Y{constructor(e,t,i,n){this.strings=e,this.values=t,this.type=i,this.processor=n}getHTML(){const e=this.strings.length-1;let t="",i=!1;for(let n=0;n-1||i)&&-1===e.indexOf("--\x3e",s+1);const r=O.exec(e);t+=null===r?e+(i?R:E):e.substr(0,r.index)+r[1]+r[2]+"$lit$"+r[3]+C}return t+=this.strings[e],t}getTemplateElement(){const e=document.createElement("template");let t=this.getHTML();return void 0!==L&&(t=L.createHTML(t)),e.innerHTML=t,e}} +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */const z=e=>null===e||!("object"==typeof e||"function"==typeof e),F=e=>Array.isArray(e)||!(!e||!e[Symbol.iterator]);class B{constructor(e,t,i){this.dirty=!0,this.element=e,this.name=t,this.strings=i,this.parts=[];for(let e=0;e{try{const e={get capture(){return K=!0,!1}};window.addEventListener("test",e,e),window.removeEventListener("test",e,e)}catch(e){}})();class Q{constructor(e,t,i){this.value=void 0,this.__pendingValue=void 0,this.element=e,this.eventName=t,this.eventContext=i,this.__boundHandleEvent=e=>this.handleEvent(e)}setValue(e){this.__pendingValue=e}commit(){for(;j(this.__pendingValue);){const e=this.__pendingValue;this.__pendingValue=I,e(this)}if(this.__pendingValue===I)return;const e=this.__pendingValue,t=this.value,i=null==e||null!=t&&(e.capture!==t.capture||e.once!==t.once||e.passive!==t.passive),n=null!=e&&(null==t||i);i&&this.element.removeEventListener(this.eventName,this.__boundHandleEvent,this.__options),n&&(this.__options=X(e),this.element.addEventListener(this.eventName,this.__boundHandleEvent,this.__options)),this.value=e,this.__pendingValue=I}handleEvent(e){"function"==typeof this.value?this.value.call(this.eventContext||this.element,e):this.value.handleEvent(e)}}const X=e=>e&&(K?{capture:e.capture,passive:e.passive,once:e.once}:e.capture) +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */;function ee(e){let t=te.get(e.type);void 0===t&&(t={stringsArray:new WeakMap,keyString:new Map},te.set(e.type,t));let i=t.stringsArray.get(e.strings);if(void 0!==i)return i;const n=e.strings.join(C);return i=t.keyString.get(n),void 0===i&&(i=new P(e,e.getTemplateElement()),t.keyString.set(n,i)),t.stringsArray.set(e.strings,i),i}const te=new Map,ie=new WeakMap; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */const ne=new +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +class{handleAttributeExpressions(e,t,i,n){const s=t[0];if("."===s){return new J(e,t.slice(1),i).parts}if("@"===s)return[new Q(e,t.slice(1),n.eventContext)];if("?"===s)return[new G(e,t.slice(1),i)];return new B(e,t,i).parts}handleTextExpression(e){return new W(e)}}; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */"undefined"!=typeof window&&(window.litHtmlVersions||(window.litHtmlVersions=[])).push("1.3.0");const se=(e,...t)=>new Y(e,t,"html",ne) +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */,re=(e,t)=>`${e}--${t}`;let oe=!0;void 0===window.ShadyCSS?oe=!1:void 0===window.ShadyCSS.prepareTemplateDom&&(console.warn("Incompatible ShadyCSS version detected. Please update to at least @webcomponents/webcomponentsjs@2.0.2 and @webcomponents/shadycss@1.3.1."),oe=!1);const ae=e=>t=>{const i=re(t.type,e);let n=te.get(i);void 0===n&&(n={stringsArray:new WeakMap,keyString:new Map},te.set(i,n));let s=n.stringsArray.get(t.strings);if(void 0!==s)return s;const r=t.strings.join(C);if(s=n.keyString.get(r),void 0===s){const i=t.getTemplateElement();oe&&window.ShadyCSS.prepareTemplateDom(i,e),s=new P(t,i),n.keyString.set(r,s)}return n.stringsArray.set(t.strings,s),s},ce=["html","svg"],de=new Set,le=(e,t,i)=>{de.add(e);const n=i?i.element:document.createElement("template"),s=t.querySelectorAll("style"),{length:r}=s;if(0===r)return void window.ShadyCSS.prepareTemplateStyles(n,e);const o=document.createElement("style");for(let e=0;e{ce.forEach(t=>{const i=te.get(re(t,e));void 0!==i&&i.keyString.forEach(e=>{const{element:{content:t}}=e,i=new Set;Array.from(t.querySelectorAll("style")).forEach(e=>{i.add(e)}),$(e,i)})})})(e);const a=n.content;i?function(e,t,i=null){const{element:{content:n},parts:s}=e;if(null==i)return void n.appendChild(t);const r=document.createTreeWalker(n,133,null,!1);let o=A(s),a=0,c=-1;for(;r.nextNode();){c++;for(r.currentNode===i&&(a=V(t),i.parentNode.insertBefore(t,i));-1!==o&&s[o].index===c;){if(a>0){for(;-1!==o;)s[o].index+=a,o=A(s,o);return}o=A(s,o)}}}(i,o,a.firstChild):a.insertBefore(o,a.firstChild),window.ShadyCSS.prepareTemplateStyles(n,e);const c=a.querySelector("style");if(window.ShadyCSS.nativeShadow&&null!==c)t.insertBefore(c.cloneNode(!0),t.firstChild);else if(i){a.insertBefore(o,a.firstChild);const e=new Set;e.add(o),$(i,e)}};window.JSCompiler_renameProperty=(e,t)=>e;const he={toAttribute(e,t){switch(t){case Boolean:return e?"":null;case Object:case Array:return null==e?e:JSON.stringify(e)}return e},fromAttribute(e,t){switch(t){case Boolean:return null!==e;case Number:return null===e?null:Number(e);case Object:case Array:return JSON.parse(e)}return e}},ue=(e,t)=>t!==e&&(t==t||e==e),pe={attribute:!0,type:String,converter:he,reflect:!1,hasChanged:ue};class me extends HTMLElement{constructor(){super(),this.initialize()}static get observedAttributes(){this.finalize();const e=[];return this._classProperties.forEach((t,i)=>{const n=this._attributeNameForProperty(i,t);void 0!==n&&(this._attributeToPropertyMap.set(n,i),e.push(n))}),e}static _ensureClassProperties(){if(!this.hasOwnProperty(JSCompiler_renameProperty("_classProperties",this))){this._classProperties=new Map;const e=Object.getPrototypeOf(this)._classProperties;void 0!==e&&e.forEach((e,t)=>this._classProperties.set(t,e))}}static createProperty(e,t=pe){if(this._ensureClassProperties(),this._classProperties.set(e,t),t.noAccessor||this.prototype.hasOwnProperty(e))return;const i="symbol"==typeof e?Symbol():"__"+e,n=this.getPropertyDescriptor(e,i,t);void 0!==n&&Object.defineProperty(this.prototype,e,n)}static getPropertyDescriptor(e,t,i){return{get(){return this[t]},set(n){const s=this[e];this[t]=n,this.requestUpdateInternal(e,s,i)},configurable:!0,enumerable:!0}}static getPropertyOptions(e){return this._classProperties&&this._classProperties.get(e)||pe}static finalize(){const e=Object.getPrototypeOf(this);if(e.hasOwnProperty("finalized")||e.finalize(),this.finalized=!0,this._ensureClassProperties(),this._attributeToPropertyMap=new Map,this.hasOwnProperty(JSCompiler_renameProperty("properties",this))){const e=this.properties,t=[...Object.getOwnPropertyNames(e),..."function"==typeof Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(e):[]];for(const i of t)this.createProperty(i,e[i])}}static _attributeNameForProperty(e,t){const i=t.attribute;return!1===i?void 0:"string"==typeof i?i:"string"==typeof e?e.toLowerCase():void 0}static _valueHasChanged(e,t,i=ue){return i(e,t)}static _propertyValueFromAttribute(e,t){const i=t.type,n=t.converter||he,s="function"==typeof n?n:n.fromAttribute;return s?s(e,i):e}static _propertyValueToAttribute(e,t){if(void 0===t.reflect)return;const i=t.type,n=t.converter;return(n&&n.toAttribute||he.toAttribute)(e,i)}initialize(){this._updateState=0,this._updatePromise=new Promise(e=>this._enableUpdatingResolver=e),this._changedProperties=new Map,this._saveInstanceProperties(),this.requestUpdateInternal()}_saveInstanceProperties(){this.constructor._classProperties.forEach((e,t)=>{if(this.hasOwnProperty(t)){const e=this[t];delete this[t],this._instanceProperties||(this._instanceProperties=new Map),this._instanceProperties.set(t,e)}})}_applyInstanceProperties(){this._instanceProperties.forEach((e,t)=>this[t]=e),this._instanceProperties=void 0}connectedCallback(){this.enableUpdating()}enableUpdating(){void 0!==this._enableUpdatingResolver&&(this._enableUpdatingResolver(),this._enableUpdatingResolver=void 0)}disconnectedCallback(){}attributeChangedCallback(e,t,i){t!==i&&this._attributeToProperty(e,i)}_propertyToAttribute(e,t,i=pe){const n=this.constructor,s=n._attributeNameForProperty(e,i);if(void 0!==s){const e=n._propertyValueToAttribute(t,i);if(void 0===e)return;this._updateState=8|this._updateState,null==e?this.removeAttribute(s):this.setAttribute(s,e),this._updateState=-9&this._updateState}}_attributeToProperty(e,t){if(8&this._updateState)return;const i=this.constructor,n=i._attributeToPropertyMap.get(e);if(void 0!==n){const e=i.getPropertyOptions(n);this._updateState=16|this._updateState,this[n]=i._propertyValueFromAttribute(t,e),this._updateState=-17&this._updateState}}requestUpdateInternal(e,t,i){let n=!0;if(void 0!==e){const s=this.constructor;i=i||s.getPropertyOptions(e),s._valueHasChanged(this[e],t,i.hasChanged)?(this._changedProperties.has(e)||this._changedProperties.set(e,t),!0!==i.reflect||16&this._updateState||(void 0===this._reflectingProperties&&(this._reflectingProperties=new Map),this._reflectingProperties.set(e,i))):n=!1}!this._hasRequestedUpdate&&n&&(this._updatePromise=this._enqueueUpdate())}requestUpdate(e,t){return this.requestUpdateInternal(e,t),this.updateComplete}async _enqueueUpdate(){this._updateState=4|this._updateState;try{await this._updatePromise}catch(e){}const e=this.performUpdate();return null!=e&&await e,!this._hasRequestedUpdate}get _hasRequestedUpdate(){return 4&this._updateState}get hasUpdated(){return 1&this._updateState}performUpdate(){if(!this._hasRequestedUpdate)return;this._instanceProperties&&this._applyInstanceProperties();let e=!1;const t=this._changedProperties;try{e=this.shouldUpdate(t),e?this.update(t):this._markUpdated()}catch(t){throw e=!1,this._markUpdated(),t}e&&(1&this._updateState||(this._updateState=1|this._updateState,this.firstUpdated(t)),this.updated(t))}_markUpdated(){this._changedProperties=new Map,this._updateState=-5&this._updateState}get updateComplete(){return this._getUpdateComplete()}_getUpdateComplete(){return this._updatePromise}shouldUpdate(e){return!0}update(e){void 0!==this._reflectingProperties&&this._reflectingProperties.size>0&&(this._reflectingProperties.forEach((e,t)=>this._propertyToAttribute(t,this[t],e)),this._reflectingProperties=void 0),this._markUpdated()}updated(e){}firstUpdated(e){}}me.finalized=!0; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +const ge=e=>t=>"function"==typeof t?((e,t)=>(window.customElements.define(e,t),t))(e,t):((e,t)=>{const{kind:i,elements:n}=t;return{kind:i,elements:n,finisher(t){window.customElements.define(e,t)}}})(e,t),fe=(e,t)=>"method"===t.kind&&t.descriptor&&!("value"in t.descriptor)?Object.assign(Object.assign({},t),{finisher(i){i.createProperty(t.key,e)}}):{kind:"field",key:Symbol(),placement:"own",descriptor:{},initializer(){"function"==typeof t.initializer&&(this[t.key]=t.initializer.call(this))},finisher(i){i.createProperty(t.key,e)}};function ve(e){return(t,i)=>void 0!==i?((e,t,i)=>{t.constructor.createProperty(i,e)})(e,t,i):fe(e,t)} +/** +@license +Copyright (c) 2019 The Polymer Project Authors. All rights reserved. +This code may only be used under the BSD style license found at +http://polymer.github.io/LICENSE.txt The complete set of authors may be found at +http://polymer.github.io/AUTHORS.txt The complete set of contributors may be +found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as +part of the polymer project is also subject to an additional IP rights grant +found at http://polymer.github.io/PATENTS.txt +*/const ye=window.ShadowRoot&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,_e=Symbol();class Se{constructor(e,t){if(t!==_e)throw new Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=e}get styleSheet(){return void 0===this._styleSheet&&(ye?(this._styleSheet=new CSSStyleSheet,this._styleSheet.replaceSync(this.cssText)):this._styleSheet=null),this._styleSheet}toString(){return this.cssText}}const be=(e,...t)=>{const i=t.reduce((t,i,n)=>t+(e=>{if(e instanceof Se)return e.cssText;if("number"==typeof e)return e;throw new Error(`Value passed to 'css' function must be a 'css' function result: ${e}. Use 'unsafeCSS' to pass non-literal values, but\n take care to ensure page security.`)})(i)+e[n+1],e[0]);return new Se(i,_e)}; +/** + * @license + * Copyright (c) 2017 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +(window.litElementVersions||(window.litElementVersions=[])).push("2.4.0");const we={};class xe extends me{static getStyles(){return this.styles}static _getUniqueStyles(){if(this.hasOwnProperty(JSCompiler_renameProperty("_styles",this)))return;const e=this.getStyles();if(Array.isArray(e)){const t=(e,i)=>e.reduceRight((e,i)=>Array.isArray(i)?t(i,e):(e.add(i),e),i),i=t(e,new Set),n=[];i.forEach(e=>n.unshift(e)),this._styles=n}else this._styles=void 0===e?[]:[e];this._styles=this._styles.map(e=>{if(e instanceof CSSStyleSheet&&!ye){const t=Array.prototype.slice.call(e.cssRules).reduce((e,t)=>e+t.cssText,"");return new Se(String(t),_e)}return e})}initialize(){super.initialize(),this.constructor._getUniqueStyles(),this.renderRoot=this.createRenderRoot(),window.ShadowRoot&&this.renderRoot instanceof window.ShadowRoot&&this.adoptStyles()}createRenderRoot(){return this.attachShadow({mode:"open"})}adoptStyles(){const e=this.constructor._styles;0!==e.length&&(void 0===window.ShadyCSS||window.ShadyCSS.nativeShadow?ye?this.renderRoot.adoptedStyleSheets=e.map(e=>e instanceof CSSStyleSheet?e:e.styleSheet):this._needsShimAdoptedStyleSheets=!0:window.ShadyCSS.ScopingShim.prepareAdoptedCssText(e.map(e=>e.cssText),this.localName))}connectedCallback(){super.connectedCallback(),this.hasUpdated&&void 0!==window.ShadyCSS&&window.ShadyCSS.styleElement(this)}update(e){const t=this.render();super.update(e),t!==we&&this.constructor.render(t,this.renderRoot,{scopeName:this.localName,eventContext:this}),this._needsShimAdoptedStyleSheets&&(this._needsShimAdoptedStyleSheets=!1,this.constructor._styles.forEach(e=>{const t=document.createElement("style");t.textContent=e.cssText,this.renderRoot.appendChild(t)}))}render(){return we}}xe.finalized=!0,xe.render=(e,t,i)=>{if(!i||"object"!=typeof i||!i.scopeName)throw new Error("The `scopeName` option is required.");const n=i.scopeName,s=ie.has(t),r=oe&&11===t.nodeType&&!!t.host,o=r&&!de.has(n),a=o?document.createDocumentFragment():t;if(((e,t,i)=>{let n=ie.get(t);void 0===n&&(x(t,t.firstChild),ie.set(t,n=new W(Object.assign({templateFactory:ee},i))),n.appendInto(t)),n.setValue(e),n.commit()})(e,a,Object.assign({templateFactory:ae(n)},i)),o){const e=ie.get(a);ie.delete(a);const i=e.value instanceof D?e.value.template:void 0;le(n,a,i),x(t,t.firstChild),t.appendChild(a),ie.set(t,e)}!s&&r&&window.ShadyCSS.styleElement(t.host)}; +/** + * @license + * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. + * This code may only be used under the BSD style license found at + * http://polymer.github.io/LICENSE.txt + * The complete set of authors may be found at + * http://polymer.github.io/AUTHORS.txt + * The complete set of contributors may be found at + * http://polymer.github.io/CONTRIBUTORS.txt + * Code distributed by Google as part of the polymer project is also + * subject to an additional IP rights grant found at + * http://polymer.github.io/PATENTS.txt + */ +class Ce{constructor(e){this.classes=new Set,this.changed=!1,this.element=e;const t=(e.getAttribute("class")||"").split(/\s+/);for(const e of t)this.classes.add(e)}add(e){this.classes.add(e),this.changed=!0}remove(e){this.classes.delete(e),this.changed=!0}commit(){if(this.changed){let e="";this.classes.forEach(t=>e+=t+" "),this.element.setAttribute("class",e)}}}const Ee=new WeakMap,Ne=(Pe=e=>t=>{if(!(t instanceof q)||t instanceof Z||"class"!==t.committer.name||t.committer.parts.length>1)throw new Error("The `classMap` directive must be used in the `class` attribute and must be the only part in the attribute.");const{committer:i}=t,{element:n}=i;let s=Ee.get(t);void 0===s&&(n.setAttribute("class",i.strings.join(" ")),Ee.set(t,s=new Set));const r=n.classList||new Ce(n);s.forEach(t=>{t in e||(r.remove(t),s.delete(t))});for(const t in e){const i=e[t];i!=s.has(t)&&(i?(r.add(t),s.add(t)):(r.remove(t),s.delete(t)))}"function"==typeof r.commit&&r.commit()},(...e)=>{const t=Pe(...e);return H.set(t,!0),t});var Pe,Me,Te,ke,Oe;!function(e){let t,i;!function(e){e.LEFT="left",e.CENTER="center",e.RIGHT="right"}(t=e.AlignControls||(e.AlignControls={})),function(e){e.HEADER="header",e.INSIDE="inside"}(i=e.Name||(e.Name={}))}(Me||(Me={})),function(e){e.UP="up",e.DOWN="down"}(Te||(Te={})),function(e){e.AM="AM",e.PM="PM"}(ke||(ke={}));let $e=Oe=class extends xe{get amClass(){return{"time-period":!0,active:this.period===ke.AM}}get pmClass(){return{"time-period":!0,active:this.period===ke.PM}}render(){return se`
    + ${"single"===this.mode?this.renderSingle():this.renderDouble()} +
    `}onTimePeriodChange(){const e=new CustomEvent(Oe.EVENT_TOGGLE);this.dispatchEvent(e)}renderSingle(){return se`
    + ${this.period} +
    `}renderDouble(){return se`
    + AM +
    +
    + PM +
    `}static get styles(){return be` + .time-period-selector { + padding: 0 8px; + } + + .time-period { + width: 30px; + padding: 8px; + background: var(--tpc-off-color); + color: var(--tpc-text-color); + text-align: center; + font-size: 1em; + cursor: pointer; + } + + .time-period.active { + background: var(--tpc-accent-color); + } + `}};var Ve;$e.EVENT_TOGGLE="toggle",e([ve()],$e.prototype,"period",void 0),e([ve()],$e.prototype,"mode",void 0),$e=Oe=e([ge("time-period")],$e);let Ae=Ve=class extends xe{render(){return se` +
    + ${this.renderStepChanger(Te.UP)} + + ${this.renderStepChanger(Te.DOWN)} +
    + `}onInputChange({target:{value:e}}){this.unit.setStringValue(e);const t=new CustomEvent(Ve.EVENT_UPDATE);this.dispatchEvent(t)}onStepChangerClick(e){const t=new CustomEvent(Ve.EVENT_STEP_CHANGE,{detail:{direction:e}});this.dispatchEvent(t)}renderStepChanger(e){return se` +
    this.onStepChangerClick(e)}> + + +
    + `}static get styles(){return be` + .time-unit { + display: flex; + flex-direction: column; + align-items: center; + padding: 0 8px; + } + + .time-picker-icon { + width: 30px; + padding: 8px; + text-align: center; + cursor: pointer; + color: var(--tpc-icon-color); + } + + .time-input { + width: 30px; + padding: 8px 8px 6px; + background: var(--tpc-elements-background-color); + border: 0; + border-bottom: 2px solid var(--tpc-elements-background-color); + color: var(--tpc-text-color, #fff); + text-align: center; + font-size: 1em; + -moz-appearance: textfield; + + transition: border-color 0.2s ease-in-out; + } + + .time-input:focus { + outline: none; + } + + .time-input:invalid { + box-shadow: none; + outline: none; + border: 0; + border-bottom: 2px solid red; + } + + .time-input::-webkit-inner-spin-button, + .time-input::-webkit-outer-spin-button { + -webkit-appearance: none; + margin: 0; + } + `}};Ae.EVENT_UPDATE="update",Ae.EVENT_STEP_CHANGE="stepChange",e([ve()],Ae.prototype,"unit",void 0),Ae=Ve=e([ge("time-unit")],Ae);const He=Me.AlignControls.CENTER,je=Me.Name.HEADER;var Ie;let Ue=Ie=class extends xe{get entity(){return this.hass.states[this.config.entity]}get datetimeEntities(){return Object.keys(this.hass.states).filter(e=>"input_datetime"===b(e)&&!0===this.hass.states[e].attributes.has_time)}render(){var e,t,i,n,s,r,o,a,c;return se`
    + + + ${this.datetimeEntities.map(e=>se`${e}`)} + + + +
    +
    + + Show name? +
    + + + ${Object.values(Me.Name).map(e=>se`${e}`)} + + +
    +
    + + +
    +
    +
    + + Show seconds? +
    + +
    +
    +
    + + 12-Hour mode +
    + ${12===this.config.hour_mode?se`
    + + "Single" hour mode layout +
    `:""} +
    +
    + + + ${Object.values(Me.AlignControls).map(e=>se`${e}`)} + + +
    + + Link Values +
    +
    +
    +
    + + Embedded +
    +
    +
    `}setConfig(e){this.config=e}onHourModeChange({target:{checked:e}}){const t=e?12:24,i=Object.assign(Object.assign({},this.config),{hour_mode:t});this.dispatch(i)}onHideNameChange({target:{checked:e}}){const t=Object.assign(Object.assign({},this.config.hide),{name:!e}),i=Object.assign(Object.assign({},this.config),{hide:t});this.dispatch(i)}onHideSecondsChange({target:{checked:e}}){const t=Object.assign(Object.assign({},this.config.hide),{seconds:!e}),i=Object.assign(Object.assign({},this.config),{hide:t});this.dispatch(i)}onLayoutAlignChange({detail:{value:e}}){const t=Object.assign(Object.assign({},this.config.layout),{align_controls:e}),i=Object.assign(Object.assign({},this.config),{layout:t});this.dispatch(i)}onLayoutNameChange({detail:{value:e}}){const t=Object.assign(Object.assign({},this.config.layout),{name:e}),i=Object.assign(Object.assign({},this.config),{layout:t});this.dispatch(i)}onHourModeLayoutChange({target:{checked:e}}){const t=e?"single":"double",i=Object.assign(Object.assign({},this.config),{layout:Object.assign(Object.assign({},this.config.layout),{hour_mode:t})});this.dispatch(i)}onLinkValuesChange({target:{checked:e}}){const t=Object.assign(Object.assign({},this.config),{link_values:e});this.dispatch(t)}onEmbeddedChange({target:{checked:e}}){const t=Object.assign(Object.assign({},this.config),{layout:{embedded:e}});this.dispatch(t)}onValueChange(e){const t=e.target.configValue;let i=e.detail.value;if(Ie.NUMBER_PROPERTIES.indexOf(t)>-1&&(i=parseInt(i),isNaN(i)))return;if(this.config[t]===i)return;const n=Object.assign(Object.assign({},this.config),{[t]:i});this.dispatch(n)}dispatch(e){const t=new CustomEvent(Ie.CONFIG_CHANGED_EVENT,{bubbles:!0,composed:!0,detail:{config:e}});this.dispatchEvent(t)}static get styles(){return be` + ha-switch { + padding: 16px 0; + margin-right: 16px; + } + .side-by-side { + display: flex; + } + .side-by-side > * { + flex: 1; + padding-right: 4px; + } + .suffix { + margin: 0 8px; + } + `}};Ue.NUMBER_PROPERTIES=["hour_step","minute_step","second_step","hour_mode"],Ue.CONFIG_CHANGED_EVENT="config-changed",e([ve({type:Object})],Ue.prototype,"hass",void 0),e([ve()],Ue.prototype,"config",void 0),Ue=Ie=e([ge("time-picker-card-editor")],Ue);class De{constructor(e,t,i){this._value=e,this._step=t,this._limit=i}get value(){return this._value}setStringValue(e){this.isValidString(e)&&this.setValue(parseInt(e))}stepUpdate(e,t=this._step){e===Te.UP?this.increment(t):this.decrement(t)}toString(){return this.value<10?"0"+this.value:this.value.toString()}increment(e=this._step){this.setValue(this.value+e)}decrement(e=this._step){this.setValue(this.value-e)}setValue(e){isNaN(e)||((e>=this._limit||e<0)&&(e=(e+this._limit)%this._limit),this._value=e)}isValidString(e){const t=parseInt(e);return!isNaN(t)&&t>=0&&t=this._limit||t<0}}Re.VALUE_LIMIT=60;class Ye extends De{constructor(e,t=5){super(e,t,Ye.VALUE_LIMIT),this.minValue=0,this.maxValue=Ye.VALUE_LIMIT-1}willOverflow(e){const t=e===Te.UP?this.value+this._step:this.value-this._step;return t>=this._limit||t<0}}Ye.VALUE_LIMIT=60;class ze{constructor(e,t,i,n=!1){this.hour=e,this.minute=t,this.second=i,this._linkValues=n}hourStep(e){this.hour.stepUpdate(e)}minuteStep(e){this._linkValues&&this.minute.willOverflow(e)&&this.hour.stepUpdate(e,1),this.minute.stepUpdate(e)}secondStep(e){this._linkValues&&this.second.willOverflow(e)&&this.minute.stepUpdate(e,1),this.second.stepUpdate(e)}get value(){return`${this.hour.value}:${this.minute.value}:${this.second.value}`}}class Fe{static error(e,t){const i=document.createElement("hui-error-card");return i.setConfig({type:"error",error:e,origConfig:t}),se`${i}`}static headerName(e){return se`
    ${e}
    `}static nestedName(e,t){return se` +
    ${e}
    `}}console.info("%c TIME-PICKER-CARD \n%c Version 1.1.1 ","color: orange; font-weight: bold; background: black","color: white; font-weight: bold; background: dimgray"),window.customCards=window.customCards||[],window.customCards.push({type:"time-picker-card",name:"Time Picker Card",description:"A Time Picker card for setting the time value of Input Datetime entities."});let Be=class extends xe{get entity(){return this.hass.states[this.config.entity]}get isEmbedded(){var e;return!0===(null===(e=this.config.layout)||void 0===e?void 0:e.embedded)}get hasNameInHeader(){var e,t,i;return Boolean(this.name)&&!1===Boolean(null===(e=this.config.hide)||void 0===e?void 0:e.name)&&(null===(t=this.config.layout)||void 0===t?void 0:t.name)!==Me.Name.INSIDE&&!1===Boolean(null===(i=this.config.layout)||void 0===i?void 0:i.embedded)}get hasNameInside(){var e,t,i;return Boolean(this.name)&&!1===Boolean(null===(e=this.config.hide)||void 0===e?void 0:e.name)&&((null===(t=this.config.layout)||void 0===t?void 0:t.name)===Me.Name.INSIDE||Boolean(null===(i=this.config.layout)||void 0===i?void 0:i.embedded))}get name(){var e;return this.config.name||(null===(e=this.entity)||void 0===e?void 0:e.attributes.friendly_name)}get shouldShowPeriod(){return 12===this.config.hour_mode}get layoutAlign(){var e,t;return null!==(t=null===(e=this.config.layout)||void 0===e?void 0:e.align_controls)&&void 0!==t?t:He}get haCardClass(){return{embedded:this.isEmbedded}}get rowClass(){return{"time-picker-row":!0,"with-header-name":this.hasNameInHeader,embedded:this.isEmbedded}}get contentClass(){return{"time-picker-content":!0,["layout-"+this.layoutAlign]:!0}}render(){var e,t,i;if(!this.entity)return Fe.error("Entity not found",this.config);if("input_datetime"!==b(this.entity.entity_id))return Fe.error("You must set an input_datetime entity",this.config);if(!this.entity.attributes.has_time)return Fe.error("You must set an input_datetime entity that sets has_time: true",this.config);const{hour:n,minute:s,second:r}=this.entity.attributes,o=new Le(n,this.config.hour_step,this.config.hour_mode),a=new Re(s,this.config.minute_step),c=new Ye(r,this.config.second_step);return this.time=new ze(o,a,c,this.config.link_values),this.period=o.value>=12?ke.PM:ke.AM,se` + + ${this.hasNameInHeader?Fe.headerName(this.name):""} +
    + ${this.hasNameInside?Fe.nestedName(this.name,this.entity):""} + +
    + +
    :
    + + ${!1===(null===(e=this.config.hide)||void 0===e?void 0:e.seconds)?se`
    :
    + `:""} + ${this.shouldShowPeriod?se``:""} +
    +
    +
    + `}setConfig(e){if(!e)throw new Error("Invalid configuration");if(!e.entity)throw new Error("You must set an entity");if(e.hour_mode&&12!==e.hour_mode&&24!==e.hour_mode)throw new Error("Invalid hour_mode: select either 12 or 24");this.config=e}getCardSize(){return 3}onPeriodToggle(){this.time.hour.togglePeriod(),this.callHassService()}onHourStepChange(e){this.time.hourStep(e.detail.direction),this.callHassService()}onMinuteStepChange(e){this.time.minuteStep(e.detail.direction),this.callHassService()}onSecondStepChange(e){this.time.secondStep(e.detail.direction),this.callHassService()}callHassService(){if(!this.hass)throw new Error("Unable to update datetime");return this.hass.callService("input_datetime","set_datetime",{entity_id:this.entity.entity_id,time:this.time.value})}static get styles(){return be` + :host { + --tpc-elements-background-color: var( + --time-picker-elements-background-color, + var(--primary-color) + ); + + --tpc-icon-color: var(--time-picker-icon-color, var(--primary-text-color)); + --tpc-text-color: var(--time-picker-text-color, #fff); + --tpc-accent-color: var(--time-picker-accent-color, var(--primary-color)); + --tpc-off-color: var(--time-picker-off-color, var(--disabled-text-color)); + + --tpc-border-radius: var(--time-picker-border-radius, var(--ha-card-border-radius, 4px)); + } + + ha-card.embedded { + box-shadow: none; + } + + .time-picker-header { + padding: 16px; + color: var(--tpc-text-color); + background-color: var(--tpc-elements-background-color); + border-top-left-radius: var(--tpc-border-radius); + border-top-right-radius: var(--tpc-border-radius); + font-size: 1em; + text-align: center; + } + + .time-picker-row { + display: flex; + flex-direction: row; + align-items: center; + padding: 16px; + } + + .time-picker-row.embedded { + padding: 0; + } + + .time-picker-row.with-header-name { + padding: 8px 16px 16px; + } + + .time-picker-content { + display: flex; + flex-direction: row; + align-items: center; + flex: 1 0 auto; + } + + .time-picker-content.layout-left { + justify-content: flex-start; + } + + .time-picker-content.layout-center { + justify-content: center; + } + + .time-picker-content.layout-right { + justify-content: flex-end; + } + + .entity-name-inside { + margin-left: 16px; + } + `}static getStubConfig(e,t){return{entity:t.find(e=>"input_datetime"===b(e))||"input_datetime.example_entity",hour_mode:24,hour_step:1,minute_step:5}}static getConfigElement(){return document.createElement("time-picker-card-editor")}};e([ve({type:Object})],Be.prototype,"hass",void 0),e([ve()],Be.prototype,"config",void 0),e([ve()],Be.prototype,"time",void 0),e([ve()],Be.prototype,"period",void 0),Be=e([ge("time-picker-card")],Be);export{Be as TimePickerCard}; diff --git a/www/icons/weather_icons/LICENSE b/www/icons/weather_icons/LICENSE new file mode 100644 index 0000000..c51f3a6 --- /dev/null +++ b/www/icons/weather_icons/LICENSE @@ -0,0 +1,6 @@ +The works contained in this archive were created by amCharts (https://www.amcharts.com/) +and is licensed under Creative Commons Attribution 4.0 International Public License: + +https://creativecommons.org/licenses/by/4.0/ + +If in doubt, email amCharts at contact@amcharts.com \ No newline at end of file diff --git a/www/icons/weather_icons/animated/cloudy-day-1.svg b/www/icons/weather_icons/animated/cloudy-day-1.svg new file mode 100644 index 0000000..c1f1922 --- /dev/null +++ b/www/icons/weather_icons/animated/cloudy-day-1.svg @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/cloudy-day-2.svg b/www/icons/weather_icons/animated/cloudy-day-2.svg new file mode 100644 index 0000000..712a564 --- /dev/null +++ b/www/icons/weather_icons/animated/cloudy-day-2.svg @@ -0,0 +1,176 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/cloudy-day-3.svg b/www/icons/weather_icons/animated/cloudy-day-3.svg new file mode 100644 index 0000000..ddcd408 --- /dev/null +++ b/www/icons/weather_icons/animated/cloudy-day-3.svg @@ -0,0 +1,175 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/cloudy-night-1.svg b/www/icons/weather_icons/animated/cloudy-night-1.svg new file mode 100644 index 0000000..bc6360d --- /dev/null +++ b/www/icons/weather_icons/animated/cloudy-night-1.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/cloudy-night-2.svg b/www/icons/weather_icons/animated/cloudy-night-2.svg new file mode 100644 index 0000000..391ceeb --- /dev/null +++ b/www/icons/weather_icons/animated/cloudy-night-2.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/cloudy-night-3.svg b/www/icons/weather_icons/animated/cloudy-night-3.svg new file mode 100644 index 0000000..2425af9 --- /dev/null +++ b/www/icons/weather_icons/animated/cloudy-night-3.svg @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/cloudy.svg b/www/icons/weather_icons/animated/cloudy.svg new file mode 100644 index 0000000..d94fd30 --- /dev/null +++ b/www/icons/weather_icons/animated/cloudy.svg @@ -0,0 +1,500 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/day.svg b/www/icons/weather_icons/animated/day.svg new file mode 100644 index 0000000..6566b16 --- /dev/null +++ b/www/icons/weather_icons/animated/day.svg @@ -0,0 +1,521 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/night.svg b/www/icons/weather_icons/animated/night.svg new file mode 100644 index 0000000..0c13686 --- /dev/null +++ b/www/icons/weather_icons/animated/night.svg @@ -0,0 +1,503 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/rainy-1.svg b/www/icons/weather_icons/animated/rainy-1.svg new file mode 100644 index 0000000..575df6c --- /dev/null +++ b/www/icons/weather_icons/animated/rainy-1.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/rainy-2.svg b/www/icons/weather_icons/animated/rainy-2.svg new file mode 100644 index 0000000..faa8cf0 --- /dev/null +++ b/www/icons/weather_icons/animated/rainy-2.svg @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/rainy-3.svg b/www/icons/weather_icons/animated/rainy-3.svg new file mode 100644 index 0000000..bfb2a4c --- /dev/null +++ b/www/icons/weather_icons/animated/rainy-3.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/rainy-4.svg b/www/icons/weather_icons/animated/rainy-4.svg new file mode 100644 index 0000000..52252b4 --- /dev/null +++ b/www/icons/weather_icons/animated/rainy-4.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/rainy-5.svg b/www/icons/weather_icons/animated/rainy-5.svg new file mode 100644 index 0000000..52461db --- /dev/null +++ b/www/icons/weather_icons/animated/rainy-5.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/rainy-6.svg b/www/icons/weather_icons/animated/rainy-6.svg new file mode 100644 index 0000000..ea384b1 --- /dev/null +++ b/www/icons/weather_icons/animated/rainy-6.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/rainy-7.svg b/www/icons/weather_icons/animated/rainy-7.svg new file mode 100644 index 0000000..220dcf8 --- /dev/null +++ b/www/icons/weather_icons/animated/rainy-7.svg @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/snowy-1.svg b/www/icons/weather_icons/animated/snowy-1.svg new file mode 100644 index 0000000..1045466 --- /dev/null +++ b/www/icons/weather_icons/animated/snowy-1.svg @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/snowy-2.svg b/www/icons/weather_icons/animated/snowy-2.svg new file mode 100644 index 0000000..f01f8b7 --- /dev/null +++ b/www/icons/weather_icons/animated/snowy-2.svg @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/snowy-3.svg b/www/icons/weather_icons/animated/snowy-3.svg new file mode 100644 index 0000000..8c51ef8 --- /dev/null +++ b/www/icons/weather_icons/animated/snowy-3.svg @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/snowy-4.svg b/www/icons/weather_icons/animated/snowy-4.svg new file mode 100644 index 0000000..e282d18 --- /dev/null +++ b/www/icons/weather_icons/animated/snowy-4.svg @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/snowy-5.svg b/www/icons/weather_icons/animated/snowy-5.svg new file mode 100644 index 0000000..463d6d8 --- /dev/null +++ b/www/icons/weather_icons/animated/snowy-5.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/snowy-6.svg b/www/icons/weather_icons/animated/snowy-6.svg new file mode 100644 index 0000000..beba9e4 --- /dev/null +++ b/www/icons/weather_icons/animated/snowy-6.svg @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/thunder.svg b/www/icons/weather_icons/animated/thunder.svg new file mode 100644 index 0000000..a27ff55 --- /dev/null +++ b/www/icons/weather_icons/animated/thunder.svg @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/weather-sprite.svg b/www/icons/weather_icons/animated/weather-sprite.svg new file mode 100644 index 0000000..b0117b7 --- /dev/null +++ b/www/icons/weather_icons/animated/weather-sprite.svg @@ -0,0 +1,1245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/weather.svg b/www/icons/weather_icons/animated/weather.svg new file mode 100644 index 0000000..7258930 --- /dev/null +++ b/www/icons/weather_icons/animated/weather.svg @@ -0,0 +1,1245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/weather_sagittarius.svg b/www/icons/weather_icons/animated/weather_sagittarius.svg new file mode 100644 index 0000000..1e85eb7 --- /dev/null +++ b/www/icons/weather_icons/animated/weather_sagittarius.svg @@ -0,0 +1,9 @@ + + + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/animated/weather_sunset.svg b/www/icons/weather_icons/animated/weather_sunset.svg new file mode 100644 index 0000000..b90dc3f --- /dev/null +++ b/www/icons/weather_icons/animated/weather_sunset.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/www/icons/weather_icons/static/cloudy-day-1.svg b/www/icons/weather_icons/static/cloudy-day-1.svg new file mode 100644 index 0000000..0779355 --- /dev/null +++ b/www/icons/weather_icons/static/cloudy-day-1.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/cloudy-day-2.svg b/www/icons/weather_icons/static/cloudy-day-2.svg new file mode 100644 index 0000000..e8d51a2 --- /dev/null +++ b/www/icons/weather_icons/static/cloudy-day-2.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/cloudy-day-3.svg b/www/icons/weather_icons/static/cloudy-day-3.svg new file mode 100644 index 0000000..6ccf90b --- /dev/null +++ b/www/icons/weather_icons/static/cloudy-day-3.svg @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/cloudy-night-1.svg b/www/icons/weather_icons/static/cloudy-night-1.svg new file mode 100644 index 0000000..1f43b45 --- /dev/null +++ b/www/icons/weather_icons/static/cloudy-night-1.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/cloudy-night-2.svg b/www/icons/weather_icons/static/cloudy-night-2.svg new file mode 100644 index 0000000..054d6ef --- /dev/null +++ b/www/icons/weather_icons/static/cloudy-night-2.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/cloudy-night-3.svg b/www/icons/weather_icons/static/cloudy-night-3.svg new file mode 100644 index 0000000..3e9df40 --- /dev/null +++ b/www/icons/weather_icons/static/cloudy-night-3.svg @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/cloudy.svg b/www/icons/weather_icons/static/cloudy.svg new file mode 100644 index 0000000..a95dacd --- /dev/null +++ b/www/icons/weather_icons/static/cloudy.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/day.svg b/www/icons/weather_icons/static/day.svg new file mode 100644 index 0000000..e638ec7 --- /dev/null +++ b/www/icons/weather_icons/static/day.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/night.svg b/www/icons/weather_icons/static/night.svg new file mode 100644 index 0000000..918ffd5 --- /dev/null +++ b/www/icons/weather_icons/static/night.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/rainy-1.svg b/www/icons/weather_icons/static/rainy-1.svg new file mode 100644 index 0000000..f3bdb19 --- /dev/null +++ b/www/icons/weather_icons/static/rainy-1.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/rainy-2.svg b/www/icons/weather_icons/static/rainy-2.svg new file mode 100644 index 0000000..294e2d0 --- /dev/null +++ b/www/icons/weather_icons/static/rainy-2.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/rainy-3.svg b/www/icons/weather_icons/static/rainy-3.svg new file mode 100644 index 0000000..2576756 --- /dev/null +++ b/www/icons/weather_icons/static/rainy-3.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/rainy-4.svg b/www/icons/weather_icons/static/rainy-4.svg new file mode 100644 index 0000000..ca0a309 --- /dev/null +++ b/www/icons/weather_icons/static/rainy-4.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/rainy-5.svg b/www/icons/weather_icons/static/rainy-5.svg new file mode 100644 index 0000000..423a393 --- /dev/null +++ b/www/icons/weather_icons/static/rainy-5.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/rainy-6.svg b/www/icons/weather_icons/static/rainy-6.svg new file mode 100644 index 0000000..ebb00e6 --- /dev/null +++ b/www/icons/weather_icons/static/rainy-6.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/rainy-7.svg b/www/icons/weather_icons/static/rainy-7.svg new file mode 100644 index 0000000..6535324 --- /dev/null +++ b/www/icons/weather_icons/static/rainy-7.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/snowy-1.svg b/www/icons/weather_icons/static/snowy-1.svg new file mode 100644 index 0000000..d8217db --- /dev/null +++ b/www/icons/weather_icons/static/snowy-1.svg @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/snowy-2.svg b/www/icons/weather_icons/static/snowy-2.svg new file mode 100644 index 0000000..ba822b9 --- /dev/null +++ b/www/icons/weather_icons/static/snowy-2.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/snowy-3.svg b/www/icons/weather_icons/static/snowy-3.svg new file mode 100644 index 0000000..28fde7a --- /dev/null +++ b/www/icons/weather_icons/static/snowy-3.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/snowy-4.svg b/www/icons/weather_icons/static/snowy-4.svg new file mode 100644 index 0000000..6825143 --- /dev/null +++ b/www/icons/weather_icons/static/snowy-4.svg @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/snowy-5.svg b/www/icons/weather_icons/static/snowy-5.svg new file mode 100644 index 0000000..08345bf --- /dev/null +++ b/www/icons/weather_icons/static/snowy-5.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/snowy-6.svg b/www/icons/weather_icons/static/snowy-6.svg new file mode 100644 index 0000000..f24d541 --- /dev/null +++ b/www/icons/weather_icons/static/snowy-6.svg @@ -0,0 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/thunder.svg b/www/icons/weather_icons/static/thunder.svg new file mode 100644 index 0000000..e2da380 --- /dev/null +++ b/www/icons/weather_icons/static/thunder.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/weather-sprite.svg b/www/icons/weather_icons/static/weather-sprite.svg new file mode 100644 index 0000000..654fb42 --- /dev/null +++ b/www/icons/weather_icons/static/weather-sprite.svg @@ -0,0 +1,1245 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/weather.svg b/www/icons/weather_icons/static/weather.svg new file mode 100644 index 0000000..c194c2c --- /dev/null +++ b/www/icons/weather_icons/static/weather.svg @@ -0,0 +1,622 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/weather_sagittarius.svg b/www/icons/weather_icons/static/weather_sagittarius.svg new file mode 100644 index 0000000..1e85eb7 --- /dev/null +++ b/www/icons/weather_icons/static/weather_sagittarius.svg @@ -0,0 +1,9 @@ + + + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/www/icons/weather_icons/static/weather_sunset.svg b/www/icons/weather_icons/static/weather_sunset.svg new file mode 100644 index 0000000..b90dc3f --- /dev/null +++ b/www/icons/weather_icons/static/weather_sunset.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/www/panels.yaml b/www/panels.yaml new file mode 100644 index 0000000..21862a1 --- /dev/null +++ b/www/panels.yaml @@ -0,0 +1,10 @@ +panel_custom: + - name: panel-redirect + # url_path needs to be unique for each panel_custom config + url_path: redirect-states + sidebar_title: States + sidebar_icon: hass:gesture-tap-button + module_url: /local/panel-redirect.js + config: + # Where you want to redirect to + target: /developer-tools/state diff --git a/www/sidebar-order.yaml b/www/sidebar-order.yaml new file mode 100644 index 0000000..5a66098 --- /dev/null +++ b/www/sidebar-order.yaml @@ -0,0 +1,35 @@ +title: 238 McHaley +order: + - item: map + hide: true + - item: history + hide: true + - item: overview + hide: true + - item: logbook + hide: true + - item: 'Media Browser' + hide: true + - item: 'Visual Studio Code' + hide: true + - item: ESPHome + hide: true + - item: HACS + hide: true + - item: Setup + icon: "mdi:vector-link" + bottom: true + + - new_item: true + item: Server Controls + href: /config/server_control + icon: "mdi:server" + bottom: true +# exceptions: +# - user: sysadmin +# base_order: true +# order: +# - item: map +# hide: false +# - item: developer tools +# hide: true diff --git a/www/wupws_icons/00.png b/www/wupws_icons/00.png new file mode 100644 index 0000000000000000000000000000000000000000..991ca0c5f308d899a90b887bf9e1b08c957b77ca GIT binary patch literal 2090 zcmZ{ldpOgJAICRFGhr^nYSUbjDAA6xL-ft1jojtZkjA=@OAI-tm`(|2ExF_p)p5T} zq|nsq;xd^gxnC-4Cd?#qr=OnFALl&J@BH!ke4gj?=j(Z2t~=Tvm4R)8fj}S`0^ZVj z^Rj;oRC04ydNA`gk5nk$GaLkxR{SwwPy|?l?=Q8(7>BpCdCML#4;_Y-? zop=MS6U|0rd$?v8O^SUd@Vpkp+{jc~Ov3mX0$oh^>+HPMj^@ymT~16H6Kn zwztmk17!VQTc}L&LCRvduaj_E+8`2#eT?bLR)w4s*eGhcJfrL5sdmbj)=m;NEl5JO z^QeLfwSE<@{d!--;rpg330mF(T}}zz#VvRl1)rDWn4{4Twx(*vl#r%>tqQ}8oVhXP zvTOo$At;MM3!lk2B~2Y{sM3;BIYH zAQl#{;`>%I4g&%}w11G?eEbd7K=(lB3!VAfUn172BC8~wsC@U%44L9V?ZbPM1P#d9 zMGI}SSbepFl>=}{1b7HRwHqH4y}{WRuDhBBdxbkN8d;L$cJm;X3Gq;2PCd&dD_g5L z3lbFIEiTNzsMw&ZK;$krBMS;+42qfH1@1}p`^H@D~P35e;Nr-^~ORpB~V2Z=G!K54$U*kk?AH*l*nfwRVhb7MAdJQx|C z6gx2MJana0zkA&fA7&rlB!O%of>Tk}3=Ru%RuIeJ+h8fC1@0!+m1QP%>*j)2>@_VhjB8U6>Ft=>QV1=&(S1-vc7wuNl0l%%v#y*IDvle z2^UT_(XLYi&(=`+eY!`G($^@nX5i={Bbh;z3l7)&>$ax#yH(i!D_3tWcu(HbcwdCh zSQ0k4j>Ld~miyaAPj=@H9>;$&HJ@{TO_YQ4m%gUUc(bjz6`6sto*a}4ACp+&KF5)# zrw1bd|EDva`+7aC)R~C@+&9Fd7W~7n|00Kh{#0N}k_@Hum&=9cPRxJHRc5(~@0&piY@sJbiK{yZXTPHDXRsMG7+`>1w|g}C=SE zPD{zLBFPUAg$^e^64vMN&i=++P3T@{2Gk8nsMpuCQ&G^rzVP4ph^7jdbCJ1W~h73+|@+OL7B!W)*DD`i=4J;ai7g0 zX|r7AA+i23>=Qs@$M7=~AWpgBzh(HUI$k#NelTo4t(PyL^H7>n!qdIaqhuD;sXA)ATt*ZjtK#Eb^&<^! zvFkpcFzZ^^qh44U;f-rcLhQW+8xkoADNT8LflTMi`6&R=?+M^rrXP75teB^P_$YzE``V{~HC$>|dC+LOlxx z&bG6;%H@vOl=>mGx?4INCsIO~vzQrM_a~v8lI(7NCk20>JsoJ=0$dW*+-|YCW}BIc z@uO50)ySQiNK)|su;&*rw2SlY%CqEgIhrtY^4@~x0KhoSV?I8Zb@^Hqu<(a14)qE1 z&C?&L*3T{9GP2~AI;5s%-L-h-0s~zVd0RJWMsuRw=5F6^@{Ogelz6z7QFLDBJ(O); z-4l0+x{`TMo3Zl-vuj&Iu?y|q5=comZzN8gd*xQ7GhAO2b{G$Lcu^R|q5Ocaq V{>n%RcC+U}1S@+><`G}o{{RaNv7!I~ literal 0 HcmV?d00001 diff --git a/www/wupws_icons/01.png b/www/wupws_icons/01.png new file mode 100644 index 0000000000000000000000000000000000000000..1129ba710721158e93328d5feaba98b600784074 GIT binary patch literal 2516 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 oyAHHN{% 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 N002ovPDHLkV1oPdEyMr- literal 0 HcmV?d00001 diff --git a/www/wupws_icons/04.png b/www/wupws_icons/04.png new file mode 100644 index 0000000000000000000000000000000000000000..501c80f41fe6f054bd1cb2b5192af8ff3691ef5c GIT binary patch literal 3279 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 N002ovPDHLkV1oPdEyMr- literal 0 HcmV?d00001 diff --git a/www/wupws_icons/05.png b/www/wupws_icons/05.png new file mode 100644 index 0000000000000000000000000000000000000000..7fb036c1f048a6155cf4fa791bafbbedd6268633 GIT binary patch literal 3478 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 literal 0 HcmV?d00001 diff --git a/www/wupws_icons/06.png b/www/wupws_icons/06.png new file mode 100644 index 0000000000000000000000000000000000000000..4e9607044ac36521e93e49bd4fc7191cc1de69d3 GIT binary patch literal 3324 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 literal 0 HcmV?d00001 diff --git a/www/wupws_icons/08.png b/www/wupws_icons/08.png new file mode 100644 index 0000000000000000000000000000000000000000..549b9f29fffa43d990dc88c17ea24ec9942fcffb GIT binary patch literal 3011 zcmV;!3q16RP)_wx6bA3uI108q>kO#px(00;trAOHvg zfFJ+}0uVAEXoBHze}8{szD&%u{kmoM`?a|qcYe10etmX!_DCz*nt(Mw$2r_b{C_kkc8Y8m`as(N7^pF*QpJ<8Je9@T()F302 zBTZg5IUu#_UXP?>nzuy+`I%ZFhn;$4RH;_<5k%91Z`|8f`E4v~Nr)gxazztZXEaeH zYC*Ixr`O6p7h@S^k2b~9e)|ozhcg~bnw1hk!exdWPB|Sz6p2}ooO{?g2oh*|HKX?M zR*Os!gikgoXO19YMGq+)nNs4n)6Q)S7{4-5nrt_3V_3f!zsb0Hm z^m^=LVOCV*AUgK(KkirWJeiyOWGBU)CO=QrYPtcRoN=-}MQiDLVq|QBNc3PSZ9jZh zA#C>^iZEBMWQk1>3G-~kFw!WMysQSbdW1u4f@~7TKVrbzStrp5Lj4hCM7ip^JAKfC zXu+2j{OH}8;d++GzqxOsLu!<)Z4X#5f@oLZH2K;4Q72WBt@|a+u2iJ2+%M5alX;u% zQU@)FMuTK2esBszPwF~?AP9uascS(}+89FT zh$aB&M-Wmh2pU-*;!=g+$lP`k zM0bGL6@oP%FeWyQ1<{QcwiolPV9cy*38GSDq)iZ`Af*eYw5C`PDIh|g&d$!B$MKFz zAdGiF%sdrEkNqMYAXc*7o~t*4 zXn_t8b81z3PWn;V`Op9na95UEkzxULN0Q9SQmcJI9%wI!Opa-t>}e~=iZ+AD%Wf$%+670)QX@2m*j000;trAOJ!0fhOo5J#r#VUPgsCw8(H^ps;^A zWYh5NoHrRko*)S+79_7zcy=d*%I^9ZErTHNLeVNtq6aC~D5JqlZ+N-E=T+0LbxFY{ zd3z?y>*0|=kv=9lrsm5x{q{l&?rFutijLL3qBTje#z?;R{Y8t%2n(7LLB?2U?+bBT zBjW6Dv2;W46@tW4XjesojOam;9z*nKTxge^2l-$glhUF@kg?IG(9UYsBY+@LrdRuq zd2?_*3=6t>+oxAjXzy=c@%>Ptt#hvSBhJG1RyB3`$6x=I*CVOj=3jsR8#NFS#41Ip zQdN32Z}Avca!6WtZLY8KRlf%Wu~wX478Kemn-RyM91|5|mnrWog1D?bsy>0+L>yNX zrBiF(Rs?aCUL7p7+e93P(PN$zZ~V97ZAB0p>6LWt?vH{mD+=vF)E-8Tv?S6!nIfH@ zGz1w;di9j_GjcA!^O)#?Ag-c^d;-6lcU7Znw`s(2m0Fceq9l4Ch!ygh1#eYxo_m(# zq~doX2!eEu9&(e-3JUGTfyYdmMjX$nm2hY3k4N5N1nElAysN6{(R*=jqKCZhPZjb2 zg7{6Z4s`7vkzUn+>F)KT$f7|6=({-23k2PV(Q_h5Z}m!1 z*RGdbUsJ0($9EGw5TqCK7WInl!n_Vd4+QZZJsJ-tT8Oklx6W=7Wj08416zB*NNA%D|Q1b>4Ve}k9yhjhY zN%u#s?>u$wE|SC3Nhr<{B--e4Xb}|Yl?B+Fw|n&HobO_H-XXOTSbL5jt>n0F`S43$k~CZdMS6uGR^*s; zkYiVpxrdi)LqyUm1Zjk=c~ZF+_^zTyo-|toMFc^R&e0=H+Rb4qt4U^#Cc# z=Lph-*}$LAW@<(BK#;~v2mX9*7eOHif>^V;Db4#)wTmBOnJx7qvWXa1} zgRKf+bIrQ0G_K?HR-zNuEW#{K@h~AB<7Mx@>^2fT`MWo zXY=nTmX_U0YIW-1&tml4GSRO|*W;Y0I@4F8O1kr7i8L#lyR_>fs4tHG9HIw;I1^=) z_}M$kRK&Hsy55_@yMi)>?Eip}sr^4TAfxjfB^3VST7yzIsU(6_h`&a+~002ovPDHLk FV1j%kX21Xd literal 0 HcmV?d00001 diff --git a/www/wupws_icons/09.png b/www/wupws_icons/09.png new file mode 100644 index 0000000000000000000000000000000000000000..7fffd6ec21cb525208ce7394bd776e05da8ca880 GIT binary patch literal 2698 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 Uuf|DA_&WvJSUOl#ow-E)4_`GNpa1{> literal 0 HcmV?d00001 diff --git a/www/wupws_icons/13.png b/www/wupws_icons/13.png new file mode 100644 index 0000000000000000000000000000000000000000..61363a597bb482816af8751d1faff226d9c6cde5 GIT binary patch literal 3204 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 bbBO|wR9*p|P-)uH4+3Crg2U7qUm^bspXbnI literal 0 HcmV?d00001 diff --git a/www/wupws_icons/14.png b/www/wupws_icons/14.png new file mode 100644 index 0000000000000000000000000000000000000000..87b023d20f887879b6aa527c7c6ca2e7a1c95d1c GIT binary patch literal 3968 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 JPDHLkV1f|*gjoOp literal 0 HcmV?d00001 diff --git a/www/wupws_icons/18.png b/www/wupws_icons/18.png new file mode 100644 index 0000000000000000000000000000000000000000..d0e457229ae2d122fff4801e2b8e18a6802b8f30 GIT binary patch literal 3015 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 JPDHLkV1f|*gjoOp literal 0 HcmV?d00001 diff --git a/www/wupws_icons/19.png b/www/wupws_icons/19.png new file mode 100644 index 0000000000000000000000000000000000000000..9363b69f1141ea02a281fd09ef1f088526804b6b GIT binary patch literal 4055 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 Hl0f@E2?s-| literal 0 HcmV?d00001 diff --git a/www/wupws_icons/24.png b/www/wupws_icons/24.png new file mode 100644 index 0000000000000000000000000000000000000000..034613c96366361fcc44eebb460ead4fd779931f GIT binary patch literal 2477 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 Hl0f@E2?s-| literal 0 HcmV?d00001 diff --git a/www/wupws_icons/25.png b/www/wupws_icons/25.png new file mode 100644 index 0000000000000000000000000000000000000000..ef167a79eb0ecbdd2c00d09fc24bc4eb6244070a GIT binary patch literal 3830 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 UvWv=f_C6B`@8;!N>l~W>e;C3KhX4Qo literal 0 HcmV?d00001 diff --git a/www/wupws_icons/27.png b/www/wupws_icons/27.png new file mode 100644 index 0000000000000000000000000000000000000000..3ac0084219a41b57f24ad7240f2bfe578b7f874a GIT binary patch literal 3901 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 Li^Y^{JCgqo<}F>g#Eloz?Dt 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 literal 0 HcmV?d00001 diff --git a/www/wupws_icons/29.png b/www/wupws_icons/29.png new file mode 100644 index 0000000000000000000000000000000000000000..1f86e1fd83e30e00d5a0a61485583e3d1afc5992 GIT binary patch literal 3037 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 literal 0 HcmV?d00001 diff --git a/www/wupws_icons/30.png b/www/wupws_icons/30.png new file mode 100644 index 0000000000000000000000000000000000000000..75e3024948bf6bed7af9f10eefdae966cdcbbbc4 GIT binary patch literal 3720 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 literal 0 HcmV?d00001 diff --git a/www/wupws_icons/34.png b/www/wupws_icons/34.png new file mode 100644 index 0000000000000000000000000000000000000000..cd4ff91da0a49cd2f861f1bc151f4d25537d64df GIT binary patch literal 4791 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 RV5|TD002ovPDHLkV1jde*lz#; literal 0 HcmV?d00001 diff --git a/www/wupws_icons/35.png b/www/wupws_icons/35.png new file mode 100644 index 0000000000000000000000000000000000000000..4e9607044ac36521e93e49bd4fc7191cc1de69d3 GIT binary patch literal 3324 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 WQ-zlvr%3#BU4XWRJ`Ag7AN?Phq$7R+ literal 0 HcmV?d00001 diff --git a/www/wupws_icons/38.png b/www/wupws_icons/38.png new file mode 100644 index 0000000000000000000000000000000000000000..73d8746fc369a9f315f5962db85db0d0b4db3de1 GIT binary patch literal 4621 zcmV+o67ubdP)Ov zK~#9!?43<)8^;yLhZH5*iXG9m0n*?v zQ(bRe;gHl}9#9f+T+H;qK}y3kAOP}E8I$+2xGWF=u?7Sl1Ogy7faGvlAOM1J3UW{? zu%cq`MZD{x!vvBIFrr$%~l^0MR{hR-$Cd?3kcrhjledfE~n&a=~G#FHn z9;DRsn+kj@!VD2&dC^?Y|7V1Osn)m!pgG0B=X>CQ0EjPDOx~LphNjAB14+jD_mG#m zZStoEOb~#0lE^n|CV#pu3{JOlir`3Fz`o3XV;Lq0K>Wt+rrt#uo=yl<^@R`wQx@+- z0K}IUp(pRM$)Bba8CY6p^3EZq1bo&890-6Yc>vKL%8QIU zJgx1X@_s@vtK*g&z$?P+0ElWYQng6d4P6xpu^Y(%l6DD4p1-_evrEeh%n$&Pky9`F zb(bA~Sw;I8aAYOUv4Yww0HQO@YB?F8WauR89Ws(|q{aD-JM4<^3V>(}vzij&iJN>R zs|pa|IhJ6UJ%!#d%j=B z$j@g^JPf9goC6RmBcUeGn-04zcX^ejkME)o&KSmeOL&c@$>)N>~9D8N_(VuH_Bpq0^AVkP)8Ls1NhHx)piiy4b258m#j^NPr0A@9urK z@*ZvzeBG*vFtFhQ3i5z+0*on5=VjE7(F3N!fkA^o)B)-85JnMWrRQ(-w7nOiaEb~g zOOjB~xvQ=Va|=tEgM!9`CNTtt4=Qui$!(lAz zJRU(?xFtYAG9OVunte1pTR4Q*D2F(lauasVp^5ld$%w#_4KWkx~>q18Cd zW>a@L53XOCmbk^VaF~iZbjvLG=J&zzAR4@pt}w@Rc{EY3{tj5M)eY`N ziU1@Wkmx8@qz=KnMPlB7_mF$Q4DzHAQCj7;s^OSL3pk2^Lt3Rl{&7(PMD^_Hix#03 z!&h^rgMMy{)0t z(ASR)kR|@p>2x+y!>R4biHSD>$gzRTP9thrX)8t=HicKHdR;miWhF{=jRR7mD(KT= zV?Q1p9sNQ3v-Xq8Wa9C5yUh*`4op9k8XjiDd`qQLYePdr_l8r$cN59P?TeF>e~T3$ zBBZJ?cjMQ!t{}^AX4qzokFr|gq2)WD8y!8xMn*^Pof;i|Cy_|}=HkW4<$(so=SsIP z=~|1N15#(d|K`sML3Adjy9n`s()da{POfWj2oH=vm z4!7G^g77HX0qI)r!|SXhcSK^X%X=XIh@j4#IlIk4d39pqLMgI<7`@^iLMn=5OU$Ch z$x5HzZ~kic!GkN?TPt538crt;_IICPZ0JHFkz|L52P}~|Sl-{?-5wnoS{@lW{Yf%4 z_V&jsE2mliXN`wE<|wDf#_l}vrOR`X1H@v@K_}G&6lK{DeDdk1zuny2d_KVv-`L*X z8nbF8G1CaYz^S1m+u7NHwMA6PWRhLDaN)oFA^05~A(wJML`X#jE<4FWOYC2g^%kqE zYg7EZU*kahsNHT~asvf4m`bP5y+1NC@;%j%CXRq_~ zRkY~=U;>b1xOMLQzj@;RH6=NY;&WT<`9C$$QY}@6 z*8K1S2ZdCd8ykNF9FVLwTFvLy*4CAD3+PDFGwfkeVBKqN)Nxp~`ny(Z|7YD+F@X+dYo_pYqLQ1v0wKd7ZtXCrq$Wdax zz5QL>PDn>C(&==*A8ikE$X@#lRke^N?d|P-E7E{$Y-~*9HbUCMtnIBanpGu|fKaW~ z-u~X>xQ&p`Fl(dLd^VDR9PIDEyw~aAE<(DMjU*tuoz8Q(cTfPzL=uq0gTtqB@1OuQ z6G=e!_BtqKheD~o14321G$kbj1tFRe)y@8WFEv?N`ClEk4GK`{$c0!Cgb)jY!l60< zfglS_Pfs@^1BjRxH;>x}g;lA5fuI(IoY*k#0}5KTPyr%VJvD%!35K!68PrcT`VNSI zq9CBaqBKyLRP7re91Ln-rr8tcaUY;T0Yu5PKO(n~owOQ)`l03N>FG@19z?)VMScN= zT%M*PP5OkxSY8cqKw(!Ya6q_<2&^=Mff=)mv>{|5WJv;*<@(&0KvuN4Bg5QI9^&kKQ3cK=dOWRcff?={y zn6=+L%3y`yqKKf7G4``6#@Ec;KhlhvX z?{qq6ac`gyB)HAD?1qOf&3>*!ne#ZI*4oNv`*W#D|Sf{mLX!13hW|oQw zbHD%gL?cf2fbp^?;h1B3mUC!E5RMFcSZYp7>Wz!*l8>8B0FaZ?F(LOY?ma3IycT8) zBQ@e?5D2l3y$7w9bzkvempB~R2zrk)`{H7(EgoK_;X&X%RH?-xxI_epBhSoNDlW&- zC;*5)K=RysG$IBEc{{7M{7P{&3WQjC!Yj%<3K4^Yu0frdH&WDic$I?(f%j0MTGZg^ z3ZbTK{>A`Ee5J~uqndXV`OB=;Z{pqqfW$bw$}n}y$<1-^(To@z6gpjZ@GJ~~AiVOc z8yt>m#NeQZgHs<(34p{~c}00gF@j0CS!ZBxa^8ruain4%US$=9S0#%A$9jlEu9C!W zk9BxOighzDH)=AHS2TVFCGFX;^@oVgz}rse9><<@gjjm=k0^y!SqV7cJ>m|C2(NUn zYf=c4_vp$y7ytnvK_usPS`6VBkc#9(VTp%V7~};&v}uW{>wHw@J&OG0s*403iYp*u zVAQzA#5^CS)+-JN{od;?&c6aQ;_X3nCg+7PjX@Wkfo?**3JCioYOHvU$+z|xdMQj3 zGlcRfI!LH+?ioZultpLOvO<)a6C=A$BIWs3$y-qG+7MlSO2$io5FyaFJOa?`q{eDt7AtCOtv$a@G6k(>h%Er95lwZyE|$<2`0 zqL1%|0En{iO8IPwisqy8QhbS?a{wae5&D>S*p;B`e!8RQ9DpbTL}%V%CbvUgTL`ZJ zh>q|o!{mCLb>tm(CFo_%#$H2m4nR~T=Q{EZN~*0|oxIFPMSv{3*uRKd4?t9eS32?z zrxJ9xa4?Ze&~cX}c3`P4~W;Rh;XC;#IssohDCW{u|M?f~>KF5_8rEm#sy~7O+MTmjUMgFdr3ikkBXRy7G>j61yTd03e|cuapjc>dHH0Lof({^gX5Q4+alsmCqW*H$? z{NN}s_w!L9yn+Xb1svoB-f{UcB$8d{h7gvhW^x=50PI^qd2b zSg7dna|o{hNG#-OJ}Qjr10WE*2LORw%_M^49Du+ZAUOvh&`EEwe*p#nle)yUse*1m00000NkvXXu0mjf D#2bxM literal 0 HcmV?d00001 diff --git a/www/wupws_icons/39.png b/www/wupws_icons/39.png new file mode 100644 index 0000000000000000000000000000000000000000..7fffd6ec21cb525208ce7394bd776e05da8ca880 GIT binary patch literal 2698 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 t;R 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 literal 0 HcmV?d00001 diff --git a/www/wupws_icons/42.png b/www/wupws_icons/42.png new file mode 100644 index 0000000000000000000000000000000000000000..0973cd620c9ec3b99bc0208678cef411abed21c3 GIT binary patch literal 4588 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 literal 0 HcmV?d00001 diff --git a/www/wupws_icons/46.png b/www/wupws_icons/46.png new file mode 100644 index 0000000000000000000000000000000000000000..058d68626b0db6ca3ae2ef6d2e0dc824a0d2ec04 GIT binary patch literal 5098 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 u+%$P43AQ!DN`L