Browse Source

adding hacs

master
Thomas 4 years ago
parent
commit
4a951a7871
Signed by: taschenbier
GPG Key ID: 306F574D34A9673B
  1. BIN
      .DS_Store
  2. 6
      configuration.yaml
  3. BIN
      custom_components/.DS_Store
  4. 165
      custom_components/hacs/.translations/da.json
  5. 168
      custom_components/hacs/.translations/de.json
  6. 99
      custom_components/hacs/.translations/el.json
  7. 168
      custom_components/hacs/.translations/en.json
  8. 99
      custom_components/hacs/.translations/es.json
  9. 167
      custom_components/hacs/.translations/fr.json
  10. 168
      custom_components/hacs/.translations/hu.json
  11. 159
      custom_components/hacs/.translations/it.json
  12. 166
      custom_components/hacs/.translations/nb.json
  13. 166
      custom_components/hacs/.translations/nl.json
  14. 121
      custom_components/hacs/.translations/nn.json
  15. 167
      custom_components/hacs/.translations/pl.json
  16. 96
      custom_components/hacs/.translations/pt-BR.json
  17. 99
      custom_components/hacs/.translations/ro.json
  18. 163
      custom_components/hacs/.translations/ru.json
  19. 167
      custom_components/hacs/.translations/sl.json
  20. 137
      custom_components/hacs/.translations/sv.json
  21. 168
      custom_components/hacs/.translations/zh-Hans.json
  22. 198
      custom_components/hacs/__init__.py
  23. 100
      custom_components/hacs/config_flow.py
  24. 51
      custom_components/hacs/configuration_schema.py
  25. 293
      custom_components/hacs/const.py
  26. 60
      custom_components/hacs/constrains.py
  27. 382
      custom_components/hacs/hacsbase/__init__.py
  28. 72
      custom_components/hacs/hacsbase/backup.py
  29. 58
      custom_components/hacs/hacsbase/configuration.py
  30. 10
      custom_components/hacs/hacsbase/const.py
  31. 143
      custom_components/hacs/hacsbase/data.py
  32. 45
      custom_components/hacs/hacsbase/exceptions.py
  33. 60
      custom_components/hacs/hacsbase/task_factory.py
  34. 1
      custom_components/hacs/handler/__init__.py
  35. 79
      custom_components/hacs/handler/download.py
  36. 29
      custom_components/hacs/handler/template.py
  37. 43
      custom_components/hacs/helpers/get_defaults.py
  38. 27
      custom_components/hacs/helpers/misc.py
  39. 61
      custom_components/hacs/http.py
  40. 24
      custom_components/hacs/manifest.json
  41. 6
      custom_components/hacs/repositories/__init__.py
  42. 87
      custom_components/hacs/repositories/appdaemon.py
  43. 171
      custom_components/hacs/repositories/integration.py
  44. 42
      custom_components/hacs/repositories/manifest.py
  45. 169
      custom_components/hacs/repositories/plugin.py
  46. 79
      custom_components/hacs/repositories/python_script.py
  47. 714
      custom_components/hacs/repositories/repository.py
  48. 79
      custom_components/hacs/repositories/theme.py
  49. 82
      custom_components/hacs/sensor.py
  50. 15
      custom_components/hacs/services.yaml
  51. 90
      custom_components/hacs/setup.py
  52. 19
      custom_components/hacs/store.py
  53. 362
      custom_components/hacs/ws_api_handlers.py
  54. 11
      lovelace/home.yaml

BIN
.DS_Store vendored

Binary file not shown.

6
configuration.yaml

@ -15,6 +15,12 @@ automation: !include automations.yaml @@ -15,6 +15,12 @@ automation: !include automations.yaml
script: !include scripts.yaml
scene: !include scenes.yaml
hacs:
sidepanel_title: Hacs Store
sidepanel_icon: mdi:store
token: 6bf2b1f782d1739eae8f3b52a5266bc259898e55
theme: true
frontend:
javascript_version: latest
themes: !include_dir_merge_named themes

BIN
custom_components/.DS_Store vendored

Binary file not shown.

165
custom_components/hacs/.translations/da.json

@ -0,0 +1,165 @@ @@ -0,0 +1,165 @@
{
"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 Adgang Token er ikke korrekt."
},
"step": {
"user": {
"data": {
"appdaemon": "Aktiver AppDaemon apps opdagelse & sporing",
"python_script": "Aktivér opdagelse og sporing af python_scripts",
"sidepanel_icon": "Side panel ikon",
"sidepanel_title": "Side panel 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?",
"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 afslutte.",
"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 release notes for dem alle inden du fortsætter.",
"yes": "Ja"
},
"options": {
"step": {
"user": {
"data": {
"country": "Filtrer med landekode.",
"debug": "Aktiver debug.",
"experimental": "Aktivér eksperimentelle funktioner",
"release_limit": "Antal udgivelser, der skal vises."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Denne integration er ikke indlæst i Home Assistant.",
"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": "Change log",
"downloads": "Downloads",
"flag_this": "Marker denne",
"frontend_version": "Frontend version",
"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",
"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",
"repository_configuration": "Konfiguration af repository",
"save": "Gem",
"table": "Tabel",
"table_view": "Tabel visning",
"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 et søgeudtryk ...",
"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",
"second": "sekund",
"seconds": "sekunder",
"year": "år",
"years": "år"
}
}

168
custom_components/hacs/.translations/de.json

@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
{
"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",
"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-Discovery & Tracking aktivieren",
"python_script": "Python Script-Discovery & Tracking aktivieren",
"sidepanel_icon": "Sidepanel Symbol",
"sidepanel_title": "Sidepanel Titel",
"theme": "Theme-Discovery & Tracking 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?",
"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.",
"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": {
"country": "Nach Ländercode filtern.",
"debug": "Debug aktivieren.",
"experimental": "Experimentelle Funktionen aktivieren",
"release_limit": "Anzahl anzuzeigender Releases."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Diese Integration ist in Home Assistant nicht geladen.",
"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",
"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",
"open_issue": "Problem melden",
"open_plugin": "Plugin öffnen",
"reinstall": "Neu installieren",
"repository": "Repository",
"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",
"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",
"second": "Sekunde",
"seconds": "Sekunden",
"year": "Jahr",
"years": "Jahre"
}
}

99
custom_components/hacs/.translations/el.json

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
{
"common": {
"appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Apps",
"background_task": "Τρέχει μια διεργασία στο παρασκήνιο, η σελίδα θα ανανεωθεί μόλις αυτό ολοκληρωθεί.",
"installed": "εγκατεστημένο",
"integration": "Ενσωμάτωση",
"integrations": "Ενσωματωμένα",
"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",
"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": {
"delete": "Είστε σίγουροι ότι θέλετε να διαγράψετε το \"{item}\";"
},
"options": {
"step": {
"user": {
"data": {
"country": "Κριτήριο με βάση τον κωδικό χώρας.",
"experimental": "Ενεργοποίση πειραματικών λειτουργιών",
"release_limit": "Αριθμός εκδόσεων που να παραθέτονται."
}
}
}
},
"repository": {
"authors": "Συγγραφείς",
"available": "Διαθέσιμο",
"back_to": "Πίσω στο",
"changelog": "Σημειώσεις των αλλαγών",
"flag_this": "Σημείωσε αυτό",
"hide": "Απόκρυψη",
"hide_beta": "Απόκριση του 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": "Αποθετήριο",
"show_beta": "Εμφάνιση του beta",
"uninstall": "Απεγκατάσταση",
"update_information": "Ενημέρωση πληροφοριών",
"upgrade": "Ενημέρωση"
},
"settings": {
"add_custom_repository": "ΠΡΟΣΘΕΣΤΕ ΕΝΑ ΕΙΔΙΚΟ ΑΠΟΘΕΤΗΡΙΟ",
"category": "Κατηγορία",
"custom_repositories": "ΕΙΔΙΚΑ ΑΠΟΘΕΤΗΡΙΑ",
"delete": "Διαγραφή",
"display": "Εμφάνιση",
"grid": "Πλέγμα",
"hacs_repo": "Αποθετήριο του HACS",
"reload_data": "Επαναφόρτωση δεδομένων",
"save": "Αποθήκευση",
"table": "Πίνακας",
"upgrade_all": "Αναβάθμιση όλων"
},
"store": {
"placeholder_search": "Παρακαλώ πληκτρολογήστε έναν όρο προς αναζήτηση..."
}
}

168
custom_components/hacs/.translations/en.json

@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
{
"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",
"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",
"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?",
"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.",
"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": {
"country": "Filter with country code.",
"debug": "Enable debug.",
"experimental": "Enable experimental features",
"release_limit": "Number of releases to show."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "This integration is not loaded in Home Assistant.",
"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",
"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)",
"open_issue": "Open issue",
"open_plugin": "Open plugin",
"reinstall": "Reinstall",
"repository": "Repository",
"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",
"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",
"second": "second",
"seconds": "seconds",
"year": "year",
"years": "years"
}
}

99
custom_components/hacs/.translations/es.json

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
{
"common": {
"appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Apps",
"background_task": "Ejecutando tareas en segundo plano. Se refrescará automaticamente esta página al finalizar.",
"installed": "instalado",
"integration": "Integración",
"integrations": "Integraciones",
"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",
"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 dirigete a: https:\/\/hacs.xyz\/docs\/configuration\/start\/",
"title": "HACS (Home Assistant Community Store)"
}
},
"title": "HACS (Home Assistant Community Store)"
},
"confirm": {
"delete": "¿Seguro que quieres eliminar \" {item} \"?"
},
"options": {
"step": {
"user": {
"data": {
"country": "Filtrar por el código de país.",
"experimental": "Habilitar funciones experimentales",
"release_limit": "Número de versiones a mostrar."
}
}
}
},
"repository": {
"authors": "Autores",
"available": "Disponible",
"back_to": "Volver a",
"changelog": "Changelog",
"flag_this": "Marcar esto",
"hide": "Ocultar",
"hide_beta": "Ocultar beta",
"install": "Instalar",
"installed": "Instalado",
"lovelace_copy_example": "Copiar el ejemplo al clipboard",
"lovelace_instruction": "Cuando agregue esto a su configuración de lovelace, use esto",
"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",
"show_beta": "Mostrar beta",
"uninstall": "Desinstalar",
"update_information": "Actualizar información",
"upgrade": "Actualizar"
},
"settings": {
"add_custom_repository": "AGREGAR CUSTOM REPOSITORY",
"category": "Categoría",
"custom_repositories": "CUSTOM REPOSITORIES",
"delete": "Eliminar",
"display": "Display",
"grid": "Grilla",
"hacs_repo": "HACS repo",
"reload_data": "Releer datos",
"save": "Grabar",
"table": "Tabla",
"upgrade_all": "Actualizar todo"
},
"store": {
"placeholder_search": "Por favor escriba una palabra clave de búsqueda..."
}
}

167
custom_components/hacs/.translations/fr.json

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
{
"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",
"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",
"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?",
"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.",
"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": {
"country": "Filtrer par code pays.",
"debug": "Activez le débogage.",
"experimental": "Activer les fonctionnalités expérimentales",
"release_limit": "Nombre de recensés à afficher."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Cette intégration n'est pas chargée dans Home Assistant.",
"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",
"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",
"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",
"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",
"second": "seconde",
"seconds": "secondes",
"year": "an",
"years": "ans"
}
}

168
custom_components/hacs/.translations/hu.json

@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
{
"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?",
"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.",
"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": {
"country": "Szűrés országkóddal.",
"debug": "Hibakeresés engedélyezése.",
"experimental": "Kísérleti funkciók engedélyezése",
"release_limit": "Megjelenítendő kiadások száma."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Ez az integráció nincs betöltve a Home Assistantban.",
"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ó",
"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ó",
"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",
"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": "nappal",
"days": "nappal",
"hour": "órával",
"hours": "órával",
"minute": "perccel",
"minutes": "perccel",
"month": "hónappal",
"months": "hónappal",
"one": "Egy",
"second": "másodperccel",
"seconds": "másodperccel",
"year": "évvel",
"years": "évvel"
}
}

159
custom_components/hacs/.translations/it.json

@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
{
"common": {
"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",
"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",
"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?",
"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.",
"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": {
"country": "Filtra con prefisso internazionale.",
"debug": "Abilita debug.",
"experimental": "Abilita funzionalità sperimentali",
"release_limit": "Numero di versioni da mostrare."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Questa integrazione non è caricata in Home Assistant.",
"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",
"flag_this": "Spunta questo",
"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": "Repository",
"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",
"reload_data": "Ricarica i dati",
"repository_configuration": "Configurazione delle repository",
"save": "Salva",
"table": "Tabella",
"table_view": "Vista tabella",
"unhide": "Mostra",
"upgrade_all": "Aggiorna tutto"
},
"store": {
"clear_new": "Ripulisci i nuovi repository",
"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": {
"day": "giorno",
"days": "giorni",
"hour": "ora",
"hours": "ore",
"minute": "minuto",
"minutes": "minuti",
"month": "mese",
"months": "mesi",
"second": "secondo",
"seconds": "secondi",
"year": "anno",
"years": "anni"
}
}

166
custom_components/hacs/.translations/nb.json

@ -0,0 +1,166 @@ @@ -0,0 +1,166 @@
{
"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": "Integrering",
"integrations": "Integrasjoner",
"manage": "manage",
"plugin": "Plugin",
"plugins": "Plugins",
"python_script": "Python-skript",
"python_scripts": "Python-skript",
"repositories": "Repositories",
"settings": "Innstillinger",
"theme": "Teama",
"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",
"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?",
"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.",
"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": {
"country": "Filtrer med landskode.",
"debug": "Aktiver debug",
"experimental": "Aktiver eksperimentelle funksjoner",
"release_limit": "Antall utgivelser som skal vises."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Integrasjonen er ikke lastet inn i Home Assistant.",
"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",
"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",
"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 '{kategori}'.",
"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",
"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",
"second": "sekund",
"seconds": "sekunder",
"year": "år",
"years": "år"
}
}

166
custom_components/hacs/.translations/nl.json

@ -0,0 +1,166 @@ @@ -0,0 +1,166 @@
{
"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.",
"disabled": "Uitgeschakeld",
"documentation": "Documentatie",
"hacs_is_disabled": "HACS is uitgeschakeld",
"installed": "geinstalleerd",
"integration": "Integratie",
"integrations": "Integraties",
"manage": "beheer",
"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",
"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?",
"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",
"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": {
"country": "Filter met land code.",
"debug": "Schakel debug in.",
"experimental": "Zet experimentele functies aan",
"release_limit": "Aantal releases om te laten zien."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Deze integratie wordt niet geladen in Home Assistant.",
"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",
"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",
"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",
"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",
"second": "seconde",
"seconds": "seconden",
"year": "jaar",
"years": "jaren"
}
}

121
custom_components/hacs/.translations/nn.json

@ -0,0 +1,121 @@ @@ -0,0 +1,121 @@
{
"common": {
"appdaemon": "AppDaemon",
"appdaemon_apps": "AppDeamon-appar",
"background_task": "Bakgrunnsoppgåve køyrer. Denne sida kjem til å laste seg omatt når ho er ferdig.",
"installed": "Installert",
"integration": "Integrasjon",
"integrations": "Integrasjonar",
"plugin": "Tillegg",
"plugins": "Tillegg",
"python_script": "Pythonskript",
"python_scripts": "Pythonskript",
"repositories": "Repositories",
"settings": "innstillingar",
"theme": "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": {
"user": {
"data": {
"appdaemon": "Aktiver AppDeamon-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"
},
"description": "Dersom du treng hjelp med konfigurasjonen, ta ein kik 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 til dette i Lovelace-ressursane dine?",
"continue": "Er du sikker på at du vil halde fram?",
"delete": "Er du sikker på at du vil slette \"{item}\"?",
"generic": "Er du sikker?",
"overwrite": "Ved å gjere dette kjem du til å overskrive. ",
"uninstall": "Er du sikker på at du vil avinstallere \"{item}\"?"
},
"options": {
"step": {
"user": {
"data": {
"country": "Filterer med landskode",
"experimental": "Aktiver ekspreimentelle funksjonar",
"release_limit": "Talet på utgivingar"
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Integrasjonen er ikkje lasta inn i Home Assistant.",
"not_loaded": "Ikkje lasta",
"plugin_not_loaded": "Tillegget er ikkje lagt til i Lovelace-ressursane dine.",
"restart": "Du må starte Home Assistant på nytt",
"restart_pending": "Omstart venter"
},
"repository": {
"add_to_lovelace": "Legg til i Lovelace",
"authors": "Forfatter(e)",
"available": "Tilgjengeleg",
"back_to": "Tilbake til",
"changelog": "Endre logg",
"flag_this": "Marker dette",
"hide": "Gøym",
"hide_beta": "Gøym beta",
"install": "Installer",
"installed": "Installert",
"lovelace_copy_example": "Kopier eksempelet til utklippsbreittet ditt",
"lovelace_instruction": "Når du legg til dette i Lovelace-konfigurasjonen din, bruk dette",
"lovelace_no_js_type": "Kunne ikkje slå fast typen til dette tilegget. Sjå i repositoryet.",
"newest": "nyaste",
"note_appdaemon": "du må framleis legge dette til i \"apps.yaml\"-fila di",
"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",
"open_issue": "Opne problem",
"open_plugin": "Opne tillegg",
"reinstall": "Installer på nytt",
"repository": "Repository",
"show_beta": "Vis beta",
"uninstall": "Avinstaller",
"update_information": "Oppdater informasjonen",
"upgrade": "Oppdater"
},
"settings": {
"add_custom_repository": "LEGG TIL EIN ANNAN REPOSITORY",
"category": "Kategori",
"compact_mode": "Kompaktmodus",
"custom_repositories": "VANLEG REPOSITORY",
"delete": "Slett",
"display": "Vis",
"grid": "rutenett",
"hacs_repo": "HACS repo",
"hidden_repositories": "gøymde repositories",
"reload_data": "Last om dataa",
"save": "Lagre",
"table": "Tavle",
"table_view": "Tabellvisning",
"unhide": "vis",
"upgrade_all": "Oppdater alle"
},
"store": {
"clear_new": "Fjern alle nye repositories",
"name": "Namn",
"new_repositories": "Ny repository",
"placeholder_search": "Ver vennleg og skriv inn ei søkefrase",
"sort": "Sorter",
"status": "Status"
}
}

167
custom_components/hacs/.translations/pl.json

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
{
"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",
"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",
"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?",
"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ć.",
"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": {
"country": "Filtruj według kodu kraju",
"debug": "Włącz debugowanie.",
"experimental": "Włącz funkcje eksperymentalne",
"release_limit": "Liczba wydań do wyświetlenia"
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Ta integracja nie jest załadowana do Home Assistant'a.",
"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": "Pliki do pobrania",
"flag_this": "Oflaguj",
"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",
"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",
"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": "Gwiazdy",
"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",
"second": "sekunda",
"seconds": "sekundy",
"year": "rok",
"years": "lata"
}
}

96
custom_components/hacs/.translations/pt-BR.json

@ -0,0 +1,96 @@ @@ -0,0 +1,96 @@
{
"common": {
"appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Apps",
"background_task": "Tarefa em segundo plano em execução, esta página será recarregada quando terminar.",
"installed": "instalado",
"integration": "Integração",
"integrations": "Integrações",
"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",
"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)"
},
"options": {
"step": {
"user": {
"data": {
"country": "Filtro pelo código do país.",
"experimental": "Ativar recursos experimentais",
"release_limit": "Número de lançamentos a serem exibidos."
}
}
}
},
"repository": {
"authors": "Autores",
"available": "Disponível",
"back_to": "Voltar para",
"changelog": "Changelog",
"flag_this": "Sinalizar isso",
"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",
"show_beta": "Mostrar beta",
"uninstall": "Desinstalar",
"update_information": "Atualizar informação",
"upgrade": "Melhorar"
},
"settings": {
"add_custom_repository": "ADICIONAR REPOSITÓRIO PERSONALIZADO",
"category": "Categoria",
"custom_repositories": "REPOSITÓRIOS PERSONALIZADOS",
"delete": "Deletar",
"display": "Display",
"grid": "Grade",
"hacs_repo": "HACS repo",
"reload_data": "Recarregar dados",
"save": "Salvar",
"table": "Tabela",
"upgrade_all": "Atualizar tudo"
},
"store": {
"placeholder_search": "Por favor insira um termo de pesquisa..."
}
}

99
custom_components/hacs/.translations/ro.json

@ -0,0 +1,99 @@ @@ -0,0 +1,99 @@
{
"common": {
"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.",
"installed": "instalat",
"integration": "Integrare",
"integrations": "Integrări",
"plugin": "Plugin",
"plugins": "Plugin-uri",
"python_script": "Script Python",
"python_scripts": "Scripturi Python",
"repositories": "Depozite",
"settings": "setări",
"theme": "Temă",
"themes": "Teme",
"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": {
"user": {
"data": {
"appdaemon": "Activați descoperirea și urmărirea aplicațiilor AppDaemon",
"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"
},
"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)"
}
},
"title": "HACS (Home Assistant Community Store)"
},
"confirm": {
"delete": "Sigur doriți să ștergeți „{item}”?"
},
"options": {
"step": {
"user": {
"data": {
"country": "Filtrează cu codul țării.",
"experimental": "Activați funcțiile experimentale",
"release_limit": "Număr de versiuni afișate."
}
}
}
},
"repository": {
"authors": "Autori",
"available": "Disponibil",
"back_to": "Înapoi la",
"changelog": "Jurnal modificari",
"flag_this": "Semnalizează",
"hide": "Ascunde",
"hide_beta": "Ascundere beta",
"install": "Instalează",
"installed": "Instalat",
"lovelace_copy_example": "Copiați exemplul în clipboard",
"lovelace_instruction": "Când adăugați acest lucru la configurația lovelace, folosiți acest",
"lovelace_no_js_type": "Nu s-a putut determina tipul acestui plugin, verificați \"repository\"",
"newest": "cele mai noi",
"note_appdaemon": "mai trebuie să o adăugați in fișierul „apps.yaml”",
"note_installed": "Când este instalat, acesta va fi localizat în",
"note_integration": "mai trebuie să o adăugați in fișierul „configuration.yaml”",
"note_plugin": "mai trebuie să o adăugați la configurația lovelace ('ui-lovelace.yaml' sau in editorul UI)",
"open_issue": "Deschidere problemă",
"open_plugin": "Deschide plugin",
"reinstall": "Reinstalare",
"repository": "Depozit",
"show_beta": "Afișare beta",
"uninstall": "Dezinstalare",
"update_information": "Actualizare informație",
"upgrade": "Actualizare"
},
"settings": {
"add_custom_repository": "ADĂUGARE DEPOZIT PERSONALIZAT",
"category": "Categorie",
"custom_repositories": "DEPOZITE PERSONALIZATE",
"delete": "Șterge",
"display": "Afişeaza",
"grid": "Grilă",
"hacs_repo": "HACS repo",
"reload_data": "Reîncărcați datele",
"save": "Salveaza",
"table": "Tabel",
"upgrade_all": "Actualizați toate"
},
"store": {
"placeholder_search": "Vă rugăm să introduceți un termen de căutare ..."
}
}

163
custom_components/hacs/.translations/ru.json

@ -0,0 +1,163 @@ @@ -0,0 +1,163 @@
{
"common": {
"appdaemon": "AppDaemon",
"appdaemon_apps": "Приложения AppDaemon",
"background_task": "Выполняется фоновая задача, страница перезагрузится по готовности.",
"check_log_file": "Проверьте логи для получения более подробной информации.",
"continue": "Продолжить",
"disabled": "Отключено",
"documentation": "Документация",
"hacs_is_disabled": "HACS отключен",
"installed": "установлено",
"integration": "Интеграция",
"integrations": "Интеграции",
"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",
"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, это займет некоторое время.",
"uninstall": "Вы уверены, что хотите удалить \"{item}\"?",
"yes": "Да"
},
"options": {
"step": {
"user": {
"data": {
"country": "Фильтр с кодом страны.",
"debug": "Включить отладку.",
"experimental": "Включить экспериментальные функции",
"release_limit": "Число доступных версий."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Эта интеграция не загружена в Home Assistant.",
"not_loaded": "Не загружено",
"plugin_not_loaded": "Этот плагин не добавлен к ресурсам Lovelace.",
"restart": "Вам нужно перезапустить Home Assistant.",
"restart_pending": "Ожидается перезапуск"
},
"repository": {
"add_to_lovelace": "Добавить в Lovelace",
"authors": "Авторы",
"available": "Доступно",
"back_to": "Назад к",
"changelog": "Изменения",
"downloads": "Загрузки",
"flag_this": "Пожаловаться",
"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": "Репозиторий",
"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": "Вы должны выбрать категорию",
"reload_data": "Перезагрузить",
"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": "Одна",
"second": "секунда",
"seconds": "секунд",
"year": "год",
"years": "лет"
}
}

167
custom_components/hacs/.translations/sl.json

@ -0,0 +1,167 @@ @@ -0,0 +1,167 @@
{
"common": {
"about": "O tem",
"appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Aplikacije",
"background_task": "v ozadju se izvaja naloga, ko se konča, se bo stran osvežila.",
"check_log_file": "Za več podrobnosti preverite datoteko dnevnika.",
"continue": "Nadaljuj",
"disabled": "Onemogočeno",
"documentation": "Dokumentacija",
"hacs_is_disabled": "HACS je onemogočen",
"installed": "Nameščeno",
"integration": "Integracija",
"integrations": "Integracije",
"manage": "upravljanje",
"plugin": "vtičnik",
"plugins": "Vtičniki",
"python_script": "Python skripta",
"python_scripts": "Python skripte",
"repositories": "Repozitoriji",
"settings": "nastavitve",
"theme": "Tema",
"themes": "Teme",
"version": "Različica"
},
"config": {
"abort": {
"single_instance_allowed": "Dovoljena je samo ena konfiguracija HACS."
},
"error": {
"auth": "Osebni dostopni žeton ni pravilen."
},
"step": {
"user": {
"data": {
"appdaemon": "Omogoči odkrivanje in sledenje aplikacij AppDaemon",
"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"
},
"description": "Če potrebujete pomoč pri konfiguraciji, poglejte tukaj: https:\/\/hacs.xyz\/docs\/configuration\/start\/",
"title": "HACS (Home Assistant Community Store)"
}
},
"title": "HACS (Home Assistant Community Store)"
},
"confirm": {
"add_to_lovelace": "Ali si prepričan, da želiš to dodati med svoje vire Lovelace?",
"bg_task": "Dejanje je onemogočeno, medtem ko se izvajajo opravila v ozadju.",
"cancel": "Prekliči",
"continue": "Ali ste prepričani, da želite nadaljevati?",
"delete": "Ali ste prepričani, da želite izbrisati \"{item}\"?",
"delete_installed": "'{item}' je nameščen, pred odstranitvijo ga morate odstraniti.",
"exist": "{item} že obstaja",
"generic": "Ali si prepričan?",
"no": "Ne",
"no_upgrades": "Ni nadgradenj",
"ok": "V redu",
"overwrite": "Če to storite, bo to prepisano.",
"reload_data": "Ponovno se naložijo podatki vseh shramb HA, to bo trajalo nekaj časa.",
"uninstall": "Ali ste prepričani, da želite odstraniti \"{item}\"?",
"upgrade_all": "To bo nadgradil vse te repozitorije, preden nadaljujete poskrbite, da ste prebrali vse opombe ob izdaji.",
"yes": "Da"
},
"options": {
"step": {
"user": {
"data": {
"country": "Filtrirajte s kodo države.",
"debug": "Omogoči odpravljanje napak.",
"experimental": "Omogočite poskusne funkcije",
"release_limit": "Število izdaj, ki jih želite prikazati."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Ta integracija ni naložena v programu Home Assistant.",
"not_loaded": "Ni naloženo",
"plugin_not_loaded": "Ta vtičnik ni dodan vašim virom Lovelace.",
"restart": "Znova morate zagnati Home Assistant.",
"restart_pending": "Vnovični zagon v čakanju"
},
"repository": {
"add_to_lovelace": "Dodaj v Lovelace",
"authors": "Avtorji",
"available": "Na voljo",
"back_to": "Nazaj na",
"changelog": "Dnevnik sprememb",
"downloads": "Prenosi",
"flag_this": "Označite to",
"hide": "Skrij",
"hide_beta": "Skrij različico beta",
"install": "Namestite",
"installed": "Nameščeno",
"lovelace_copy_example": "Kopirajte primer v odložišče",
"lovelace_instruction": "Ko ga boste dodajali v svojo lovelace konfiguracijo uporabite to:",
"lovelace_no_js_type": "Ni bilo mogoče določiti vrsto tega vtičnika, preverite repozitorij.",
"newest": "najnovejše",
"note_appdaemon": "Še vedno ga morate dodati v svojo 'apps.yaml' datoteko",
"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)",
"open_issue": "Odprite težavo",
"open_plugin": "Odprite vtičnik",
"reinstall": "Znova namestite",
"repository": "Repozitorij",
"show_beta": "Pokaži različico beta",
"uninstall": "Odstranite",
"update_information": "Posodobite podatke",
"upgrade": "Nadgradnja"
},
"settings": {
"add_custom_repository": "DODAJTE SVOJ REPOZITORIJ",
"adding_new_repo": "Dodajanje novega repozitorija '{repo}'",
"adding_new_repo_category": "S kategorijo '{category}'.",
"bg_task_custom": "Med izvajanjem nalog v ozadju so repozitoriji po meri skriti.",
"category": "Kategorija",
"compact_mode": "Kompaktni način",
"custom_repositories": "VAŠI REPOZITORIJI",
"delete": "Izbriši",
"display": "Prikaz",
"grid": "Mreža",
"hacs_repo": "HACS repo",
"hidden_repositories": "skriti repozitoriji",
"missing_category": "Izbrati moraš kategorijo",
"open_repository": "Odprite skladišče",
"reload_data": "Ponovno naloži podatke",
"repository_configuration": "Konfiguracija skladišč",
"save": "Shranite",
"table": "Tabela",
"table_view": "Pogled tabele",
"unhide": "Razkrij",
"upgrade_all": "Nadgradite vse"
},
"store": {
"ascending": "naraščajoče",
"clear_new": "Počisti vse nove repozitorije",
"descending": "padajoče",
"last_updated": "Nazadnje posodobljeno",
"name": "Ime",
"new_repositories": "Novi repozitoriji",
"pending_upgrades": "Nadgradnje na čakanju",
"placeholder_search": "Prosim vnesite iskalni izraz ...",
"sort": "razvrsti",
"stars": "Zvezd",
"status": "Stanje"
},
"time": {
"ago": "nazaj",
"day": "dan",
"days": "Dni",
"hour": "ura",
"hours": "Ur",
"minute": "minuta",
"minutes": "minut",
"month": "mesec",
"months": "mesecev",
"one": "Eno",
"second": "drugič",
"seconds": "sekund",
"year": "leto",
"years": "Let"
}
}

137
custom_components/hacs/.translations/sv.json

@ -0,0 +1,137 @@ @@ -0,0 +1,137 @@
{
"common": {
"appdaemon": "AppDaemon",
"appdaemon_apps": "Appdaemon Applikationer",
"background_task": "Bakgrundsjobb körs, denna sida kommer att laddas igen när det är klart.",
"installed": "installerad",
"integration": "Integration",
"integrations": "Integrationer",
"plugin": "Plugin",
"plugins": "Plugins",
"python_script": "Python skript",
"python_scripts": "Python skript",
"repositories": "Repositories",
"settings": "inställningar",
"theme": "Tema",
"themes": "Teman",
"version": "Version"
},
"config": {
"abort": {
"single_instance_allowed": "Endast en konfiguration kan användas i HACS."
},
"error": {
"auth": "Personal Access Token är inte korrekt."
},
"step": {
"user": {
"data": {
"appdaemon": "Upptäck och följ Appdaemon 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",
"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)"
}
},
"title": "HACS (Home Assistant Community Store)"
},
"confirm": {
"add_to_lovelace": "Är du säker på att du vill lägga till detta till dina Lovelace-resurser?",
"bg_task": "Åtgärden är inaktiverad medan aktiviteter i bakgrunden körs",
"cancel": "Avbryt",
"continue": "Är du säker på att du vill fortsätta?",
"delete": "Är du säker på att du vill ta bort \"{item}\"?",
"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?",
"no": "Nej",
"no_upgrades": "Inga uppgraderingar väntar",
"ok": "OK",
"overwrite": "Detta kommer att skriva över den.",
"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",
"yes": "Ja"
},
"options": {
"step": {
"user": {
"data": {
"country": "Filtrera på landskod.",
"debug": "Aktivera felsökning",
"experimental": "Använd experimentella funktioner",
"release_limit": "Antalet releaser som visas."
}
}
}
},
"repository_banner": {
"integration_not_loaded": "Denna integration inte laddats i Hem Assistent.",
"not_loaded": "Ej laddad",
"plugin_not_loaded": "Detta plugin är inte tillagt till din Lovelaceresurs.",
"restart": "Du måste starta om Home Assistant.",
"restart_pending": "Omstart väntar"
},
"repository": {
"add_to_lovelace": "Lägg till i Lovelace",
"authors": "Författare",
"available": "Tillgänglig",
"back_to": "Tillbaka till",
"changelog": "Ändringslogg",
"flag_this": "Flagga denna",
"hide": "Göm",
"hide_beta": "Göm betaversioner",
"install": "Installera",
"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",
"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)",
"open_issue": "Rapportera problem",
"open_plugin": "Öppna plugin",
"reinstall": "Ominstallera",
"repository": "Repository",
"show_beta": "Visa betaversioner",
"uninstall": "Avinstallera",
"update_information": "Uppdatera information",
"upgrade": "Uppdatera"
},
"settings": {
"add_custom_repository": "LÄGG TILL ETT REPOSITORY",
"adding_new_repo": "Lägger till nytt repository '{repo}'",
"adding_new_repo_category": "Med kategori '{kategori}'.",
"bg_task_custom": "Anpassade repositories är dolda under tiden aktiviteter i bakgrunden körs",
"category": "Kategori",
"compact_mode": "Kompakt läge",
"custom_repositories": "REPOSITORIES",
"delete": "Ta bort",
"display": "Visa",
"grid": "Rutnät",
"hacs_repo": "HACS repo",
"hidden_repositories": "dolda förråd",
"missing_category": "Du behöver välja en kategori",
"reload_data": "Ladda om data",
"save": "Spara",
"table": "Tabell",
"table_view": "Tabellvy",
"unhide": "ta fram",
"upgrade_all": "Uppgradera alla"
},
"store": {
"clear_new": "Rensa alla nya förvar",
"name": "Namn",
"new_repositories": "Nya förvar",
"pending_upgrades": "Väntande uppgraderingar",
"placeholder_search": "Skriv ett sökord...",
"sort": "sortera",
"stars": "Stjärnor",
"status": "Status"
}
}

168
custom_components/hacs/.translations/zh-Hans.json

@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
{
"common": {
"about": "关于",
"appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon应用",
"background_task": "后台任务正在运行,完成后将重新加载此页面。",
"check_log_file": "请查看日志文件以了解更多信息。",
"continue": "继续",
"disabled": "禁用",
"documentation": "文档",
"hacs_is_disabled": "HACS已禁用",
"installed": "已安装",
"integration": "自定义组件",
"integrations": "自定义组件",
"manage": "管理",
"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应用发现和跟踪",
"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": "你确定吗?",
"no": "不",
"no_upgrades": "暂无升级",
"ok": "确定",
"overwrite": "这样做会覆盖它。",
"reload_data": "这将重新加载HACS知道的所有仓库的数据,这需要一些时间才能完成。",
"uninstall": "您确定要卸载“ {item} ”吗?",
"upgrade_all": "这将升级所有这些仓库,请确保在继续之前已阅读所有仓库的发行说明。",
"yes": "是"
},
"options": {
"step": {
"user": {
"data": {
"country": "用国家代码过滤。",
"debug": "启用调试。",
"experimental": "启用实验功能",
"release_limit": "要显示的发行数量。"
}
}
}
},
"repository_banner": {
"integration_not_loaded": "此集成未加载到Home Assistant中。",
"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": "前端版本",
"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": "仓库",
"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": "重载数据",
"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": "一",
"second": "秒",
"seconds": "秒",
"year": "年",
"years": "年"
}
}

198
custom_components/hacs/__init__.py

@ -0,0 +1,198 @@ @@ -0,0 +1,198 @@
"""
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 .configuration_schema import hacs_base_config_schema, hacs_config_option_schema
from .const import DOMAIN, ELEMENT_TYPES, STARTUP, VERSION
from .constrains import check_constans
from .hacsbase import Hacs
from .hacsbase.configuration import Configuration
from .hacsbase.data import HacsData
from .setup import add_sensor, load_hacs_repository, setup_frontend, setup_extra_stores
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."""
if DOMAIN not in config:
return True
hass.data[DOMAIN] = config
Hacs.hass = hass
Hacs.configuration = Configuration.from_dict(
config[DOMAIN], config[DOMAIN].get("options")
)
Hacs.configuration.config_type = "yaml"
await startup_wrapper_for_yaml(Hacs)
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."""
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.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(Hacs)
if not startup_result:
Hacs.system.disabled = True
raise ConfigEntryNotReady
Hacs.system.disabled = False
return startup_result
async def startup_wrapper_for_yaml(hacs):
"""Startup wrapper for yaml config."""
startup_result = await hacs_startup(hacs)
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(hacs))
return
hacs.system.disabled = False
async def hacs_startup(hacs):
"""HACS startup tasks."""
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, hacs):
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(hacs)
# Set up sensor
await hacs.hass.async_add_executor_job(add_sensor, hacs)
# Load HACS
if not await load_hacs_repository(hacs):
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.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."
)
await hacs.hass.async_add_executor_job(setup_extra_stores, hacs)
# 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())
# Mischief managed!
return True
async def async_remove_entry(hass, config_entry):
"""Handle removal of an entry."""
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)

100
custom_components/hacs/config_flow.py

@ -0,0 +1,100 @@ @@ -0,0 +1,100 @@
"""Adds config flow for HACS."""
# pylint: disable=dangerous-default-value
import logging
import voluptuous as vol
from aiogithubapi import AIOGitHub, 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
_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)),
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)
client = AIOGitHub(token, session)
await client.get_repo("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."""
if user_input is not None:
return self.async_create_entry(title="", data=user_input)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema(
hacs_config_option_schema(self.config_entry.options)
),
)

51
custom_components/hacs/configuration_schema.py

@ -0,0 +1,51 @@ @@ -0,0 +1,51 @@
"""HACS Configuration Schemas."""
# pylint: disable=dangerous-default-value
import voluptuous as vol
from .const import LOCALE
# Configuration:
TOKEN = "token"
SIDEPANEL_TITLE = "sidepanel_title"
SIDEPANEL_ICON = "sidepanel_icon"
APPDAEMON = "appdaemon"
PYTHON_SCRIPT = "python_script"
THEME = "theme"
# Options:
COUNTRY = "country"
DEBUG = "debug"
RELEASE_LIMIT = "release_limit"
EXPERIMENTAL = "experimental"
def hacs_base_config_schema(config: dict = {}) -> dict:
"""Return a shcema configuration dict for HACS."""
if not config:
config = {
TOKEN: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
SIDEPANEL_TITLE: "Community",
SIDEPANEL_ICON: "mdi:alpha-c-box",
APPDAEMON: False,
PYTHON_SCRIPT: False,
THEME: False,
}
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(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}
return {
vol.Optional("country", default=options.get(COUNTRY)): vol.In(LOCALE),
vol.Optional(DEBUG, default=options.get(DEBUG)): bool,
vol.Optional(RELEASE_LIMIT, default=options.get(RELEASE_LIMIT)): int,
vol.Optional(EXPERIMENTAL, default=options.get(EXPERIMENTAL)): bool,
}

293
custom_components/hacs/const.py

@ -0,0 +1,293 @@ @@ -0,0 +1,293 @@
"""Constants for HACS"""
NAME_LONG = "HACS (Home Assistant Community Store)"
NAME_SHORT = "HACS"
VERSION = "0.20.4"
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",
]

60
custom_components/hacs/constrains.py

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
"""HACS Startup constrains."""
# pylint: disable=bad-continuation
import os
import json
from .const import CUSTOM_UPDATER_LOCATIONS, CUSTOM_UPDATER_WARNING
from .helpers.misc import version_left_higher_then_right
def check_constans(hacs):
"""Check HACS constrains."""
if not constrain_translations(hacs):
return False
if not constrain_custom_updater(hacs):
return False
if not constrain_version(hacs):
return False
return True
def constrain_custom_updater(hacs):
"""Check if custom_updater exist."""
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(hacs):
"""Check if the version is valid."""
with open(
f"{hacs.system.config_path}/custom_components/hacs/manifest.json", "r"
) as read:
manifest = json.loads(read.read())
# Check if HA is the required version.
installed = hacs.system.ha_version
minimum = manifest["homeassistant"]
if not version_left_higher_then_right(installed, minimum):
hacs.logger.critical(
f"You need HA version {manifest['homeassistant']} or newer to use this integration."
)
return False
return True
def constrain_translations(hacs):
"""Check if traslations exist."""
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

382
custom_components/hacs/hacsbase/__init__.py

@ -0,0 +1,382 @@ @@ -0,0 +1,382 @@
"""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 .task_factory import HacsTaskFactory
from ..const import ELEMENT_TYPES
from ..store import async_load_from_store, async_save_to_store
from ..helpers.get_defaults import get_default_repos_lists, get_default_repos_orgs
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 = []
blacklist = []
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
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.information.full_name == repository_full_name:
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."""
for repository in self.repositories:
if repository.information.full_name == repository_full_name:
return True
return False
@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.information.full_name)
async def register_repository(self, full_name, category, check=True):
"""Register a repository."""
from ..repositories.repository import RERPOSITORY_CLASSES
if full_name in self.common.skip:
if full_name != "hacs/integration":
self.logger.debug(f"Skipping {full_name}")
return
if category not in RERPOSITORY_CLASSES:
self.logger.error(f"{category} is not a valid repository category.")
return False
repository = RERPOSITORY_CLASSES[category](full_name)
if check:
try:
await repository.registration()
if self.system.status.new:
repository.status.new = False
if repository.validate.errors:
self.common.skip.append(repository.information.full_name)
if not self.system.status.startup:
self.logger.error(f"Validation for {full_name} failed.")
return repository.validate.errors
repository.logger.info("Registration complete")
except AIOGitHubException as exception:
self.logger.debug(self.github.ratelimits.remaining)
self.logger.debug(self.github.ratelimits.reset_utc)
self.common.skip.append(repository.information.full_name)
# if not self.system.status.startup:
if self.system.status.startup:
self.logger.error(
f"Validation for {full_name} failed with {exception}."
)
return exception
self.hass.bus.async_fire(
"hacs/repository",
{
"id": 1337,
"action": "registration",
"repository": repository.information.full_name,
"repository_id": repository.information.uid,
},
)
self.repositories.append(repository)
async def startup_tasks(self):
"""Tasks tha are started after startup."""
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)
await self.handle_critical_repositories_startup()
await self.handle_critical_repositories()
await self.load_known_repositories()
await self.clear_out_blacklisted_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:
self.common.blacklist.append(repository["repository"])
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)
# 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.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")
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.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_blacklisted_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_blacklisted_repositories(self):
"""Clear out blaclisted repositories."""
need_to_save = False
for repository in self.common.blacklist:
if self.is_known(repository):
repository = self.get_by_name(repository)
if repository.status.installed:
self.logger.warning(
f"You have {repository.information.full_name} installed with HACS "
+ "this repository has been blacklisted, please consider removing it."
)
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.github, 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.github, "blacklist"):
if item not in self.common.blacklist:
self.common.blacklist.append(item)
for category in repositories:
for repo in repositories[category]:
if repo in self.common.blacklist:
continue
if self.is_known(repo):
continue
self.factory.tasks.append(
self.factory.safe_register(self, repo, category)
)
await self.factory.execute()

72
custom_components/hacs/hacsbase/backup.py

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
"""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")

58
custom_components/hacs/hacsbase/configuration.py

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
"""HACS Configuration."""
import attr
from custom_components.hacs.hacsbase.exceptions import HacsUserScrewupException
@attr.s(auto_attribs=True)
class Configuration:
"""Configuration class."""
# Main configuration:
appdaemon_path: str = "appdaemon/apps/"
appdaemon: 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
@staticmethod
def from_dict(configuration: dict, options: dict):
"""Set attributes from dicts."""
if isinstance(options, bool) or isinstance(configuration.get("options"), bool):
raise HacsUserScrewupException("Configuration is not valid.")
if options is None:
options = {}
if not configuration:
raise HacsUserScrewupException("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

10
custom_components/hacs/hacsbase/const.py

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
"""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."

143
custom_components/hacs/hacsbase/data.py

@ -0,0 +1,143 @@ @@ -0,0 +1,143 @@
"""Data handler for HACS."""
from integrationhelper import Logger
from . import Hacs
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
class HacsData(Hacs):
"""HacsData class."""
def __init__(self):
"""Initialize."""
self.logger = Logger("hacs.data")
async def async_write(self):
"""Write content to the store files."""
if self.system.status.background_task or self.system.disabled:
return
self.logger.debug("Saving data")
# Hacs
await async_save_to_store(
self.hass,
"hacs",
{
"view": self.configuration.frontend_mode,
"compact": self.configuration.frontend_compact,
"onboarding_done": self.configuration.onboarding_done,
},
)
# Repositories
content = {}
for repository in self.repositories:
if repository.repository_manifest is not None:
repository_manifest = repository.repository_manifest.manifest
else:
repository_manifest = None
content[repository.information.uid] = {
"authors": repository.information.authors,
"category": repository.information.category,
"description": repository.information.description,
"downloads": repository.releases.last_release_object_downloads,
"full_name": repository.information.full_name,
"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.information.name,
"new": repository.status.new,
"repository_manifest": repository_manifest,
"selected_tag": repository.status.selected_tag,
"show_beta": repository.status.show_beta,
"stars": repository.information.stars,
"topics": repository.information.topics,
"version_installed": repository.versions.installed,
}
await async_save_to_store(self.hass, "repositories", content)
self.hass.bus.async_fire("hacs/repository", {})
self.hass.bus.fire("hacs/config", {})
async def restore(self):
"""Restore saved data."""
hacs = await async_load_from_store(self.hass, "hacs")
repositories = await async_load_from_store(self.hass, "repositories")
try:
if not hacs and not repositories:
# Assume new install
self.system.status.new = True
return True
self.logger.info("Restore started")
# Hacs
self.configuration.frontend_mode = hacs.get("view", "Grid")
self.configuration.frontend_compact = hacs.get("compact", False)
self.configuration.onboarding_done = hacs.get("onboarding_done", False)
# Repositories
for entry in repositories:
repo = repositories[entry]
if repo["full_name"] == "hacs/integration":
# Skip the old repo location
continue
if not self.is_known(repo["full_name"]):
await self.register_repository(
repo["full_name"], repo["category"], False
)
repository = self.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.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.information.authors = repository_data.get("authors", [])
repository.information.description = repository_data.get("description")
repository.information.name = repository_data.get("name")
repository.releases.last_release_object_downloads = repository_data.get("downloads")
repository.information.last_updated = repository_data.get("last_updated")
repository.information.topics = repository_data.get("topics", [])
repository.information.stars = 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

45
custom_components/hacs/hacsbase/exceptions.py

@ -0,0 +1,45 @@ @@ -0,0 +1,45 @@
"""Custom Exceptions."""
class HacsBaseException(Exception):
"""Super basic."""
class HacsUserScrewupException(HacsBaseException):
"""Raise this when the user does something they should not do."""
class HacsNotSoBasicException(HacsBaseException):
"""Not that basic."""
class HacsDataFileMissing(HacsBaseException):
"""Raise this storage datafile is missing."""
class HacsDataNotExpected(HacsBaseException):
"""Raise this when data returned from storage is not ok."""
class HacsRepositoryInfo(HacsBaseException):
"""Raise this when repository info is missing/wrong."""
class HacsRequirement(HacsBaseException):
"""Raise this when repository is missing a requirement."""
class HacsMissingManifest(HacsBaseException):
"""Raise this when manifest is missing."""
def __init__(self, message="The manifest file is missing in the repository."):
super().__init__(message)
self.message = message
class HacsBlacklistException(HacsBaseException):
"""Raise this when the repository is currently in the blacklist."""
def __init__(self, message="The repository is currently in the blacklist."):
super().__init__(message)
self.message = message

60
custom_components/hacs/hacsbase/task_factory.py

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
# pylint: disable=missing-docstring,invalid-name
import logging
import time
from datetime import timedelta
import asyncio
from aiogithubapi import AIOGitHubException
max_concurrent_tasks = asyncio.Semaphore(15)
sleeper = 5
logger = logging.getLogger("hacs.factory")
class HacsTaskFactory:
def __init__(self):
self.tasks = []
async def execute(self):
if not self.tasks:
logger.debug("No tasks to execute")
return
logger.info("Prosessing %s tasks", len(self.tasks))
start = time.time()
await asyncio.gather(*self.tasks)
logger.info(
"Task prosessing of %s tasks completed in %s seconds",
len(self.tasks),
timedelta(seconds=round(time.time() - start)).seconds,
)
self.tasks = []
async def safe_common_update(self, repository):
async with max_concurrent_tasks:
try:
await repository.common_update()
except AIOGitHubException as exception:
logger.error(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 as exception:
logger.error(exception)
# Due to GitHub ratelimits we need to sleep a bit
await asyncio.sleep(sleeper)
async def safe_register(self, hacs, repo, category):
async with max_concurrent_tasks:
try:
await hacs.register_repository(repo, category)
except AIOGitHubException as exception:
logger.error(exception)
# Due to GitHub ratelimits we need to sleep a bit
await asyncio.sleep(sleeper)

1
custom_components/hacs/handler/__init__.py

@ -0,0 +1 @@ @@ -0,0 +1 @@
"""Initialize handlers."""

79
custom_components/hacs/handler/download.py

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
"""Download."""
import os
import gzip
import shutil
import aiofiles
import async_timeout
from integrationhelper import Logger
import backoff
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from ..hacsbase.exceptions import HacsNotSoBasicException
@backoff.on_exception(backoff.expo, Exception, max_tries=5)
async def async_download_file(hass, url):
"""
Download files, and return the content.
"""
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=hass.loop):
request = await async_get_clientsession(hass).get(url)
# Make sure that we got a valid result
if request.status == 200:
result = await request.read()
else:
raise HacsNotSoBasicException(
"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)
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)

29
custom_components/hacs/handler/template.py

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
"""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

43
custom_components/hacs/helpers/get_defaults.py

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
"""Helpers to get default repositories."""
import json
from aiogithubapi import AIOGitHub, AIOGitHubException
from integrationhelper import Logger
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(github: type(AIOGitHub), default: str) -> dict:
"""Gets repositories from default list."""
repositories = []
logger = Logger("hacs")
try:
repo = await github.get_repo("hacs/default")
content = await repo.get_contents(default)
repositories = json.loads(content.content)
except AIOGitHubException as exception:
logger.error(exception)
return repositories

27
custom_components/hacs/helpers/misc.py

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
"""Helper functions: misc"""
import semantic_version
def get_repository_name(
hacs_manifest, repository_name: str, category: str = None, manifest: dict = None
) -> str:
"""Return the name of the repository for use in the frontend."""
if hacs_manifest.name is not None:
return hacs_manifest.name
if category == "integration":
if manifest:
if "name" in manifest:
return manifest["name"]
return repository_name.replace("-", " ").replace("_", " ").title()
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):
return False
if new == old:
return True
return semantic_version.Version.coerce(new) > semantic_version.Version.coerce(old)

61
custom_components/hacs/http.py

@ -0,0 +1,61 @@ @@ -0,0 +1,61 @@
"""HACS http endpoints."""
import os
from homeassistant.components.http import HomeAssistantView
from aiohttp import web
from hacs_frontend import locate_gz, locate_debug_gz
from .hacsbase import Hacs
class HacsFrontend(HomeAssistantView, Hacs):
"""Base View Class for HACS."""
requires_auth = False
name = "hacs_frontend"
url = r"/hacs_frontend/{requested_file:.+}"
async def get(self, request, requested_file): # pylint: disable=unused-argument
"""Handle HACS Web requests."""
if self.configuration.debug:
servefile = await self.hass.async_add_executor_job(locate_debug_gz)
self.logger.debug("Serving DEBUG frontend")
else:
servefile = await self.hass.async_add_executor_job(locate_gz)
if os.path.exists(servefile):
return web.FileResponse(servefile)
return web.Response(status=404)
class HacsPluginView(HomeAssistantView, Hacs):
"""Serve plugins."""
requires_auth = False
name = "hacs_plugin"
url = r"/community_plugin/{requested_file:.+}"
async def get(self, request, requested_file): # pylint: disable=unused-argument
"""Serve plugins for lovelace."""
try:
file = f"{self.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):
self.logger.debug("Serving {} from {}".format(requested_file, file))
response = web.FileResponse(file)
response.headers["Cache-Control"] = "max-age=0, must-revalidate"
return response
else:
self.logger.error(f"Tried to serve up '{file}' but it does not exist")
except Exception as error: # pylint: disable=broad-except
self.logger.debug(
"there was an issue trying to serve {} - {}".format(
requested_file, error
)
)
return web.Response(status=404)

24
custom_components/hacs/manifest.json

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
{
"codeowners": [
"@ludeeus"
],
"config_flow": true,
"dependencies": [
"websocket_api",
"frontend",
"persistent_notification",
"lovelace"
],
"documentation": "https://hacs.xyz",
"domain": "hacs",
"homeassistant": "0.98.0",
"name": "HACS (Home Assistant Community Store)",
"requirements": [
"aiofiles==0.4.0",
"aiogithubapi==0.4.3",
"backoff==1.10.0",
"hacs_frontend>=20191227161046",
"integrationhelper==0.2.2",
"semantic_version==2.8.4"
]
}

6
custom_components/hacs/repositories/__init__.py

@ -0,0 +1,6 @@ @@ -0,0 +1,6 @@
"""Initialize repositories."""
from .theme import HacsTheme
from .integration import HacsIntegration
from .python_script import HacsPythonScript
from .appdaemon import HacsAppdaemon
from .plugin import HacsPlugin

87
custom_components/hacs/repositories/appdaemon.py

@ -0,0 +1,87 @@ @@ -0,0 +1,87 @@
"""Class for appdaemon apps in HACS."""
from .repository import HacsRepository, register_repository_class
@register_repository_class
class HacsAppdaemon(HacsRepository):
"""Appdaemon apps in HACS."""
category = "appdaemon"
def __init__(self, full_name):
"""Initialize."""
super().__init__()
self.information.full_name = full_name
self.information.category = self.category
self.content.path.local = self.localpath
self.content.path.remote = "apps"
@property
def localpath(self):
"""Return localpath."""
return f"{self.system.config_path}/appdaemon/apps/{self.information.name}"
async def validate_repository(self):
"""Validate."""
await self.common_validate()
# Custom step 1: Validate content.
addir = await self.repository_object.get_contents("apps", self.ref)
if not isinstance(addir, list):
self.validate.errors.append("Repostitory structure not compliant")
self.content.path.remote = addir[0].path
self.information.name = addir[0].name
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
# Handle potential errors
if self.validate.errors:
for error in self.validate.errors:
if not self.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.github.ratelimits.remaining == 0:
return
await self.common_update()
# Get appdaemon objects.
if self.repository_manifest:
if self.repository_manifest.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.information.name = addir[0].name
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
# Set local path
self.content.path.local = self.localpath

171
custom_components/hacs/repositories/integration.py

@ -0,0 +1,171 @@ @@ -0,0 +1,171 @@
"""Class for integrations in HACS."""
import json
from aiogithubapi import AIOGitHubException
from homeassistant.loader import async_get_custom_components
from .repository import HacsRepository, register_repository_class
from ..hacsbase.exceptions import HacsRequirement
@register_repository_class
class HacsIntegration(HacsRepository):
"""Integrations in HACS."""
category = "integration"
def __init__(self, full_name):
"""Initialize."""
super().__init__()
self.information.full_name = full_name
self.information.category = self.category
self.domain = None
self.content.path.remote = "custom_components"
self.content.path.local = self.localpath
@property
def localpath(self):
"""Return localpath."""
return f"{self.system.config_path}/custom_components/{self.domain}"
@property
def config_flow(self):
"""Return bool if integration has config_flow."""
if self.manifest:
if self.information.full_name == "hacs/integration":
return False
return self.manifest.get("config_flow", False)
return False
async def validate_repository(self):
"""Validate."""
await self.common_validate()
# Attach repository
if self.repository_object is None:
self.repository_object = await self.github.get_repo(
self.information.full_name
)
# Custom step 1: Validate content.
if self.repository_manifest:
if self.repository_manifest.content_in_root:
self.content.path.remote = ""
if self.content.path.remote == "custom_components":
ccdir = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
if not isinstance(ccdir, list):
self.validate.errors.append("Repostitory structure not compliant")
for item in ccdir or []:
if item.type == "dir":
self.content.path.remote = item.path
break
if self.repository_manifest.zip_release:
self.content.objects = self.releases.last_release_object.assets
else:
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
self.content.files = []
for filename in self.content.objects or []:
self.content.files.append(filename.name)
if not await self.get_manifest():
self.validate.errors.append("Missing manifest file.")
# Handle potential errors
if self.validate.errors:
for error in self.validate.errors:
if not self.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()
# Get the content of the manifest file.
await self.get_manifest()
# Set local path
self.content.path.local = self.localpath
async def update_repository(self):
"""Update."""
if self.github.ratelimits.remaining == 0:
return
await self.common_update()
# Get integration objects.
if self.repository_manifest:
if self.repository_manifest.content_in_root:
self.content.path.remote = ""
if self.content.path.remote == "custom_components":
ccdir = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
if not isinstance(ccdir, list):
self.validate.errors.append("Repostitory structure not compliant")
self.content.path.remote = ccdir[0].path
try:
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
except AIOGitHubException:
return
self.content.files = []
if isinstance(self.content.objects, list):
for filename in self.content.objects or []:
self.content.files.append(filename.name)
await self.get_manifest()
# 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.hass.data["custom_components"]
await async_get_custom_components(self.hass)
async def get_manifest(self):
"""Get info from the manifest file."""
manifest_path = f"{self.content.path.remote}/manifest.json"
try:
manifest = await self.repository_object.get_contents(
manifest_path, self.ref
)
manifest = json.loads(manifest.content)
except Exception: # pylint: disable=broad-except
return False
if manifest:
try:
self.manifest = manifest
self.information.authors = manifest["codeowners"]
self.domain = manifest["domain"]
self.information.name = manifest["name"]
self.information.homeassistant_version = manifest.get("homeassistant")
# Set local path
self.content.path.local = self.localpath
return True
except KeyError as exception:
raise HacsRequirement(
f"Missing expected key {exception} in 'manifest.json'"
)
return False

42
custom_components/hacs/repositories/manifest.py

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
"""
Manifest handling of a repository.
https://hacs.xyz/docs/publish/start#hacsjson
"""
from typing import List
import attr
from custom_components.hacs.hacsbase.exceptions import HacsRepositoryInfo
@attr.s(auto_attribs=True)
class HacsManifest:
"""HacsManifest class."""
name: str = None
content_in_root: bool = False
zip_release: bool = False
filename: str = None
manifest: dict = {}
hacs: str = None
hide_default_branch: bool = False
domains: List[str] = []
country: List[str] = []
homeassistant: str = None
persistent_directory: str = None
iot_class: str = None
render_readme: bool = False
@staticmethod
def from_dict(manifest: dict):
"""Set attributes from dicts."""
if manifest is None:
raise HacsRepositoryInfo("Missing manifest data")
manifest_data = HacsManifest()
manifest_data.manifest = manifest
for key in manifest:
setattr(manifest_data, key, manifest[key])
return manifest_data

169
custom_components/hacs/repositories/plugin.py

@ -0,0 +1,169 @@ @@ -0,0 +1,169 @@
"""Class for plugins in HACS."""
import json
from aiogithubapi import AIOGitHubException
from .repository import HacsRepository, register_repository_class
@register_repository_class
class HacsPlugin(HacsRepository):
"""Plugins in HACS."""
category = "plugin"
def __init__(self, full_name):
"""Initialize."""
super().__init__()
self.information.full_name = full_name
self.information.category = self.category
self.information.file_name = None
self.information.javascript_type = None
self.content.path.local = (
f"{self.system.config_path}/www/community/{full_name.split('/')[-1]}"
)
async def validate_repository(self):
"""Validate."""
# Run common validation steps.
await self.common_validate()
# Custom step 1: Validate content.
await self.get_plugin_location()
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
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
# Handle potential errors
if self.validate.errors:
for error in self.validate.errors:
if not self.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.github.ratelimits.remaining == 0:
return
# Run common update steps.
await self.common_update()
# Get plugin objects.
await self.get_plugin_location()
# 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
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
async def get_plugin_location(self):
"""Get plugin location."""
if self.content.path.remote is not None:
return
possible_locations = ["dist", "release", ""]
if self.repository_manifest:
if self.repository_manifest.content_in_root:
possible_locations = [""]
for location in possible_locations:
if self.content.path.remote is not None:
continue
try:
objects = []
files = []
if location != "release":
try:
objects = await self.repository_object.get_contents(
location, self.ref
)
except AIOGitHubException:
continue
else:
await self.get_releases()
if self.releases.releases:
if self.releases.last_release_object.assets is not None:
objects = self.releases.last_release_object.assets
for item in objects:
if item.name.endswith(".js"):
files.append(item.name)
# Handler for plug requirement 3
valid_filenames = [
f"{self.information.name.replace('lovelace-', '')}.js",
f"{self.information.name}.js",
f"{self.information.name}.umd.js",
f"{self.information.name}-bundle.js",
]
if self.repository_manifest:
if self.repository_manifest.filename:
valid_filenames.append(self.repository_manifest.filename)
for name in valid_filenames:
if name in files:
# YES! We got it!
self.information.file_name = name
self.content.path.remote = location
self.content.objects = objects
self.content.files = files
break
except SystemError:
pass
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.information.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

79
custom_components/hacs/repositories/python_script.py

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
"""Class for python_scripts in HACS."""
from .repository import HacsRepository, register_repository_class
@register_repository_class
class HacsPythonScript(HacsRepository):
"""python_scripts in HACS."""
category = "python_script"
def __init__(self, full_name):
"""Initialize."""
super().__init__()
self.information.full_name = full_name
self.information.category = self.category
self.content.path.remote = "python_scripts"
self.content.path.local = f"{self.system.config_path}/python_scripts"
self.content.single = True
async def validate_repository(self):
"""Validate."""
# Run common validation steps.
await self.common_validate()
# Custom step 1: Validate content.
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
if not isinstance(self.content.objects, list):
self.validate.errors.append("Repostitory structure not compliant")
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
# Handle potential errors
if self.validate.errors:
for error in self.validate.errors:
if not self.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
self.information.name = self.content.objects[0].name.replace(".py", "")
async def update_repository(self): # lgtm[py/similar-function]
"""Update."""
if self.github.ratelimits.remaining == 0:
return
# Run common update steps.
await self.common_update()
# Get python_script objects.
if self.repository_manifest:
if self.repository_manifest.content_in_root:
self.content.path.remote = ""
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
# Update name
self.information.name = self.content.objects[0].name.replace(".py", "")
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)

714
custom_components/hacs/repositories/repository.py

@ -0,0 +1,714 @@ @@ -0,0 +1,714 @@
"""Repository."""
# pylint: disable=broad-except, bad-continuation, no-member
import pathlib
import json
import os
import tempfile
import zipfile
from integrationhelper import Validate, Logger
from aiogithubapi import AIOGitHubException
from .manifest import HacsManifest
from ..helpers.misc import get_repository_name
from ..hacsbase import Hacs
from ..hacsbase.backup import Backup
from ..handler.download import async_download_file, async_save_file
from ..helpers.misc import version_left_higher_then_right
RERPOSITORY_CLASSES = {}
def register_repository_class(cls):
"""Register class."""
RERPOSITORY_CLASSES[cls.category] = cls
return cls
class RepositoryVersions:
"""Versions."""
available = None
available_commit = None
installed = None
installed_commit = None
class RepositoryStatus:
"""Repository status."""
hide = False
installed = False
last_updated = None
new = True
selected_tag = None
show_beta = False
track = True
updated_info = False
first_install = True
class RepositoryInformation:
"""RepositoryInformation."""
additional_info = None
authors = []
category = None
default_branch = None
description = ""
state = None
full_name = None
file_name = None
javascript_type = None
homeassistant_version = None
last_updated = None
uid = None
stars = 0
info = None
name = None
topics = []
class RepositoryReleases:
"""RepositoyReleases."""
last_release = None
last_release_object = None
last_release_object_downloads = None
published_tags = []
objects = []
releases = False
class RepositoryPath:
"""RepositoryPath."""
local = None
remote = None
class RepositoryContent:
"""RepositoryContent."""
path = None
files = []
objects = []
single = False
class HacsRepository(Hacs):
"""HacsRepository."""
def __init__(self):
"""Set up HacsRepository."""
self.content = RepositoryContent()
self.content.path = RepositoryPath()
self.information = RepositoryInformation()
self.repository_object = None
self.status = RepositoryStatus()
self.state = None
self.manifest = {}
self.repository_manifest = HacsManifest.from_dict({})
self.validate = Validate()
self.releases = RepositoryReleases()
self.versions = RepositoryVersions()
self.pending_restart = False
self.logger = None
@property
def pending_upgrade(self):
"""Return pending upgrade."""
if self.status.installed:
if self.status.selected_tag is not None:
if self.status.selected_tag == self.information.default_branch:
if self.versions.installed_commit != self.versions.available_commit:
return True
return False
if self.display_installed_version != self.display_available_version:
return True
return False
@property
def ref(self):
"""Return the ref."""
if self.status.selected_tag is not None:
if self.status.selected_tag == self.information.default_branch:
return self.information.default_branch
return "tags/{}".format(self.status.selected_tag)
if self.releases.releases:
return "tags/{}".format(self.versions.available)
return self.information.default_branch
@property
def custom(self):
"""Return flag if the repository is custom."""
if self.information.full_name.split("/")[0] in [
"custom-components",
"custom-cards",
]:
return False
if self.information.full_name in self.common.default:
return False
if self.information.full_name == "hacs/integration":
return False
return True
@property
def can_install(self):
"""Return bool if repository can be installed."""
target = None
if self.information.homeassistant_version is not None:
target = self.information.homeassistant_version
if self.repository_manifest is not None:
if self.repository_manifest.homeassistant is not None:
target = self.repository_manifest.homeassistant
if target is not None:
if self.releases.releases:
if not version_left_higher_then_right(self.system.ha_version, target):
return False
return True
@property
def display_name(self):
"""Return display name."""
return get_repository_name(
self.repository_manifest,
self.information.name,
self.information.category,
self.manifest,
)
@property
def display_status(self):
"""Return display_status."""
if self.status.new:
status = "new"
elif self.pending_restart:
status = "pending-restart"
elif self.pending_upgrade:
status = "pending-upgrade"
elif self.status.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.versions.installed is not None:
installed = self.versions.installed
else:
if self.versions.installed_commit is not None:
installed = self.versions.installed_commit
else:
installed = ""
return installed
@property
def display_available_version(self):
"""Return display_authors"""
if self.versions.available is not None:
available = self.versions.available
else:
if self.versions.available_commit is not None:
available = self.versions.available_commit
else:
available = ""
return available
@property
def display_version_or_commit(self):
"""Does the repositoriy use releases or commits?"""
if self.releases.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):
"""Common validation steps of the repository."""
# Attach helpers
self.validate.errors = []
self.logger = Logger(
f"hacs.repository.{self.information.category}.{self.information.full_name}"
)
# Step 1: Make sure the repository exist.
self.logger.debug("Checking repository.")
try:
self.repository_object = await self.github.get_repo(
self.information.full_name
)
except Exception as exception: # Gotta Catch 'Em All
if not self.system.status.startup:
self.logger.error(exception)
self.validate.errors.append("Repository does not exist.")
return
# Step 2: Make sure the repository is not archived.
if self.repository_object.archived:
self.validate.errors.append("Repository is archived.")
return
# Step 3: Make sure the repository is not in the blacklist.
if self.information.full_name in self.common.blacklist:
self.validate.errors.append("Repository is in the blacklist.")
return
# Step 4: default branch
self.information.default_branch = self.repository_object.default_branch
# Step 5: Get releases.
await self.get_releases()
# Step 6: Get the content of hacs.json
await self.get_repository_manifest_content()
# Set repository name
self.information.name = self.information.full_name.split("/")[1]
async def common_registration(self):
"""Common registration steps of the repository."""
# Attach logger
if self.logger is None:
self.logger = Logger(
f"hacs.repository.{self.information.category}.{self.information.full_name}"
)
# Attach repository
if self.repository_object is None:
self.repository_object = await self.github.get_repo(
self.information.full_name
)
# Set id
self.information.uid = str(self.repository_object.id)
# Set topics
self.information.topics = self.repository_object.topics
# Set stargazers_count
self.information.stars = self.repository_object.attributes.get(
"stargazers_count", 0
)
# Set description
if self.repository_object.description:
self.information.description = self.repository_object.description
async def common_update(self):
"""Common information update steps of the repository."""
# Attach logger
if self.logger is None:
self.logger = Logger(
f"hacs.repository.{self.information.category}.{self.information.full_name}"
)
self.logger.debug("Getting repository information")
# Attach repository
self.repository_object = await self.github.get_repo(self.information.full_name)
# Update description
if self.repository_object.description:
self.information.description = self.repository_object.description
# Set stargazers_count
self.information.stars = self.repository_object.attributes.get(
"stargazers_count", 0
)
# Update default branch
self.information.default_branch = self.repository_object.default_branch
# Update last updaeted
self.information.last_updated = self.repository_object.attributes.get(
"pushed_at", 0
)
# Update topics
self.information.topics = self.repository_object.topics
# Update last available commit
await self.repository_object.set_last_commit()
self.versions.available_commit = self.repository_object.last_commit
# Get the content of hacs.json
await self.get_repository_manifest_content()
# Update "info.md"
await self.get_info_md_content()
# Update releases
await self.get_releases()
async def install(self):
"""Common installation steps of the repository."""
self.validate.errors = []
persistent_directory = None
await self.update_repository()
if self.repository_manifest:
if self.repository_manifest.persistent_directory:
if os.path.exists(
f"{self.content.path.local}/{self.repository_manifest.persistent_directory}"
):
persistent_directory = Backup(
f"{self.content.path.local}/{self.repository_manifest.persistent_directory}",
tempfile.gettempdir() + "/hacs_persistent_directory/",
)
persistent_directory.create()
if self.status.installed and not self.content.single:
backup = Backup(self.content.path.local)
backup.create()
if self.repository_manifest.zip_release:
validate = await self.download_zip(self.validate)
else:
validate = await self.download_content(
self.validate,
self.content.path.remote,
self.content.path.local,
self.ref,
)
if validate.errors:
for error in validate.errors:
self.logger.error(error)
if self.status.installed and not self.content.single:
backup.restore()
if self.status.installed and not self.content.single:
backup.cleanup()
if persistent_directory is not None:
persistent_directory.restore()
persistent_directory.cleanup()
if validate.success:
if self.information.full_name not in self.common.installed:
if self.information.full_name == "hacs/integration":
self.common.installed.append(self.information.full_name)
self.status.installed = True
self.versions.installed_commit = self.versions.available_commit
if self.status.selected_tag is not None:
self.versions.installed = self.status.selected_tag
else:
self.versions.installed = self.versions.available
if self.information.category == "integration":
if (
self.config_flow
and self.information.full_name != "hacs/integration"
):
await self.reload_custom_components()
self.pending_restart = True
elif self.information.category == "theme":
try:
await self.hass.services.async_call("frontend", "reload_themes", {})
except Exception: # pylint: disable=broad-except
pass
self.hass.bus.async_fire(
"hacs/repository",
{
"id": 1337,
"action": "install",
"repository": self.information.full_name,
},
)
async def download_zip(self, validate):
"""Download ZIP archive from repository release."""
try:
contents = False
for release in self.releases.objects:
self.logger.info(f"ref: {self.ref} --- tag: {release.tag_name}")
if release.tag_name == self.ref.split("/")[1]:
contents = release.assets
if not contents:
return validate
for content in contents or []:
filecontent = await async_download_file(self.hass, content.download_url)
if filecontent is None:
validate.errors.append(f"[{content.name}] was not downloaded.")
continue
result = await async_save_file(
f"{tempfile.gettempdir()}/{self.repository_manifest.filename}",
filecontent,
)
with zipfile.ZipFile(
f"{tempfile.gettempdir()}/{self.repository_manifest.filename}", "r"
) as zip_file:
zip_file.extractall(self.content.path.local)
if result:
self.logger.info(f"download of {content.name} complete")
continue
validate.errors.append(f"[{content.name}] was not downloaded.")
except Exception:
validate.errors.append(f"Download was not complete.")
return validate
async def download_content(self, validate, directory_path, local_directory, ref):
"""Download the content of a directory."""
try:
# Get content
contents = []
if self.releases.releases:
for release in self.releases.objects:
if self.status.selected_tag == release.tag_name:
contents = release.assets
if not contents:
if self.content.single:
contents = self.content.objects
else:
contents = await self.repository_object.get_contents(
directory_path, self.ref
)
for content in contents:
if content.type == "dir" and (
self.repository_manifest.content_in_root
or self.content.path.remote != ""
):
await self.download_content(
validate, content.path, local_directory, ref
)
continue
if self.information.category == "plugin":
if not content.name.endswith(".js"):
if self.content.path.remote != "dist":
continue
self.logger.debug(f"Downloading {content.name}")
filecontent = await async_download_file(self.hass, content.download_url)
if filecontent is None:
validate.errors.append(f"[{content.name}] was not downloaded.")
continue
# Save the content of the file.
if self.content.single:
local_directory = self.content.path.local
else:
_content_path = content.path
if not self.repository_manifest.content_in_root:
_content_path = _content_path.replace(
f"{self.content.path.remote}/", ""
)
local_directory = f"{self.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}"
result = await async_save_file(local_file_path, filecontent)
if result:
self.logger.info(f"download of {content.name} complete")
continue
validate.errors.append(f"[{content.name}] was not downloaded.")
except Exception:
validate.errors.append(f"Download was not complete.")
return validate
async def get_repository_manifest_content(self):
"""Get the content of the hacs.json file."""
try:
manifest = await self.repository_object.get_contents("hacs.json", self.ref)
self.repository_manifest = HacsManifest.from_dict(
json.loads(manifest.content)
)
except (AIOGitHubException, Exception): # Gotta Catch 'Em All
pass
async def get_info_md_content(self):
"""Get the content of info.md"""
from ..handler.template import render_template
info = None
info_files = ["info", "info.md"]
if self.repository_manifest is not None:
if self.repository_manifest.render_readme:
info_files = ["readme", "readme.md"]
try:
root = await self.repository_object.get_contents("", self.ref)
for file in root:
if file.name.lower() in info_files:
info = await self.repository_object.get_contents(
file.name, self.ref
)
break
if info is None:
self.information.additional_info = ""
else:
info = info.content.replace("<svg", "<disabled").replace(
"</svg", "</disabled"
)
self.information.additional_info = render_template(info, self)
except (AIOGitHubException, Exception):
self.information.additional_info = ""
async def get_releases(self):
"""Get repository releases."""
if self.status.show_beta:
self.releases.objects = await self.repository_object.get_releases(
prerelease=True, returnlimit=self.configuration.release_limit
)
else:
self.releases.objects = await self.repository_object.get_releases(
prerelease=False, returnlimit=self.configuration.release_limit
)
if not self.releases.objects:
return
self.releases.releases = True
self.releases.published_tags = []
for release in self.releases.objects:
self.releases.published_tags.append(release.tag_name)
self.releases.last_release_object = self.releases.objects[0]
if self.status.selected_tag is not None:
if self.status.selected_tag != self.information.default_branch:
for release in self.releases.objects:
if release.tag_name == self.status.selected_tag:
self.releases.last_release_object = release
break
if self.releases.last_release_object.assets:
self.releases.last_release_object_downloads = self.releases.last_release_object.assets[
0
].attributes.get(
"download_count"
)
self.versions.available = self.releases.objects[0].tag_name
def remove(self):
"""Run remove tasks."""
# Attach logger
if self.logger is None:
self.logger = Logger(
f"hacs.repository.{self.information.category}.{self.information.full_name}"
)
self.logger.info("Starting removal")
if self.information.uid in self.common.installed:
self.common.installed.remove(self.information.uid)
for repository in self.repositories:
if repository.information.uid == self.information.uid:
self.repositories.remove(repository)
async def uninstall(self):
"""Run uninstall tasks."""
# Attach logger
if self.logger is None:
self.logger = Logger(
f"hacs.repository.{self.information.category}.{self.information.full_name}"
)
self.logger.info("Uninstalling")
await self.remove_local_directory()
self.status.installed = False
if self.information.category == "integration":
if self.config_flow:
await self.reload_custom_components()
else:
self.pending_restart = True
elif self.information.category == "theme":
try:
await self.hass.services.async_call("frontend", "reload_themes", {})
except Exception: # pylint: disable=broad-except
pass
if self.information.full_name in self.common.installed:
self.common.installed.remove(self.information.full_name)
self.versions.installed = None
self.versions.installed_commit = None
self.hass.bus.async_fire(
"hacs/repository",
{
"id": 1337,
"action": "uninstall",
"repository": self.information.full_name,
},
)
async def remove_local_directory(self):
"""Check the local directory."""
import shutil
from asyncio import sleep
try:
if self.information.category == "python_script":
local_path = "{}/{}.py".format(
self.content.path.local, self.information.name
)
elif self.information.category == "theme":
local_path = "{}/{}.yaml".format(
self.content.path.local, self.information.name
)
else:
local_path = self.content.path.local
if os.path.exists(local_path):
self.logger.debug(f"Removing {local_path}")
if self.information.category in ["python_script", "theme"]:
os.remove(local_path)
else:
shutil.rmtree(local_path)
while os.path.exists(local_path):
await sleep(1)
except Exception as exception:
self.logger.debug(f"Removing {local_path} failed with {exception}")
return

79
custom_components/hacs/repositories/theme.py

@ -0,0 +1,79 @@ @@ -0,0 +1,79 @@
"""Class for themes in HACS."""
from .repository import HacsRepository, register_repository_class
@register_repository_class
class HacsTheme(HacsRepository):
"""Themes in HACS."""
category = "theme"
def __init__(self, full_name):
"""Initialize."""
super().__init__()
self.information.full_name = full_name
self.information.category = self.category
self.content.path.remote = "themes"
self.content.path.local = f"{self.system.config_path}/themes"
self.content.single = True
async def validate_repository(self):
"""Validate."""
# Run common validation steps.
await self.common_validate()
# Custom step 1: Validate content.
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
if not isinstance(self.content.objects, list):
self.validate.errors.append("Repostitory structure not compliant")
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
# Handle potential errors
if self.validate.errors:
for error in self.validate.errors:
if not self.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
self.information.name = self.content.objects[0].name.replace(".yaml", "")
async def update_repository(self): # lgtm[py/similar-function]
"""Update."""
if self.github.ratelimits.remaining == 0:
return
# Run common update steps.
await self.common_update()
# Get theme objects.
if self.repository_manifest:
if self.repository_manifest.content_in_root:
self.content.path.remote = ""
self.content.objects = await self.repository_object.get_contents(
self.content.path.remote, self.ref
)
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)
# Update name
self.information.file_name = self.content.objects[0].name
self.information.name = self.content.objects[0].name.replace(".yaml", "")
self.content.files = []
for filename in self.content.objects:
self.content.files.append(filename.name)

82
custom_components/hacs/sensor.py

@ -0,0 +1,82 @@ @@ -0,0 +1,82 @@
"""Sensor platform for HACS."""
# pylint: disable=unused-argument
from integrationhelper import Logger
from homeassistant.helpers.entity import Entity
from .hacsbase import Hacs as hacs
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 HACSSensor(Entity):
"""HACS Sensor class."""
def __init__(self):
"""Initialize."""
self._state = None
self.logger = Logger("hacs.sensor")
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.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 "mdi:package"
@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.information.full_name,
"display_name": repository.display_name,
"installed version": repository.display_installed_version,
"available version": repository.display_available_version,
}
)
return {"repositories": data}

15
custom_components/hacs/services.yaml

@ -0,0 +1,15 @@ @@ -0,0 +1,15 @@
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'

90
custom_components/hacs/setup.py

@ -0,0 +1,90 @@ @@ -0,0 +1,90 @@
"""Setup functions for HACS."""
# pylint: disable=bad-continuation
from aiogithubapi import AIOGitHubAuthentication, AIOGitHubException, AIOGitHubRatelimit
from homeassistant.helpers import discovery
from hacs_frontend.version import VERSION as FE_VERSION
from .const import VERSION, DOMAIN
from .http import HacsPluginView, HacsFrontend
from .ws_api_handlers import setup_ws_api
async def load_hacs_repository(hacs):
"""Load HACS repositroy."""
try:
repository = hacs().get_by_name("hacs/integration")
if repository is None:
await hacs().register_repository("hacs/integration", "integration")
repository = hacs().get_by_name("hacs/integration")
if repository is None:
raise AIOGitHubException("Unknown error")
repository.status.installed = True
repository.versions.installed = VERSION
repository.status.new = False
hacs.repo = repository.repository_object
hacs.data_repo = await hacs().github.get_repo("hacs/default")
except (
AIOGitHubException,
AIOGitHubRatelimit,
AIOGitHubAuthentication,
) as exception:
hacs.logger.critical(f"[{exception}] - Could not load HACS!")
return False
return True
def setup_extra_stores(hacs):
"""Set up extra stores in HACS if enabled in Home Assistant."""
if "python_script" in hacs.hass.config.components:
hacs.common.categories.append("python_script")
if hacs.hass.services.services.get("frontend", {}).get("reload_themes") is not None:
hacs.common.categories.append("theme")
def add_sensor(hacs):
"""Add sensor."""
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(hacs):
"""Configure the HACS frontend elements."""
hacs.hass.http.register_view(HacsPluginView())
hacs.frontend.version_running = FE_VERSION
# Add to sidepanel
hacs.hass.http.register_view(HacsFrontend())
custom_panel_config = {
"name": "hacs-frontend",
"embed_iframe": False,
"trust_external": False,
"js_url": f"/hacs_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,
)
await setup_ws_api(hacs.hass)

19
custom_components/hacs/store.py

@ -0,0 +1,19 @@ @@ -0,0 +1,19 @@
"""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)

362
custom_components/hacs/ws_api_handlers.py

@ -0,0 +1,362 @@ @@ -0,0 +1,362 @@
"""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 import Hacs
from .store import async_load_from_store, async_save_to_store
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."""
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.information.category:
if repo.status.new:
Hacs().logger.debug(
f"Clearing new flag from '{repo.information.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."""
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["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."""
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."""
repositories = Hacs().repositories
content = []
for repo in repositories:
if repo.information.category in Hacs().common.categories:
data = {
"additional_info": repo.information.additional_info,
"authors": repo.information.authors,
"available_version": repo.display_available_version,
"beta": repo.status.show_beta,
"can_install": repo.can_install,
"category": repo.information.category,
"country": repo.repository_manifest.country,
"custom": repo.custom,
"default_branch": repo.information.default_branch,
"description": repo.information.description,
"domain": repo.manifest.get("domain"),
"downloads": repo.releases.last_release_object_downloads,
"file_name": repo.information.file_name,
"full_name": repo.information.full_name,
"hide": repo.status.hide,
"hide_default_branch": repo.repository_manifest.hide_default_branch,
"homeassistant": repo.repository_manifest.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.information.stars,
"state": repo.state,
"status_description": repo.display_status_description,
"status": repo.display_status,
"topics": repo.information.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."""
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.information.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.information.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."""
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:
await Hacs().register_repository(repo_id, data.lower())
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",
{"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.information.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))

11
lovelace/home.yaml

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
cards:
- type: vertical-stack
cards:
- type: custom:weather-card
entity: weather.home
details: false
title: Home
path: home
panel: false
icon: mdi:home-assistant
Loading…
Cancel
Save