#743 - Trying to fix this.

This commit is contained in:
ccostan 2020-05-21 18:48:00 -04:00
parent cf3764e3e4
commit 5c7334bc05
42 changed files with 863 additions and 513 deletions

8
.gitignore vendored
View File

@ -18,6 +18,9 @@ home-assistant_v2.db
*.sqlite *.sqlite
deps deps
__pycache__ __pycache__
hacs
alexa_media
custom_components
tts tts
secrets.yaml secrets.yaml
secrets secrets
@ -30,6 +33,5 @@ nest.conf
ipchange.yaml ipchange.yaml
ip_bans.yaml ip_bans.yaml
production_auth.json production_auth.json
hacs
alexa_media
custom_components

View File

@ -1 +1 @@
0.110.0 0.110.1

View File

@ -2,25 +2,35 @@
"common": { "common": {
"about": "Om", "about": "Om",
"appdaemon_apps": "AppDaemon-apps", "appdaemon_apps": "AppDaemon-apps",
"appdaemon_plural": "AppDaemon-apps",
"background_task": "Baggrundsopgave kører. Denne side vil genindlæses automatisk.", "background_task": "Baggrundsopgave kører. Denne side vil genindlæses automatisk.",
"check_log_file": "Tjek din logfil for flere detaljer.", "check_log_file": "Tjek din logfil for flere detaljer.",
"continue": "Fortsæt", "continue": "Fortsæt",
"disabled": "Deaktiveret", "disabled": "Deaktiveret",
"documentation": "Dokumentation", "documentation": "Dokumentation",
"element": "element",
"hacs_is_disabled": "HACS er deaktiveret", "hacs_is_disabled": "HACS er deaktiveret",
"installed": "installeret", "installed": "installeret",
"integration": "Integration", "integration": "Integration",
"integration_plural": "Integrationer",
"integrations": "Integrationer", "integrations": "Integrationer",
"lovelace": "Lovelace",
"lovelace_element": "Lovelace-element",
"lovelace_elements": "Lovelace-elementer",
"manage": "administrer", "manage": "administrer",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "NetDaemon-apps", "netdaemon_apps": "NetDaemon-apps",
"plugin": "Plugin", "netdaemon_plural": "NetDaemon-apps",
"plugins": "Plugins", "plugin": "Lovelace",
"plugin_plural": "Lovelace-elementer",
"plugins": "Lovelace-elementer",
"python_script": "Python-script", "python_script": "Python-script",
"python_script_plural": "Python-scripts",
"python_scripts": "Python-scripts", "python_scripts": "Python-scripts",
"repositories": "Repositories", "repositories": "Repositories",
"settings": "indstillinger", "settings": "indstillinger",
"theme": "Tema", "theme": "Tema",
"theme_plural": "Temaer",
"themes": "Temaer", "themes": "Temaer",
"version": "Version" "version": "Version"
}, },
@ -90,7 +100,7 @@
"integration_not_loaded": "Denne integration er ikke indlæst i Home Assistant.", "integration_not_loaded": "Denne integration er ikke indlæst i Home Assistant.",
"no_restart_required": "Ingen genstart påkrævet", "no_restart_required": "Ingen genstart påkrævet",
"not_loaded": "Ikke indlæst", "not_loaded": "Ikke indlæst",
"plugin_not_loaded": "Dette plugin er ikke føjet til dine Lovelace-ressourcer.", "plugin_not_loaded": "Dette element er ikke føjet til dine Lovelace-ressourcer.",
"restart": "Du skal genstarte Home Assistant.", "restart": "Du skal genstarte Home Assistant.",
"restart_pending": "Afventer genstart" "restart_pending": "Afventer genstart"
}, },
@ -111,7 +121,7 @@
"installed": "Installeret", "installed": "Installeret",
"lovelace_copy_example": "Kopiér eksemplet til din Udklipsholder", "lovelace_copy_example": "Kopiér eksemplet til din Udklipsholder",
"lovelace_instruction": "Tilføj dette til din lovelace-konfiguration", "lovelace_instruction": "Tilføj dette til din lovelace-konfiguration",
"lovelace_no_js_type": "Kunne ikke afgøre typen af dette plugin, tjek venligst repository.", "lovelace_no_js_type": "Kunne ikke afgøre typen af dette element, tjek venligst repository'et.",
"newest": "nyeste", "newest": "nyeste",
"note_appdaemon": "Du skal stadig føje den til filen 'apps.yaml'", "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_installed": "Når det er installeret, vil dette være placeret i",
@ -119,7 +129,7 @@
"note_plugin": "du skal stadig tilføje det til din lovelace-konfiguration ('ui-lovelace.yaml' eller Tekstbaseret redigering)", "note_plugin": "du skal stadig tilføje det til din lovelace-konfiguration ('ui-lovelace.yaml' eller Tekstbaseret redigering)",
"note_plugin_post_107": "du skal stadig tilføje det til din lovelace-konfiguration ('configuration.yaml' eller ressourceeditoren '\/config\/lovelace\/resources')", "note_plugin_post_107": "du skal stadig tilføje det til din lovelace-konfiguration ('configuration.yaml' eller ressourceeditoren '\/config\/lovelace\/resources')",
"open_issue": "Opret issue", "open_issue": "Opret issue",
"open_plugin": "Åbn plugin", "open_plugin": "Åbn element",
"reinstall": "Geninstaller", "reinstall": "Geninstaller",
"repository": "Repository", "repository": "Repository",
"restart_home_assistant": "Genstart Home Assistant", "restart_home_assistant": "Genstart Home Assistant",
@ -128,6 +138,25 @@
"update_information": "Opdater oplysninger", "update_information": "Opdater oplysninger",
"upgrade": "Opdater" "upgrade": "Opdater"
}, },
"sections": {
"about": {
"description": "Vis information om HACS",
"title": "Om"
},
"automation": {
"description": "Det er her, du finder python_scripts, AppDaemon-apps og NetDaemon-apps",
"title": "Automatisering"
},
"frontend": {
"description": "Det er her, du finder temaer, brugerdefinerede kort og andre elementer til lovelace",
"title": "Frontend"
},
"integrations": {
"description": "Det er her, du finder brugerdefinerede integrationer (custom_components)",
"title": "Integrationer"
},
"pending_repository_upgrade": "Du kører version {installed}, version {available} er tilgængelig"
},
"settings": { "settings": {
"add_custom_repository": "TILFØJ ET BRUGERDEFINERET REPOSITORY", "add_custom_repository": "TILFØJ ET BRUGERDEFINERET REPOSITORY",
"adding_new_repo": "Tilføjer nyt repository '{repo}'", "adding_new_repo": "Tilføjer nyt repository '{repo}'",

View File

@ -3,25 +3,35 @@
"about": "Über", "about": "Über",
"appdaemon": "AppDaemon", "appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Apps", "appdaemon_apps": "AppDaemon Apps",
"appdaemon_plural": "AppDaemon Apps",
"background_task": "Hintergrundprozess läuft. Diese Seite lädt neu, sobald dieser fertig ist.", "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.", "check_log_file": "Überprüfe die Logdatei für weitere Informationen.",
"continue": "Fortfahren", "continue": "Fortfahren",
"disabled": "Deaktiviert", "disabled": "Deaktiviert",
"documentation": "Dokumentation", "documentation": "Dokumentation",
"element": "Element",
"hacs_is_disabled": "HACS ist deaktiviert", "hacs_is_disabled": "HACS ist deaktiviert",
"installed": "Installiert", "installed": "Installiert",
"integration": "Integration", "integration": "Integration",
"integration_plural": "Integrationen",
"integrations": "Integrationen", "integrations": "Integrationen",
"lovelace": "Lovelace",
"lovelace_element": "Lovelace-Element",
"lovelace_elements": "Lovelace-Elemente",
"manage": "verwalten", "manage": "verwalten",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "NetDaemon Apps", "netdaemon_apps": "NetDaemon Apps",
"plugin": "Plugin", "netdaemon_plural": "NetDaemon Apps",
"plugins": "Plugins", "plugin": "Lovelace",
"plugin_plural": "Lovelace-Elemente",
"plugins": "Lovelace-Elemente",
"python_script": "Python Skript", "python_script": "Python Skript",
"python_script_plural": "Python Skripte",
"python_scripts": "Python Skripte", "python_scripts": "Python Skripte",
"repositories": "Repositories", "repositories": "Repositories",
"settings": "Einstellungen", "settings": "Einstellungen",
"theme": "Theme", "theme": "Theme",
"theme_plural": "Themes",
"themes": "Themes", "themes": "Themes",
"version": "Version" "version": "Version"
}, },
@ -93,7 +103,7 @@
"integration_not_loaded": "Diese Integration ist in Home Assistant nicht geladen.", "integration_not_loaded": "Diese Integration ist in Home Assistant nicht geladen.",
"no_restart_required": "Kein Neustart erforderlich", "no_restart_required": "Kein Neustart erforderlich",
"not_loaded": "Nicht geladen", "not_loaded": "Nicht geladen",
"plugin_not_loaded": "Dieses Plugin wurde deinen Lovelace-Ressourcen nicht hinzugefügt.", "plugin_not_loaded": "Dieses Element wird nicht zu Ihren Lovelace-Ressourcen hinzugefügt.",
"restart": "Du musst Home Assistant neu starten.", "restart": "Du musst Home Assistant neu starten.",
"restart_pending": "Neustart ausstehend" "restart_pending": "Neustart ausstehend"
}, },
@ -114,7 +124,7 @@
"installed": "Installiert", "installed": "Installiert",
"lovelace_copy_example": "Beispiel in die Zwischenablage kopieren", "lovelace_copy_example": "Beispiel in die Zwischenablage kopieren",
"lovelace_instruction": "Zum Hinzufügen zu deinen Lovelace-Einstellungen, benutze Folgendes", "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.", "lovelace_no_js_type": "Der Typ dieses Elements konnte nicht ermittelt werden. Überprüfen Sie das Repository.",
"newest": "neueste", "newest": "neueste",
"note_appdaemon": "du musst es dann noch in die Datei 'apps.yaml' hinzufügen", "note_appdaemon": "du musst es dann noch in die Datei 'apps.yaml' hinzufügen",
"note_installed": "Wird installiert nach", "note_installed": "Wird installiert nach",
@ -122,7 +132,7 @@
"note_plugin": "du musst es dann noch in deine Lovelace-Einstellungen ('ui-lovelace.yaml' oder im Raw-Konfigurationseditor) hinzufügen", "note_plugin": "du musst es dann noch in deine Lovelace-Einstellungen ('ui-lovelace.yaml' oder im Raw-Konfigurationseditor) hinzufügen",
"note_plugin_post_107": "Du musst es noch zu deiner Lovelace-Konfiguration hinzufügen ('configuration.yaml' oder der Ressourceneditor '\/config\/lovelace\/resources')", "note_plugin_post_107": "Du musst es noch zu deiner Lovelace-Konfiguration hinzufügen ('configuration.yaml' oder der Ressourceneditor '\/config\/lovelace\/resources')",
"open_issue": "Problem melden", "open_issue": "Problem melden",
"open_plugin": "Plugin öffnen", "open_plugin": "Element öffnen",
"reinstall": "Neu installieren", "reinstall": "Neu installieren",
"repository": "Repository", "repository": "Repository",
"restart_home_assistant": "Home Assistant neu starten", "restart_home_assistant": "Home Assistant neu starten",
@ -131,6 +141,25 @@
"update_information": "Aktualisierungsinformationen", "update_information": "Aktualisierungsinformationen",
"upgrade": "Aktualisieren" "upgrade": "Aktualisieren"
}, },
"sections": {
"about": {
"description": "Informationen zu HACS anzeigen",
"title": "Über"
},
"automation": {
"description": "Hier finden Sie python_scripts, AppDaemon-Apps und NetDaemon-Apps",
"title": "Automatisierung"
},
"frontend": {
"description": "Hier finden Sie Themen, individuelle Karten und andere Elemente für Lovelace",
"title": "Frontend"
},
"integrations": {
"description": "Hier finden Sie benutzerdefinierte Integrationen (custom_components)",
"title": "Integrationen"
},
"pending_repository_upgrade": "Sie verwenden Version {installed}, Version {available} ist verfügbar"
},
"settings": { "settings": {
"add_custom_repository": "BENUTZERDEFINIERTES REPOSITORY HINZUFÜGEN", "add_custom_repository": "BENUTZERDEFINIERTES REPOSITORY HINZUFÜGEN",
"adding_new_repo": "Hinzufügen eines neuen Repository '{repo}'", "adding_new_repo": "Hinzufügen eines neuen Repository '{repo}'",

View File

@ -1,28 +1,43 @@
{ {
"common": { "common": {
"about": "About", "about": "About",
"add": "add",
"appdaemon": "AppDaemon", "appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Apps", "appdaemon_apps": "AppDaemon Apps",
"appdaemon_plural": "AppDaemon Apps",
"background_task": "Background task running, this page will reload when it's done.", "background_task": "Background task running, this page will reload when it's done.",
"check_log_file": "Check your log file for more details.", "check_log_file": "Check your log file for more details.",
"continue": "Continue", "continue": "Continue",
"disabled": "Disabled", "disabled": "Disabled",
"documentation": "Documentation", "documentation": "Documentation",
"element": "element",
"hacs_is_disabled": "HACS is disabled", "hacs_is_disabled": "HACS is disabled",
"install": "Install",
"installed": "installed", "installed": "installed",
"integration": "Integration", "integration": "Integration",
"integration_plural": "Integrations",
"integrations": "Integrations", "integrations": "Integrations",
"lovelace": "Lovelace",
"lovelace_element": "Lovelace element",
"lovelace_elements": "Lovelace elements",
"manage": "manage", "manage": "manage",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "NetDaemon Apps", "netdaemon_apps": "NetDaemon Apps",
"netdaemon_plural": "NetDaemon Apps",
"plugin": "Plugin", "plugin": "Plugin",
"plugin_plural": "Lovelace elements",
"plugins": "Plugins", "plugins": "Plugins",
"python_script": "Python Script", "python_script": "Python Script",
"python_script_plural": "Python Scripts",
"python_scripts": "Python Scripts", "python_scripts": "Python Scripts",
"repositories": "Repositories", "repositories": "Repositories",
"repository": "Repository",
"settings": "settings", "settings": "settings",
"theme": "Theme", "theme": "Theme",
"theme_plural": "Themes",
"themes": "Themes", "themes": "Themes",
"uninstall": "Uninstall",
"update": "Update",
"version": "Version" "version": "Version"
}, },
"config": { "config": {
@ -70,6 +85,74 @@
"upgrade_all": "This will upgrade all of these repositories, make sure that you have read the release notes for all of them before proceeding.", "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" "yes": "Yes"
}, },
"dialog_about": {
"frontend_version": "Frontend version",
"installed_repositories": "Installed repositories",
"integration_version": "Integration version",
"useful_links": "Useful links"
},
"dialog_add_repo": {
"limit": "Only the first 100 repositories are shown, use the search to filter what you need",
"no_match": "No repositories found matching your filter",
"title": "Add repository"
},
"dialog_custom_repositories": {
"category": "Category",
"no_category": "Missing category",
"no_repository": "Missing repository",
"title": "Custom repositories",
"url_placeholder": "Add custom repository URL"
},
"dialog_info": {
"author": "Author",
"downloads": "Downloads",
"install": "Install this repository in HACS",
"loading": "Loading information...",
"no_info": "The developer has not provided any more information for this repository",
"open_issues": "Open issues",
"open_repo": "Open repository",
"stars": "Stars",
"version_installed": "Version installed"
},
"dialog_install": {
"select_version": "Select version",
"show_beta": "Show beta versions",
"type": "Type",
"url": "URL"
},
"dialog_update": {
"available_version": "Available version",
"changelog": "Changelog",
"installed_version": "Installed version",
"title": "Update pending"
},
"entry": {
"information": "Information",
"intro": "Updates and important messages will show here if there are any",
"messages": {
"disabled": {
"content": "Check your log file for more details",
"title": "HACS is disabled"
},
"has_pending_tasks": {
"content": "Some repositories might not show untill this is completed",
"title": "Background tasks pending"
},
"startup": {
"content": "HACS is starting up, during this time some information might be missing or incorrect",
"title": "HACS is starting up"
}
},
"pending_updates": "Pending updates"
},
"menu": {
"about": "About HACS",
"clear": "Clear all new",
"custom_repositories": "Custom repositories",
"documentation": "Documentation",
"open_issue": "Open issue",
"reload": "Reload window"
},
"options": { "options": {
"step": { "step": {
"user": { "user": {
@ -97,6 +180,17 @@
"restart": "You need to restart Home Assistant.", "restart": "You need to restart Home Assistant.",
"restart_pending": "Restart pending" "restart_pending": "Restart pending"
}, },
"repository_card": {
"hide": "Hide",
"information": "Information",
"new_repository": "New repository",
"open_issue": "Open issue",
"open_source": "Open source",
"pending_update": "Pending update",
"reinstall": "Reinstall",
"report": "Report for removal",
"update_information": "Update information"
},
"repository": { "repository": {
"add_to_lovelace": "Add to Lovelace", "add_to_lovelace": "Add to Lovelace",
"authors": "Authors", "authors": "Authors",
@ -131,6 +225,28 @@
"update_information": "Update information", "update_information": "Update information",
"upgrade": "Update" "upgrade": "Update"
}, },
"search": {
"placeholder": "Search for repository"
},
"sections": {
"about": {
"description": "Show information about HACS",
"title": "About"
},
"automation": {
"description": "This is where you find python_scripts, AppDaemon apps and NetDaemon apps",
"title": "Automation"
},
"frontend": {
"description": "This is where you find themes, custom cards and other elements for lovelace",
"title": "Frontend"
},
"integrations": {
"description": "This is where you find custom integrations (custom_components)",
"title": "Integrations"
},
"pending_repository_upgrade": "You are running version {installed}, version {available} is available"
},
"settings": { "settings": {
"add_custom_repository": "ADD CUSTOM REPOSITORY", "add_custom_repository": "ADD CUSTOM REPOSITORY",
"adding_new_repo": "Adding new repository '{repo}'", "adding_new_repo": "Adding new repository '{repo}'",
@ -162,6 +278,9 @@
"last_updated": "Last updated", "last_updated": "Last updated",
"name": "Name", "name": "Name",
"new_repositories": "New Repositories", "new_repositories": "New Repositories",
"no_repositories": "No repositories",
"no_repositories_desc1": "It seems like you don't have any repositories installed in this section yet.",
"no_repositories_desc2": "Click on the + in the bottom corner to add your first!",
"pending_upgrades": "Pending upgrades", "pending_upgrades": "Pending upgrades",
"placeholder_search": "Please enter a search term...", "placeholder_search": "Please enter a search term...",
"sort": "sort", "sort": "sort",

View File

@ -3,25 +3,35 @@
"about": "Acerca de", "about": "Acerca de",
"appdaemon": "AppDaemon", "appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Apps", "appdaemon_apps": "AppDaemon Apps",
"appdaemon_plural": "AppDaemon Apps",
"background_task": "Ejecutando tareas en segundo plano. Se refrescará automaticamente esta página al finalizar.", "background_task": "Ejecutando tareas en segundo plano. Se refrescará automaticamente esta página al finalizar.",
"check_log_file": "Compruebe el archivo de registro para obtener más detalles.", "check_log_file": "Compruebe el archivo de registro para obtener más detalles.",
"continue": "Continuar", "continue": "Continuar",
"disabled": "Deshabilitado", "disabled": "Deshabilitado",
"documentation": "Documentación", "documentation": "Documentación",
"element": "elemento",
"hacs_is_disabled": "HACS está deshabilitado", "hacs_is_disabled": "HACS está deshabilitado",
"installed": "instalado", "installed": "instalado",
"integration": "Integración", "integration": "Integración",
"integration_plural": "Integraciones",
"integrations": "Integraciones", "integrations": "Integraciones",
"lovelace": "Lovelace",
"lovelace_element": "Elemento de Lovelace",
"lovelace_elements": "Elementos de Lovelace",
"manage": "Administrar", "manage": "Administrar",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "NetDaemon Apps", "netdaemon_apps": "NetDaemon Apps",
"netdaemon_plural": "Aplicaciones NetDaemon",
"plugin": "Plugin", "plugin": "Plugin",
"plugin_plural": "Elementos de Lovelace",
"plugins": "Plugins", "plugins": "Plugins",
"python_script": "Python Script", "python_script": "Python Script",
"python_script_plural": "Python Scripts",
"python_scripts": "Python Scripts", "python_scripts": "Python Scripts",
"repositories": "Repositorios", "repositories": "Repositorios",
"settings": "configuraciones", "settings": "configuraciones",
"theme": "Tema", "theme": "Tema",
"theme_plural": "Temas",
"themes": "Temas", "themes": "Temas",
"version": "Versión" "version": "Versión"
}, },
@ -93,7 +103,7 @@
"integration_not_loaded": "Esta integración no se carga en Home Assistant.", "integration_not_loaded": "Esta integración no se carga en Home Assistant.",
"no_restart_required": "No es necesario reiniciar", "no_restart_required": "No es necesario reiniciar",
"not_loaded": "No está cargado", "not_loaded": "No está cargado",
"plugin_not_loaded": "Este plugin no se añade a sus recursos de Lovelace.", "plugin_not_loaded": "Este plugin aun no se ha añadido a sus recursos de Lovelace.",
"restart": "Es necesario reiniciar Home Assistant.", "restart": "Es necesario reiniciar Home Assistant.",
"restart_pending": "Reinicio pendiente" "restart_pending": "Reinicio pendiente"
}, },
@ -131,6 +141,25 @@
"update_information": "Actualizar información", "update_information": "Actualizar información",
"upgrade": "Actualizar" "upgrade": "Actualizar"
}, },
"sections": {
"about": {
"description": "Mostrar información sobre HACS",
"title": "Acerca de"
},
"automation": {
"description": "Aquí es donde se encuentran python_scripts, aplicaciones AppDaemon y aplicaciones NetDaemon",
"title": "Automatización"
},
"frontend": {
"description": "Aquí es donde encontrarás temas, tarjetas personalizadas y otros elementos para lovelace",
"title": "Interfaz"
},
"integrations": {
"description": "Aquí es donde se encuentran las integraciones personalizadas (custom_components)",
"title": "Integraciones"
},
"pending_repository_upgrade": "Está ejecutando la versión {installed}, la versión {available} está disponible"
},
"settings": { "settings": {
"add_custom_repository": "AGREGAR REPOSITORIO PERSONALIZADO", "add_custom_repository": "AGREGAR REPOSITORIO PERSONALIZADO",
"adding_new_repo": "Añadiendo un nuevo repositorio '{repo}'.", "adding_new_repo": "Añadiendo un nuevo repositorio '{repo}'.",

View File

@ -3,25 +3,35 @@
"about": "Névjegy", "about": "Névjegy",
"appdaemon": "AppDaemon", "appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Appok", "appdaemon_apps": "AppDaemon Appok",
"appdaemon_plural": "AppDaemon appok",
"background_task": "Éppen háttérfeladat fut, ez az oldal frissülni fog, ha kész.", "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.", "check_log_file": "További részletekért ellenőrizd a naplófájlt.",
"continue": "Folytatás", "continue": "Folytatás",
"disabled": "Tiltva", "disabled": "Tiltva",
"documentation": "Dokumentáció", "documentation": "Dokumentáció",
"element": "bővítmény",
"hacs_is_disabled": "A HACS le van tiltva", "hacs_is_disabled": "A HACS le van tiltva",
"installed": "Telepített", "installed": "Telepített",
"integration": "Integráció", "integration": "Integráció",
"integration_plural": "Integrációk",
"integrations": "Integrációk", "integrations": "Integrációk",
"lovelace": "Lovelace",
"lovelace_element": "Lovelace bővítmény",
"lovelace_elements": "Lovelace bővítmények",
"manage": "kezelés", "manage": "kezelés",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "NetDaemon Appok", "netdaemon_apps": "NetDaemon Appok",
"plugin": "Bővítmény", "netdaemon_plural": "NetDaemon appok",
"plugins": "Bővítmények", "plugin": "Lovelace",
"plugin_plural": "Lovelace bővítmények",
"plugins": "Lovelace bővítmények",
"python_script": "Python Szkript", "python_script": "Python Szkript",
"python_script_plural": "Python szkriptek",
"python_scripts": "Python Szkriptek", "python_scripts": "Python Szkriptek",
"repositories": "Tárolók", "repositories": "Tárolók",
"settings": "beállítások", "settings": "beállítások",
"theme": "Téma", "theme": "Téma",
"theme_plural": "Témák",
"themes": "Témák", "themes": "Témák",
"version": "Verzió" "version": "Verzió"
}, },
@ -93,7 +103,7 @@
"integration_not_loaded": "Ez az integráció nincs betöltve a Home Assistantban.", "integration_not_loaded": "Ez az integráció nincs betöltve a Home Assistantban.",
"no_restart_required": "Nincs szükség újraindításra", "no_restart_required": "Nincs szükség újraindításra",
"not_loaded": "Nincs betöltve", "not_loaded": "Nincs betöltve",
"plugin_not_loaded": "Ez a bővítmény nincs hozzáadva a Lovelace erőforrásokhoz.", "plugin_not_loaded": "Ez a bővítmény nincs hozzáadva a Lovelace erőforrásaidhoz.",
"restart": "Indítsd újra a Home Assistant programot.", "restart": "Indítsd újra a Home Assistant programot.",
"restart_pending": "Újraindítás függőben" "restart_pending": "Újraindítás függőben"
}, },
@ -114,7 +124,7 @@
"installed": "Telepített", "installed": "Telepített",
"lovelace_copy_example": "Példa másolása a vágólapra", "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_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.", "lovelace_no_js_type": "Nem sikerült meghatározni a bővítmény típusát, ellenőrizd a tárolót.",
"newest": "legújabb", "newest": "legújabb",
"note_appdaemon": "de még hozzá kell adnod az 'apps.yaml' fájlhoz", "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_installed": "Telepítéskor a következő helyre kerül:",
@ -131,6 +141,25 @@
"update_information": "Frissítési információk", "update_information": "Frissítési információk",
"upgrade": "Frissítés" "upgrade": "Frissítés"
}, },
"sections": {
"about": {
"description": "Információk megjelenítése a HACS-ről",
"title": "Névjegy"
},
"automation": {
"description": "Itt Python szkripteket, AppDaemon és NetDaemon appokat találsz",
"title": "Automatizálás"
},
"frontend": {
"description": "Itt témákat, egyéni kártyákat és más bővítményeket találsz a Lovelace-hez",
"title": "Frontend"
},
"integrations": {
"description": "Itt találod az egyéni integrációkat (custom_components)",
"title": "Integrációk"
},
"pending_repository_upgrade": "A(z) {installed} verziót futtatod, a(z) {available} verzió már elérhető"
},
"settings": { "settings": {
"add_custom_repository": "EGYÉNI TÁROLÓ HOZZÁADÁSA", "add_custom_repository": "EGYÉNI TÁROLÓ HOZZÁADÁSA",
"adding_new_repo": "Új tároló hozzáadása '{repo}'", "adding_new_repo": "Új tároló hozzáadása '{repo}'",

View File

@ -1,28 +1,43 @@
{ {
"common": { "common": {
"about": "Om", "about": "Om",
"add": "legg til",
"appdaemon": "AppDaemon", "appdaemon": "AppDaemon",
"appdaemon_apps": "AppDaemon Apper", "appdaemon_apps": "AppDaemon Apper",
"appdaemon_plural": "AppDaemon-apper",
"background_task": "Bakgrunnsoppgaven kjører. Denne siden lastes inn på nytt når den er ferdig.", "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.", "check_log_file": "Sjekk loggfilen din for mer informasjon.",
"continue": "Fortsett", "continue": "Fortsett",
"disabled": "Deaktivert", "disabled": "Deaktivert",
"documentation": "dokumentasjon", "documentation": "dokumentasjon",
"element": "element",
"hacs_is_disabled": "HACS er deaktivert", "hacs_is_disabled": "HACS er deaktivert",
"install": "Installer",
"installed": "Installert", "installed": "Installert",
"integration": "Integrasjon", "integration": "Integrasjon",
"integration_plural": "Integrasjoner",
"integrations": "Integrasjoner", "integrations": "Integrasjoner",
"lovelace": "Lovelace",
"lovelace_element": "Lovelace-element",
"lovelace_elements": "Lovelace-elementer",
"manage": "manage", "manage": "manage",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "NetDaemon Apper", "netdaemon_apps": "NetDaemon Apper",
"plugin": "Plugin", "netdaemon_plural": "NetDaemon-apper",
"plugins": "Plugins", "plugin": "Lovelace",
"plugin_plural": "Lovelace-elementer",
"plugins": "Lovelace-elementer",
"python_script": "Python-skript", "python_script": "Python-skript",
"python_script_plural": "Python-skript",
"python_scripts": "Python-skript", "python_scripts": "Python-skript",
"repositories": "Repositories", "repositories": "Repositories",
"repository": "Repository",
"settings": "Innstillinger", "settings": "Innstillinger",
"theme": "Tema", "theme": "Tema",
"theme_plural": "Temaer",
"themes": "Temaer", "themes": "Temaer",
"uninstall": "Avinstaller",
"update": "Oppdater",
"version": "Versjon" "version": "Versjon"
}, },
"config": { "config": {
@ -70,6 +85,74 @@
"upgrade_all": "Dette vil oppgradere alle disse repositorene, sørg for at du har lest utgivelses notatene for dem alle før du fortsetter.", "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" "yes": "Ja"
}, },
"dialog_about": {
"frontend_version": "Frontend versjon",
"installed_repositories": "Installerte repositories",
"integration_version": "Integrasjonsversjon",
"useful_links": "Nyttige lenker"
},
"dialog_add_repo": {
"limit": "Bare de første 100 elementene vises, bruk søket til å filtrere det du trenger",
"no_match": "Ingen elementer funnet som samsvarer med filteret ditt",
"title": "Legg til repository"
},
"dialog_custom_repositories": {
"category": "Kategori",
"no_category": "Mangler kategori",
"no_repository": "Mangler repository",
"title": "Custom repositories",
"url_placeholder": "Legg til custom repository"
},
"dialog_info": {
"author": "Utgiver",
"downloads": "Nedlastinger",
"install": "Installer dette elementet i HACS",
"loading": "Laster inn informasjon ...",
"no_info": "Utvikleren har ikke gitt mer informasjon for dette elementet",
"open_issues": "Åpne problemer",
"open_repo": "Åpne repository",
"stars": "Stjerner",
"version_installed": "Versjon installert"
},
"dialog_install": {
"select_version": "Velg versjon",
"show_beta": "Vis betaversjoner",
"type": "Type",
"url": "URL"
},
"dialog_update": {
"available_version": "Tilgjengelig versjon",
"changelog": "Endringslogg",
"installed_version": "Installert versjon",
"title": "Oppdatering venter"
},
"entry": {
"information": "Informasjon",
"intro": "Oppdateringer og viktige meldinger vises her hvis det er noen",
"messages": {
"disabled": {
"content": "Sjekk loggfilen din for mer informasjon",
"title": "HACS er deaktivert"
},
"has_pending_tasks": {
"content": "Noen elementer vises kanskje ikke før dette er fullført",
"title": "Venter på bakgrunnsoppgaver"
},
"startup": {
"content": "HACS starter opp, i løpet av denne tiden kan det hende at noe informasjon mangler eller er feil",
"title": "HACS starter opp"
}
},
"pending_updates": "Oppdateringer er klare"
},
"menu": {
"about": "Om HACS",
"clear": "Fjern alt nytt",
"custom_repositories": "Custom repositories",
"documentation": "Dokumentasjon",
"open_issue": "Meld et problem",
"reload": "Last inn vinduet på nytt"
},
"options": { "options": {
"step": { "step": {
"user": { "user": {
@ -93,10 +176,21 @@
"integration_not_loaded": "Integrasjonen er ikke lastet inn i Home Assistant.", "integration_not_loaded": "Integrasjonen er ikke lastet inn i Home Assistant.",
"no_restart_required": "Ingen omstart kreves", "no_restart_required": "Ingen omstart kreves",
"not_loaded": "Ikke lastet inn", "not_loaded": "Ikke lastet inn",
"plugin_not_loaded": "Denne pluginen er ikke lagt til i lovelace under \"resource\" delen av konfigurasjonen.", "plugin_not_loaded": "Dette elementet er ikke lagt til i lovelace under \"resource\" delen av konfigurasjonen.",
"restart": "Du må restart Home Assistant", "restart": "Du må restart Home Assistant",
"restart_pending": "Restart er nødvendig" "restart_pending": "Restart er nødvendig"
}, },
"repository_card": {
"hide": "Skjul",
"information": "Informasjon",
"new_repository": "Ny",
"open_issue": "Meld et problem",
"open_source": "Åpne kilde",
"pending_update": "Oppdatering venter",
"reinstall": "Installer på nytt",
"report": "Rapporter for fjerning",
"update_information": "Oppdater informasjon"
},
"repository": { "repository": {
"add_to_lovelace": "Legg til i Lovelace", "add_to_lovelace": "Legg til i Lovelace",
"authors": "Laget av", "authors": "Laget av",
@ -113,7 +207,7 @@
"installed": "Installert", "installed": "Installert",
"lovelace_copy_example": "Kopier eksemplet til utklippstavlen", "lovelace_copy_example": "Kopier eksemplet til utklippstavlen",
"lovelace_instruction": "Når du legger til dette i lovelace-konfigurasjonen din, bruk dette", "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.", "lovelace_no_js_type": "Kunne ikke bestemme typen for dettte elementet, sjekk repository.",
"newest": "Nyeste", "newest": "Nyeste",
"note_appdaemon": "du må fortsatt legge den til i 'apps.yaml' filen", "note_appdaemon": "du må fortsatt legge den til i 'apps.yaml' filen",
"note_installed": "Når det er installert, vil dette ligge i", "note_installed": "Når det er installert, vil dette ligge i",
@ -121,7 +215,7 @@
"note_plugin": "du må fortsatt legge den til i lovelace-konfigurasjonen ('ui-lovelace.yaml' eller den rå UI-konfigurasjonsredigereren)", "note_plugin": "du må fortsatt legge den til i lovelace-konfigurasjonen ('ui-lovelace.yaml' eller den rå UI-konfigurasjonsredigereren)",
"note_plugin_post_107": "du må fortsatt legge den til i lovelace konfigurasjonen ('configuration.yaml' eller via resource behanleren i grensesnittet '\/config\/lovelace\/resources')", "note_plugin_post_107": "du må fortsatt legge den til i lovelace konfigurasjonen ('configuration.yaml' eller via resource behanleren i grensesnittet '\/config\/lovelace\/resources')",
"open_issue": "Meld et problem", "open_issue": "Meld et problem",
"open_plugin": "Åpne plugin", "open_plugin": "Åpne kilde",
"reinstall": "Installer på nytt", "reinstall": "Installer på nytt",
"repository": "Repository", "repository": "Repository",
"restart_home_assistant": "Start Home Assistant på nytt", "restart_home_assistant": "Start Home Assistant på nytt",
@ -130,6 +224,28 @@
"update_information": "Oppdater informasjon", "update_information": "Oppdater informasjon",
"upgrade": "Oppdater" "upgrade": "Oppdater"
}, },
"search": {
"placeholder": "Søk etter repository"
},
"sections": {
"about": {
"description": "Vis informasjon om HACS",
"title": "Om"
},
"automation": {
"description": "Det er her du finner python_scripts, AppDaemon-apper og NetDaemon-apper",
"title": "Automasjon"
},
"frontend": {
"description": "Det er her du finner temaer, tilpassede kort og andre elementer for lovelace",
"title": "Frontend"
},
"integrations": {
"description": "Det er her du finner tilpassede integrasjoner (custom_components)",
"title": "Integrasjoner"
},
"pending_repository_upgrade": "Du kjører versjon {installed} , versjon {available} er tilgjengelig"
},
"settings": { "settings": {
"add_custom_repository": "LEGG TIL REPOSITORY", "add_custom_repository": "LEGG TIL REPOSITORY",
"adding_new_repo": "Legger til ny repository '{repo}'", "adding_new_repo": "Legger til ny repository '{repo}'",
@ -161,6 +277,9 @@
"last_updated": "Sist oppdatert", "last_updated": "Sist oppdatert",
"name": "Navn", "name": "Navn",
"new_repositories": "Nye repositories", "new_repositories": "Nye repositories",
"no_repositories": "Ingen repositories",
"no_repositories_desc1": "Det virker som om du ikke har noen elementer installert i denne delen ennå.",
"no_repositories_desc2": "Klikk på + i nederste hjørne for å legge til din første!",
"pending_upgrades": "Venter på oppgradering", "pending_upgrades": "Venter på oppgradering",
"placeholder_search": "Skriv inn et søkeord ...", "placeholder_search": "Skriv inn et søkeord ...",
"sort": "sorter", "sort": "sorter",

View File

@ -3,25 +3,35 @@
"about": "Om", "about": "Om",
"appdaemon": "AppDaemon", "appdaemon": "AppDaemon",
"appdaemon_apps": "AppDeamon-appar", "appdaemon_apps": "AppDeamon-appar",
"appdaemon_plural": "AppDaemon-appar",
"background_task": "Bakgrunnsoppgåve køyrer. Denne sida kjem til å laste seg omatt når ho er ferdig.", "background_task": "Bakgrunnsoppgåve køyrer. Denne sida kjem til å laste seg omatt når ho er ferdig.",
"check_log_file": "Sjå i loggfila di for meir detaljar.", "check_log_file": "Sjå i loggfila di for meir detaljar.",
"continue": "Hald fram", "continue": "Hald fram",
"disabled": "Deaktivert", "disabled": "Deaktivert",
"documentation": "Dokumentasjon", "documentation": "Dokumentasjon",
"element": "element",
"hacs_is_disabled": "HACS er deaktivert", "hacs_is_disabled": "HACS er deaktivert",
"installed": "Installert", "installed": "Installert",
"integration": "Integrasjon", "integration": "Integrasjon",
"integration_plural": "Integrasjonar",
"integrations": "Integrasjonar", "integrations": "Integrasjonar",
"lovelace": "Lovelace",
"lovelace_element": "Lovelace-element",
"lovelace_elements": "Lovelace-element",
"manage": "Handtere", "manage": "Handtere",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "NetDeamon-appar", "netdaemon_apps": "NetDeamon-appar",
"netdaemon_plural": "NetDaemon-appar",
"plugin": "Tillegg", "plugin": "Tillegg",
"plugin_plural": "Lovelace-element",
"plugins": "Tillegg", "plugins": "Tillegg",
"python_script": "Pythonskript", "python_script": "Pythonskript",
"python_script_plural": "Pythonskript",
"python_scripts": "Pythonskript", "python_scripts": "Pythonskript",
"repositories": "Repositories", "repositories": "Repositories",
"settings": "innstillingar", "settings": "innstillingar",
"theme": "Tema", "theme": "Tema",
"theme_plural": "Tema",
"themes": "Tema", "themes": "Tema",
"version": "Versjon" "version": "Versjon"
}, },
@ -59,7 +69,7 @@
"exist": "{item} eksisterer allereie", "exist": "{item} eksisterer allereie",
"generic": "Er du sikker?", "generic": "Er du sikker?",
"home_assistant_is_restarting": "Vent... Home Assistant starter på nytt no.", "home_assistant_is_restarting": "Vent... Home Assistant starter på nytt no.",
"home_assistant_version_not_correct": "Du køyrer Home Assistant-versjonen '{haversion}', men dette kodedepoet krev minimum versjon '{minversion}' for å bli installert.", "home_assistant_version_not_correct": "Du køyrer Home Assistant-versjonen '{haversion}', men dette kodedepoet krev minst versjon '{minversion}' for å bli installert.",
"no": "Nei", "no": "Nei",
"no_upgrades": "Ingen ventande oppgradringer", "no_upgrades": "Ingen ventande oppgradringer",
"ok": "OK", "ok": "OK",
@ -120,6 +130,7 @@
"note_installed": "Når dette er installert, kjem den til å vere plassert i", "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_integration": "du må framleis legge dette til i \"configuration.yaml\"-fila di",
"note_plugin": "du må framleis dette til i Lovelace-konfigurasjonen (\"ui-lovelace.yaml\" eller i rå-brukargrensesnittredigeraren", "note_plugin": "du må framleis dette til i Lovelace-konfigurasjonen (\"ui-lovelace.yaml\" eller i rå-brukargrensesnittredigeraren",
"note_plugin_post_107": "du må framleis legge dette til i lovelace-konfigurasjonen ('configuration.yaml' eller i kjelderedigeraren ''\/config\/lovelace\/resources')",
"open_issue": "Opne problem", "open_issue": "Opne problem",
"open_plugin": "Opne tillegg", "open_plugin": "Opne tillegg",
"reinstall": "Installer på nytt", "reinstall": "Installer på nytt",
@ -130,6 +141,25 @@
"update_information": "Oppdater informasjonen", "update_information": "Oppdater informasjonen",
"upgrade": "Oppdater" "upgrade": "Oppdater"
}, },
"sections": {
"about": {
"description": "Vis informasjon om HACS",
"title": "Om"
},
"automation": {
"description": "Her finn du python_scripts, AppDaemon-appar og NetDaemon-appar",
"title": "Automasjon"
},
"frontend": {
"description": "Her finn du tema, eigendefinerte kort og andre element for lovelace",
"title": "Frontend"
},
"integrations": {
"description": "Her finn du eigendefinerte ingtegrasjonar (custom_components)",
"title": "Integrasjonar"
},
"pending_repository_upgrade": "Du køyrer versjon {installed}, og versjon {available} er tilgjengeleg"
},
"settings": { "settings": {
"add_custom_repository": "LEGG TIL EIN ANNAN REPOSITORY", "add_custom_repository": "LEGG TIL EIN ANNAN REPOSITORY",
"adding_new_repo": "Legger til ny repository '{repo}'", "adding_new_repo": "Legger til ny repository '{repo}'",

View File

@ -3,6 +3,7 @@
"about": "O", "about": "O",
"appdaemon": "AppDaemon", "appdaemon": "AppDaemon",
"appdaemon_apps": "Aplikacje AppDaemon", "appdaemon_apps": "Aplikacje AppDaemon",
"appdaemon_plural": "Aplikacje AppDaemon",
"background_task": "Wykonywanie zadania w tle, ta strona zostanie odświeżona, gdy zadanie zostanie ukończone.", "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.", "check_log_file": "Sprawdź plik dziennika, aby uzyskać więcej informacji.",
"continue": "Kontynuuj", "continue": "Kontynuuj",
@ -11,17 +12,25 @@
"hacs_is_disabled": "HACS jest wyłączony", "hacs_is_disabled": "HACS jest wyłączony",
"installed": "zainstalowane", "installed": "zainstalowane",
"integration": "Integracja", "integration": "Integracja",
"integration_plural": "Integracje",
"integrations": "Integracje", "integrations": "Integracje",
"lovelace": "Lovelace",
"lovelace_element": "Element Lovelace",
"lovelace_elements": "Elementy Lovelace",
"manage": "zarządzaj", "manage": "zarządzaj",
"netdaemon": "NetDaemon", "netdaemon": "NetDaemon",
"netdaemon_apps": "Aplikacje NetDaemon", "netdaemon_apps": "Aplikacje NetDaemon",
"netdaemon_plural": "Aplikacje NetDaemon",
"plugin": "Wtyczka", "plugin": "Wtyczka",
"plugin_plural": "Elementy Lovelace",
"plugins": "Wtyczki", "plugins": "Wtyczki",
"python_script": "Skrypt Python", "python_script": "Skrypt Python",
"python_script_plural": "Skrypty języka Python",
"python_scripts": "Skrypty Python", "python_scripts": "Skrypty Python",
"repositories": "Repozytoria", "repositories": "Repozytoria",
"settings": "ustawienia", "settings": "ustawienia",
"theme": "Motyw", "theme": "Motyw",
"theme_plural": "Motywy",
"themes": "Motywy", "themes": "Motywy",
"version": "Wersja" "version": "Wersja"
}, },
@ -131,6 +140,22 @@
"update_information": "Uaktualnij informacje", "update_information": "Uaktualnij informacje",
"upgrade": "Uaktualnij" "upgrade": "Uaktualnij"
}, },
"sections": {
"about": {
"description": "Pokaż informacje o HACS"
},
"automation": {
"description": "Tutaj znajdziesz skrypty Pythona, aplikacje AppDaemon i NetDaemon",
"title": "Automatyzacja"
},
"frontend": {
"description": "Tutaj znajdziesz motywy, niestandardowe karty i inne elementy dla Lovelace"
},
"integrations": {
"description": "Tutaj znajdziesz niestandardowe integracje (custom_components)",
"title": "Integracje"
}
},
"settings": { "settings": {
"add_custom_repository": "DODAJ REPOZYTORIUM NIESTANDARDOWE", "add_custom_repository": "DODAJ REPOZYTORIUM NIESTANDARDOWE",
"adding_new_repo": "Dodawanie nowego repozytorium '{repo}'", "adding_new_repo": "Dodawanie nowego repozytorium '{repo}'",

View File

@ -6,7 +6,7 @@ https://hacs.xyz/
""" """
import voluptuous as vol import voluptuous as vol
from aiogithubapi import AIOGitHub, AIOGitHubException from aiogithubapi import GitHub, AIOGitHubAPIException
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.const import EVENT_HOMEASSISTANT_START from homeassistant.const import EVENT_HOMEASSISTANT_START
from homeassistant.const import __version__ as HAVERSION from homeassistant.const import __version__ as HAVERSION
@ -15,12 +15,9 @@ from homeassistant.exceptions import ConfigEntryNotReady, ServiceNotFound
from homeassistant.helpers.aiohttp_client import async_create_clientsession from homeassistant.helpers.aiohttp_client import async_create_clientsession
from homeassistant.helpers.event import async_call_later from homeassistant.helpers.event import async_call_later
from custom_components.hacs.configuration_schema import ( from custom_components.hacs.configuration_schema import hacs_config_combined
hacs_base_config_schema,
hacs_config_option_schema,
)
from custom_components.hacs.const import DOMAIN, ELEMENT_TYPES, STARTUP, VERSION from custom_components.hacs.const import DOMAIN, ELEMENT_TYPES, STARTUP, VERSION
from custom_components.hacs.constrains import check_constans, check_requirements from custom_components.hacs.constrains import check_constrains
from custom_components.hacs.helpers.remaining_github_calls import get_fetch_updates_for from custom_components.hacs.helpers.remaining_github_calls import get_fetch_updates_for
from custom_components.hacs.hacsbase.configuration import Configuration from custom_components.hacs.hacsbase.configuration import Configuration
from custom_components.hacs.hacsbase.data import HacsData from custom_components.hacs.hacsbase.data import HacsData
@ -34,9 +31,7 @@ from custom_components.hacs.globals import get_hacs
from custom_components.hacs.helpers.network import internet_connectivity_check from custom_components.hacs.helpers.network import internet_connectivity_check
SCHEMA = hacs_base_config_schema() CONFIG_SCHEMA = vol.Schema({DOMAIN: hacs_config_combined()}, extra=vol.ALLOW_EXTRA)
SCHEMA[vol.Optional("options")] = hacs_config_option_schema()
CONFIG_SCHEMA = vol.Schema({DOMAIN: SCHEMA}, extra=vol.ALLOW_EXTRA)
async def async_setup(hass, config): async def async_setup(hass, config):
@ -49,17 +44,10 @@ async def async_setup(hass, config):
hass.data[DOMAIN] = config hass.data[DOMAIN] = config
hacs.hass = hass hacs.hass = hass
hacs.session = async_create_clientsession(hass) hacs.session = async_create_clientsession(hass)
hacs.configuration = Configuration.from_dict( hacs.configuration = Configuration.from_dict(config[DOMAIN])
config[DOMAIN], config[DOMAIN].get("options")
)
hacs.configuration.config = config hacs.configuration.config = config
hacs.configuration.config_type = "yaml" hacs.configuration.config_type = "yaml"
await startup_wrapper_for_yaml() await startup_wrapper_for_yaml()
hass.async_create_task(
hass.config_entries.flow.async_init(
DOMAIN, context={"source": config_entries.SOURCE_IMPORT}, data={}
)
)
return True return True
@ -67,11 +55,10 @@ async def async_setup_entry(hass, config_entry):
"""Set up this integration using UI.""" """Set up this integration using UI."""
hacs = get_hacs() hacs = get_hacs()
conf = hass.data.get(DOMAIN) conf = hass.data.get(DOMAIN)
if conf is not None:
return False
if config_entry.source == config_entries.SOURCE_IMPORT: 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))
hass.async_create_task(
hass.config_entries.async_remove(config_entry.entry_id)
)
return False return False
hacs.hass = hass hacs.hass = hass
hacs.session = async_create_clientsession(hass) hacs.session = async_create_clientsession(hass)
@ -83,7 +70,7 @@ async def async_setup_entry(hass, config_entry):
config_entry.add_update_listener(reload_hacs) config_entry.add_update_listener(reload_hacs)
try: try:
startup_result = await hacs_startup() startup_result = await hacs_startup()
except AIOGitHubException: except AIOGitHubAPIException:
startup_result = False startup_result = False
if not startup_result: if not startup_result:
hacs.system.disabled = True hacs.system.disabled = True
@ -97,7 +84,7 @@ async def startup_wrapper_for_yaml():
hacs = get_hacs() hacs = get_hacs()
try: try:
startup_result = await hacs_startup() startup_result = await hacs_startup()
except AIOGitHubException: except AIOGitHubAPIException:
startup_result = False startup_result = False
if not startup_result: if not startup_result:
hacs.system.disabled = True hacs.system.disabled = True
@ -115,8 +102,6 @@ async def startup_wrapper_for_yaml():
async def hacs_startup(): async def hacs_startup():
"""HACS startup tasks.""" """HACS startup tasks."""
hacs = get_hacs() hacs = get_hacs()
if not check_requirements():
return False
if hacs.configuration.debug: if hacs.configuration.debug:
try: try:
await hacs.hass.services.async_call( await hacs.hass.services.async_call(
@ -142,7 +127,7 @@ async def hacs_startup():
hacs.system.lovelace_mode = lovelace_info.get("mode", "yaml") hacs.system.lovelace_mode = lovelace_info.get("mode", "yaml")
hacs.system.disabled = False hacs.system.disabled = False
hacs.github = AIOGitHub( hacs.github = GitHub(
hacs.configuration.token, async_create_clientsession(hacs.hass) hacs.configuration.token, async_create_clientsession(hacs.hass)
) )
hacs.data = HacsData() hacs.data = HacsData()
@ -154,7 +139,7 @@ async def hacs_startup():
hacs.logger.debug(f"Can update {can_update} repositories") hacs.logger.debug(f"Can update {can_update} repositories")
# Check HACS Constrains # Check HACS Constrains
if not await hacs.hass.async_add_executor_job(check_constans): if not await hacs.hass.async_add_executor_job(check_constrains):
if hacs.configuration.config_type == "flow": if hacs.configuration.config_type == "flow":
if hacs.configuration.config_entry is not None: if hacs.configuration.config_entry is not None:
await async_remove_entry(hacs.hass, hacs.configuration.config_entry) await async_remove_entry(hacs.hass, hacs.configuration.config_entry)
@ -189,18 +174,6 @@ async def hacs_startup():
hacs.common.categories.append("appdaemon") hacs.common.categories.append("appdaemon")
if hacs.configuration.netdaemon: if hacs.configuration.netdaemon:
hacs.common.categories.append("netdaemon") hacs.common.categories.append("netdaemon")
if hacs.configuration.python_script:
hacs.configuration.python_script = False
if hacs.configuration.config_type == "yaml":
hacs.logger.warning(
"Configuration option 'python_script' is deprecated and you should remove it from your configuration, HACS will know if you use 'python_script' in your Home Assistant configuration, this option will be removed in a future release."
)
if hacs.configuration.theme:
hacs.configuration.theme = False
if hacs.configuration.config_type == "yaml":
hacs.logger.warning(
"Configuration option 'theme' is deprecated and you should remove it from your configuration, HACS will know if you use 'theme' in your Home Assistant configuration, this option will be removed in a future release."
)
# Setup startup tasks # Setup startup tasks
if hacs.configuration.config_type == "yaml": if hacs.configuration.config_type == "yaml":

View File

@ -2,7 +2,7 @@
# pylint: disable=dangerous-default-value # pylint: disable=dangerous-default-value
import logging import logging
import voluptuous as vol import voluptuous as vol
from aiogithubapi import AIOGitHubException, AIOGitHubAuthentication from aiogithubapi import AIOGitHubAPIException, AIOGitHubAPIAuthenticationException
from homeassistant import config_entries from homeassistant import config_entries
from homeassistant.core import callback from homeassistant.core import callback
from homeassistant.helpers import aiohttp_client from homeassistant.helpers import aiohttp_client
@ -47,7 +47,7 @@ class HacsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
"""Show the configuration form to edit location data.""" """Show the configuration form to edit location data."""
return self.async_show_form( return self.async_show_form(
step_id="user", step_id="user",
data_schema=vol.Schema(hacs_base_config_schema(user_input, True)), data_schema=vol.Schema(hacs_base_config_schema(user_input)),
errors=self._errors, errors=self._errors,
) )
@ -56,23 +56,16 @@ class HacsFlowHandler(config_entries.ConfigFlow, domain=DOMAIN):
def async_get_options_flow(config_entry): def async_get_options_flow(config_entry):
return HacsOptionsFlowHandler(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): async def _test_token(self, token):
"""Return true if token is valid.""" """Return true if token is valid."""
try: try:
session = aiohttp_client.async_get_clientsession(self.hass) session = aiohttp_client.async_get_clientsession(self.hass)
await get_repository(session, token, "hacs/org") await get_repository(session, token, "hacs/org")
return True return True
except (AIOGitHubException, AIOGitHubAuthentication) as exception: except (
AIOGitHubAPIException,
AIOGitHubAPIAuthenticationException,
) as exception:
_LOGGER.error(exception) _LOGGER.error(exception)
return False return False

View File

@ -9,8 +9,6 @@ SIDEPANEL_TITLE = "sidepanel_title"
SIDEPANEL_ICON = "sidepanel_icon" SIDEPANEL_ICON = "sidepanel_icon"
APPDAEMON = "appdaemon" APPDAEMON = "appdaemon"
NETDAEMON = "netdaemon" NETDAEMON = "netdaemon"
PYTHON_SCRIPT = "python_script"
THEME = "theme"
# Options: # Options:
COUNTRY = "country" COUNTRY = "country"
@ -19,46 +17,39 @@ RELEASE_LIMIT = "release_limit"
EXPERIMENTAL = "experimental" EXPERIMENTAL = "experimental"
def hacs_base_config_schema(config: dict = {}, config_flow: bool = False) -> dict: def hacs_base_config_schema(config: dict = {}) -> dict:
"""Return a shcema configuration dict for HACS.""" """Return a shcema configuration dict for HACS."""
if not config: if not config:
config = { config = {
TOKEN: "xxxxxxxxxxxxxxxxxxxxxxxxxxx", TOKEN: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
SIDEPANEL_ICON: "mdi:alpha-c-box",
SIDEPANEL_TITLE: "HACS",
APPDAEMON: False,
NETDAEMON: False,
PYTHON_SCRIPT: False,
THEME: False,
}
if config_flow:
return {
vol.Required(TOKEN, default=config.get(TOKEN)): str,
vol.Optional(SIDEPANEL_TITLE, default=config.get(SIDEPANEL_TITLE)): str,
vol.Optional(SIDEPANEL_ICON, default=config.get(SIDEPANEL_ICON)): str,
vol.Optional(APPDAEMON, default=config.get(APPDAEMON)): bool,
vol.Optional(NETDAEMON, default=config.get(NETDAEMON)): bool,
} }
return { return {
vol.Required(TOKEN, default=config.get(TOKEN)): str, vol.Required(TOKEN, default=config.get(TOKEN)): str,
vol.Optional(SIDEPANEL_TITLE, default=config.get(SIDEPANEL_TITLE)): str,
vol.Optional(SIDEPANEL_ICON, default=config.get(SIDEPANEL_ICON)): str,
vol.Optional(APPDAEMON, default=config.get(APPDAEMON)): bool,
vol.Optional(NETDAEMON, default=config.get(NETDAEMON)): bool,
vol.Optional(PYTHON_SCRIPT, default=config.get(PYTHON_SCRIPT)): bool,
vol.Optional(THEME, default=config.get(THEME)): bool,
} }
def hacs_config_option_schema(options: dict = {}) -> dict: def hacs_config_option_schema(options: dict = {}) -> dict:
"""Return a shcema for HACS configuration options.""" """Return a shcema for HACS configuration options."""
if not options: if not options:
options = {COUNTRY: "ALL", DEBUG: False, RELEASE_LIMIT: 5, EXPERIMENTAL: False} options = {
APPDAEMON: False,
COUNTRY: "ALL",
DEBUG: False,
EXPERIMENTAL: False,
NETDAEMON: False,
RELEASE_LIMIT: 5,
SIDEPANEL_ICON: "hacs:hacs",
SIDEPANEL_TITLE: "HACS",
}
return { return {
vol.Optional(COUNTRY, default=options.get(COUNTRY)): vol.In(LOCALE), vol.Optional(SIDEPANEL_TITLE, default=options.get(SIDEPANEL_TITLE)): str,
vol.Optional(SIDEPANEL_ICON, default=options.get(SIDEPANEL_ICON)): str,
vol.Optional(RELEASE_LIMIT, default=options.get(RELEASE_LIMIT)): int, vol.Optional(RELEASE_LIMIT, default=options.get(RELEASE_LIMIT)): int,
vol.Optional(EXPERIMENTAL, default=options.get(EXPERIMENTAL)): bool, vol.Optional(COUNTRY, default=options.get(COUNTRY)): vol.In(LOCALE),
vol.Optional(APPDAEMON, default=options.get(APPDAEMON)): bool,
vol.Optional(NETDAEMON, default=options.get(NETDAEMON)): bool,
vol.Optional(DEBUG, default=options.get(DEBUG)): bool, vol.Optional(DEBUG, default=options.get(DEBUG)): bool,
vol.Optional(EXPERIMENTAL, default=options.get(EXPERIMENTAL)): bool,
} }

View File

@ -1,7 +1,7 @@
"""Constants for HACS""" """Constants for HACS"""
NAME_LONG = "HACS (Home Assistant Community Store)" NAME_LONG = "HACS (Home Assistant Community Store)"
NAME_SHORT = "HACS" NAME_SHORT = "HACS"
VERSION = "0.24.5" VERSION = "1.0.0"
DOMAIN = "hacs" DOMAIN = "hacs"
PROJECT_URL = "https://github.com/hacs/integration/" PROJECT_URL = "https://github.com/hacs/integration/"
CUSTOM_UPDATER_LOCATIONS = [ CUSTOM_UPDATER_LOCATIONS = [
@ -15,8 +15,8 @@ DOMAIN_DATA = f"{NAME_SHORT.lower()}_data"
ELEMENT_TYPES = ["integration", "plugin"] ELEMENT_TYPES = ["integration", "plugin"]
IFRAME = { IFRAME = {
"title": "Community", "title": "HACS",
"icon": "mdi:alpha-c-box", "icon": "hacs:hacs",
"url": "/community_overview", "url": "/community_overview",
"path": "community", "path": "community",
"require_admin": True, "require_admin": True,

View File

@ -7,10 +7,10 @@ from .helpers.misc import version_left_higher_then_right
from custom_components.hacs.globals import get_hacs from custom_components.hacs.globals import get_hacs
MINIMUM_HA_VERSION = "0.98.0" MINIMUM_HA_VERSION = "0.110.0"
def check_constans(): def check_constrains():
"""Check HACS constrains.""" """Check HACS constrains."""
if not constrain_translations(): if not constrain_translations():
return False return False
@ -54,46 +54,3 @@ def constrain_translations():
hacs.logger.critical("You are missing the translations directory.") hacs.logger.critical("You are missing the translations directory.")
return False return False
return True return True
def check_requirements():
"""Check the requirements"""
missing = []
try:
from aiogithubapi import AIOGitHubException # pylint: disable=unused-import
except ImportError:
missing.append("aiogithubapi")
try:
from hacs_frontend import locate_gz # pylint: disable=unused-import
except ImportError:
missing.append("hacs_frontend")
try:
import semantic_version # pylint: disable=unused-import
except ImportError:
missing.append("semantic_version")
try:
from integrationhelper import Logger # pylint: disable=unused-import
except ImportError:
missing.append("integrationhelper")
try:
import backoff # pylint: disable=unused-import
except ImportError:
missing.append("backoff")
try:
import aiofiles # pylint: disable=unused-import
except ImportError:
missing.append("aiofiles")
if missing:
hacs = get_hacs()
for requirement in missing:
hacs.logger.critical(
f"Required python requirement '{requirement}' is not installed"
)
return False
return True

View File

@ -1,6 +1,7 @@
# pylint: disable=invalid-name, missing-docstring # pylint: disable=invalid-name, missing-docstring
hacs = [] hacs = []
removed_repositories = [] removed_repositories = []
rules = {}
def get_hacs(): def get_hacs():

View File

@ -6,7 +6,7 @@ from datetime import timedelta
from homeassistant.helpers.event import async_call_later, async_track_time_interval from homeassistant.helpers.event import async_call_later, async_track_time_interval
from aiogithubapi import AIOGitHubException, AIOGitHubRatelimit from aiogithubapi import AIOGitHubAPIException, AIOGitHubAPIRatelimitException
from integrationhelper import Logger from integrationhelper import Logger
from queueman import QueueManager from queueman import QueueManager
@ -115,7 +115,7 @@ class Hacs:
"""Get repository by ID.""" """Get repository by ID."""
try: try:
for repository in self.repositories: for repository in self.repositories:
if repository.information.uid == repository_id: if str(repository.data.id) == str(repository_id):
return repository return repository
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
pass pass
@ -131,11 +131,9 @@ class Hacs:
pass pass
return None return None
def is_known(self, repository_full_name): def is_known(self, repository_id):
"""Return a bool if the repository is known.""" """Return a bool if the repository is known."""
return repository_full_name.lower() in [ return str(repository_id) in [str(x.data.id) for x in self.repositories]
x.data.full_name.lower() for x in self.repositories
]
@property @property
def sorted_by_name(self): def sorted_by_name(self):
@ -156,8 +154,6 @@ class Hacs:
self.system.status.background_task = True self.system.status.background_task = True
await self.hass.async_add_executor_job(setup_extra_stores) await self.hass.async_add_executor_job(setup_extra_stores)
self.hass.bus.async_fire("hacs/status", {}) 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_startup()
await self.handle_critical_repositories() await self.handle_critical_repositories()
@ -186,7 +182,6 @@ class Hacs:
await self.prosess_queue() await self.prosess_queue()
self.system.status.startup = False self.system.status.startup = False
self.system.status.new = False
self.system.status.background_task = False self.system.status.background_task = False
self.hass.bus.async_fire("hacs/status", {}) self.hass.bus.async_fire("hacs/status", {})
await self.data.async_write() await self.data.async_write()
@ -216,7 +211,7 @@ class Hacs:
try: try:
critical = await self.data_repo.get_contents("critical") critical = await self.data_repo.get_contents("critical")
critical = json.loads(critical.content) critical = json.loads(critical.content)
except AIOGitHubException: except AIOGitHubAPIException:
pass pass
if not critical: if not critical:
@ -290,11 +285,10 @@ class Hacs:
) )
self.system.status.background_task = True self.system.status.background_task = True
self.hass.bus.async_fire("hacs/status", {}) 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: for repository in self.repositories:
if ( if (
repository.status.installed repository.data.installed
and repository.data.category in self.common.categories and repository.data.category in self.common.categories
): ):
self.queue.add(self.factory.safe_update(repository)) self.queue.add(self.factory.safe_update(repository))
@ -311,8 +305,7 @@ class Hacs:
await self.hass.async_add_executor_job(setup_extra_stores) await self.hass.async_add_executor_job(setup_extra_stores)
self.system.status.background_task = True self.system.status.background_task = True
self.hass.bus.async_fire("hacs/status", {}) 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: for repository in self.repositories:
if repository.data.category in self.common.categories: if repository.data.category in self.common.categories:
self.queue.add(self.factory.safe_common_update(repository)) self.queue.add(self.factory.safe_common_update(repository))
@ -329,9 +322,9 @@ class Hacs:
"""Clear out blaclisted repositories.""" """Clear out blaclisted repositories."""
need_to_save = False need_to_save = False
for removed in removed_repositories: for removed in removed_repositories:
if self.is_known(removed.repository): repository = self.get_by_name(removed.repository)
repository = self.get_by_name(removed.repository) if repository is not None:
if repository.status.installed and removed.removal_type != "critical": if repository.data.installed and removed.removal_type != "critical":
self.logger.warning( self.logger.warning(
f"You have {repository.data.full_name} installed with HACS " f"You have {repository.data.full_name} installed with HACS "
+ f"this repository has been removed, please consider removing it. " + f"this repository has been removed, please consider removing it. "
@ -354,11 +347,6 @@ class Hacs:
org = await get_default_repos_orgs(self.github, category) org = await get_default_repos_orgs(self.github, category)
for repo in org: for repo in org:
repositories[category].append(repo) 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 return repositories
async def load_known_repositories(self): async def load_known_repositories(self):
@ -378,6 +366,11 @@ class Hacs:
for repo in repositories[category]: for repo in repositories[category]:
if is_removed(repo): if is_removed(repo):
continue continue
if self.is_known(repo): repository = self.get_by_name(repo)
if repository is not None:
if str(repository.data.id) not in self.common.default:
self.common.default.append(str(repository.data.id))
else:
continue
continue continue
self.queue.add(self.factory.safe_register(repo, category)) self.queue.add(self.factory.safe_register(repo, category))

View File

@ -25,8 +25,8 @@ class Configuration:
plugin_path: str = "www/community/" plugin_path: str = "www/community/"
python_script_path: str = "python_scripts/" python_script_path: str = "python_scripts/"
python_script: bool = False python_script: bool = False
sidepanel_icon: str = "mdi:alpha-c-box" sidepanel_icon: str = "hacs:hacs"
sidepanel_title: str = "Community" sidepanel_title: str = "HACS"
theme_path: str = "themes/" theme_path: str = "themes/"
theme: bool = False theme: bool = False
token: str = None token: str = None
@ -50,7 +50,7 @@ class Configuration:
logger.debug(f"{key}: {config[key]}") logger.debug(f"{key}: {config[key]}")
@staticmethod @staticmethod
def from_dict(configuration: dict, options: dict): def from_dict(configuration: dict, options: dict = None):
"""Set attributes from dicts.""" """Set attributes from dicts."""
if isinstance(options, bool) or isinstance(configuration.get("options"), bool): if isinstance(options, bool) or isinstance(configuration.get("options"), bool):
raise HacsException("Configuration is not valid.") raise HacsException("Configuration is not valid.")

View File

@ -42,28 +42,39 @@ class HacsData:
repository_manifest = repository.repository_manifest.manifest repository_manifest = repository.repository_manifest.manifest
else: else:
repository_manifest = None repository_manifest = None
content[repository.information.uid] = { data = {
"authors": repository.data.authors, "authors": repository.data.authors,
"category": repository.data.category, "category": repository.data.category,
"description": repository.data.description, "description": repository.data.description,
"downloads": repository.releases.downloads, "domain": repository.data.domain,
"downloads": repository.data.downloads,
"full_name": repository.data.full_name, "full_name": repository.data.full_name,
"first_install": repository.status.first_install, "first_install": repository.status.first_install,
"hide": repository.status.hide, "installed_commit": repository.data.installed_commit,
"installed_commit": repository.versions.installed_commit, "installed": repository.data.installed,
"installed": repository.status.installed, "last_commit": repository.data.last_commit,
"last_commit": repository.versions.available_commit, "last_release_tag": repository.data.last_version,
"last_release_tag": repository.versions.available, "last_updated": repository.data.last_updated,
"last_updated": repository.information.last_updated,
"name": repository.data.name, "name": repository.data.name,
"new": repository.status.new, "new": repository.data.new,
"repository_manifest": repository_manifest, "repository_manifest": repository_manifest,
"selected_tag": repository.status.selected_tag, "selected_tag": repository.data.selected_tag,
"show_beta": repository.status.show_beta, "show_beta": repository.data.show_beta,
"stars": repository.data.stargazers_count, "stars": repository.data.stargazers_count,
"topics": repository.data.topics, "topics": repository.data.topics,
"version_installed": repository.versions.installed, "version_installed": repository.data.installed_version,
} }
if data:
if repository.data.installed and (
repository.data.installed_commit
or repository.data.installed_version
):
await async_save_to_store(
self.hacs.hass,
f"hacs/{repository.data.id}.hacs",
repository.data.to_json(),
)
content[str(repository.data.id)] = data
await async_save_to_store(self.hacs.hass, "repositories", content) await async_save_to_store(self.hacs.hass, "repositories", content)
self.hacs.hass.bus.async_fire("hacs/repository", {}) self.hacs.hass.bus.async_fire("hacs/repository", {})
@ -79,6 +90,7 @@ class HacsData:
self.hacs.system.status.new = True self.hacs.system.status.new = True
return True return True
self.logger.info("Restore started") self.logger.info("Restore started")
self.hacs.system.status.new = False
# Hacs # Hacs
self.hacs.configuration.frontend_mode = hacs.get("view", "Grid") self.hacs.configuration.frontend_mode = hacs.get("view", "Grid")
@ -88,21 +100,40 @@ class HacsData:
# Repositories # Repositories
for entry in repositories: for entry in repositories:
repo = repositories[entry] repo = repositories[entry]
if not self.hacs.is_known(repo["full_name"]): if not self.hacs.is_known(entry):
await register_repository( await register_repository(
repo["full_name"], repo["category"], False repo["full_name"], repo["category"], False
) )
repository = self.hacs.get_by_name(repo["full_name"]) repository = [
if repository is None: x
self.logger.error(f"Did not find {repo['full_name']}") for x in self.hacs.repositories
if str(x.data.id) == str(entry)
or x.data.full_name == repo["full_name"]
]
if not repository:
self.logger.error(f"Did not find {repo['full_name']} ({entry})")
continue continue
repository = repository[0]
# Restore repository attributes # Restore repository attributes
repository.information.uid = entry repository.data.id = entry
await self.hacs.hass.async_add_executor_job( await self.hacs.hass.async_add_executor_job(
restore_repository_data, repository, repo restore_repository_data, repository, repo
) )
restored = await async_load_from_store(
self.hacs.hass, f"hacs/{entry}.hacs"
)
if restored:
repository.data.update_data(restored)
if not repository.data.installed:
repository.logger.debug(
"Should be installed but is not... Fixing that!"
)
repository.data.installed = True
self.logger.info("Restore done") self.logger.info("Restore done")
except Exception as exception: # pylint: disable=broad-except except Exception as exception: # pylint: disable=broad-except
self.logger.critical(f"[{exception}] Restore Failed!") self.logger.critical(f"[{exception}] Restore Failed!")
@ -117,27 +148,28 @@ def restore_repository_data(
repository.data.authors = repository_data.get("authors", []) repository.data.authors = repository_data.get("authors", [])
repository.data.description = repository_data.get("description") repository.data.description = repository_data.get("description")
repository.releases.last_release_object_downloads = repository_data.get("downloads") repository.releases.last_release_object_downloads = repository_data.get("downloads")
repository.information.last_updated = repository_data.get("last_updated") repository.data.last_updated = repository_data.get("last_updated")
repository.data.topics = repository_data.get("topics", []) repository.data.topics = repository_data.get("topics", [])
repository.data.domain = repository_data.get("domain", None)
repository.data.stargazers_count = repository_data.get("stars", 0) repository.data.stargazers_count = repository_data.get("stars", 0)
repository.releases.last_release = repository_data.get("last_release_tag") repository.releases.last_release = repository_data.get("last_release_tag")
repository.status.hide = repository_data.get("hide", False) repository.data.hide = repository_data.get("hide", False)
repository.status.installed = repository_data.get("installed", False) repository.data.installed = repository_data.get("installed", False)
repository.status.new = repository_data.get("new", True) repository.data.new = repository_data.get("new", True)
repository.status.selected_tag = repository_data.get("selected_tag") repository.data.selected_tag = repository_data.get("selected_tag")
repository.status.show_beta = repository_data.get("show_beta", False) repository.data.show_beta = repository_data.get("show_beta", False)
repository.versions.available = repository_data.get("last_release_tag") repository.data.last_version = repository_data.get("last_release_tag")
repository.versions.available_commit = repository_data.get("last_commit") repository.data.last_commit = repository_data.get("last_commit")
repository.versions.installed = repository_data.get("version_installed") repository.data.installed_version = repository_data.get("version_installed")
repository.versions.installed_commit = repository_data.get("installed_commit") repository.data.installed_commit = repository_data.get("installed_commit")
repository.repository_manifest = HacsManifest.from_dict( repository.repository_manifest = HacsManifest.from_dict(
repository_data.get("repository_manifest", {}) repository_data.get("repository_manifest", {})
) )
if repository.status.installed: if repository.data.installed:
repository.status.first_install = False repository.status.first_install = False
if repository_data["full_name"] == "hacs/integration": if repository_data["full_name"] == "hacs/integration":
repository.versions.installed = VERSION repository.data.installed_version = VERSION
repository.status.installed = True repository.data.installed = True

View File

@ -3,7 +3,7 @@ import logging
import time import time
from datetime import timedelta from datetime import timedelta
import asyncio import asyncio
from aiogithubapi import AIOGitHubException from aiogithubapi import AIOGitHubAPIException
from custom_components.hacs.hacsbase.exceptions import HacsException from custom_components.hacs.hacsbase.exceptions import HacsException
from custom_components.hacs.helpers.register_repository import register_repository from custom_components.hacs.helpers.register_repository import register_repository
@ -48,7 +48,7 @@ class HacsTaskFactory:
async with max_concurrent_tasks: async with max_concurrent_tasks:
try: try:
await repository.common_update() await repository.common_update()
except (AIOGitHubException, HacsException) as exception: except (AIOGitHubAPIException, HacsException) as exception:
logger.error("%s - %s", repository.data.full_name, exception) logger.error("%s - %s", repository.data.full_name, exception)
# Due to GitHub ratelimits we need to sleep a bit # Due to GitHub ratelimits we need to sleep a bit
@ -58,7 +58,7 @@ class HacsTaskFactory:
async with max_concurrent_tasks: async with max_concurrent_tasks:
try: try:
await repository.update_repository() await repository.update_repository()
except (AIOGitHubException, HacsException) as exception: except (AIOGitHubAPIException, HacsException) as exception:
logger.error("%s - %s", repository.data.full_name, exception) logger.error("%s - %s", repository.data.full_name, exception)
# Due to GitHub ratelimits we need to sleep a bit # Due to GitHub ratelimits we need to sleep a bit
@ -68,7 +68,7 @@ class HacsTaskFactory:
async with max_concurrent_tasks: async with max_concurrent_tasks:
try: try:
await register_repository(repo, category) await register_repository(repo, category)
except (AIOGitHubException, HacsException) as exception: except (AIOGitHubAPIException, HacsException) as exception:
logger.error("%s - %s", repo, exception) logger.error("%s - %s", repo, exception)
# Due to GitHub ratelimits we need to sleep a bit # Due to GitHub ratelimits we need to sleep a bit

View File

@ -16,10 +16,10 @@ def render_template(content, context):
try: try:
render = Template(content) render = Template(content)
render = render.render( render = render.render(
installed=context.status.installed, installed=context.data.installed,
pending_update=context.pending_upgrade, pending_update=context.pending_upgrade,
prerelease=prerelease, prerelease=prerelease,
selected_tag=context.status.selected_tag, selected_tag=context.data.selected_tag,
version_available=context.releases.last_release, version_available=context.releases.last_release,
version_installed=context.display_installed_version, version_installed=context.display_installed_version,
) )

View File

@ -26,7 +26,7 @@ def should_try_releases(repository):
return False return False
if repository.data.category not in ["plugin", "theme"]: if repository.data.category not in ["plugin", "theme"]:
return False return False
if not repository.releases.releases: if not repository.data.releases:
return False return False
return True return True

View File

@ -1,11 +1,12 @@
"""Helpers to get default repositories.""" """Helpers to get default repositories."""
import json import json
from aiogithubapi import AIOGitHub, AIOGitHubException from aiogithubapi import GitHub, AIOGitHubAPIException
from integrationhelper import Logger from integrationhelper import Logger
from custom_components.hacs.hacsbase.exceptions import HacsException
from custom_components.hacs.helpers.information import get_repository from custom_components.hacs.helpers.information import get_repository
async def get_default_repos_orgs(github: type(AIOGitHub), category: str) -> dict: async def get_default_repos_orgs(github: type(GitHub), category: str) -> dict:
"""Gets default org repositories.""" """Gets default org repositories."""
repositories = [] repositories = []
logger = Logger("hacs") logger = Logger("hacs")
@ -22,7 +23,7 @@ async def get_default_repos_orgs(github: type(AIOGitHub), category: str) -> dict
for repo in repos: for repo in repos:
repositories.append(repo.full_name) repositories.append(repo.full_name)
except AIOGitHubException as exception: except AIOGitHubAPIException as exception:
logger.error(exception) logger.error(exception)
return repositories return repositories
@ -38,7 +39,7 @@ async def get_default_repos_lists(session, token, default: str) -> dict:
content = await repo.get_contents(default) content = await repo.get_contents(default)
repositories = json.loads(content.content) repositories = json.loads(content.content)
except AIOGitHubException as exception: except (AIOGitHubAPIException, HacsException) as exception:
logger.error(exception) logger.error(exception)
return repositories return repositories

View File

@ -1,6 +1,6 @@
"""Return repository information if any.""" """Return repository information if any."""
import json import json
from aiogithubapi import AIOGitHubException, AIOGitHub from aiogithubapi import AIOGitHubAPIException, GitHub
from custom_components.hacs.handler.template import render_template from custom_components.hacs.handler.template import render_template
from custom_components.hacs.hacsbase.exceptions import HacsException from custom_components.hacs.hacsbase.exceptions import HacsException
@ -29,7 +29,7 @@ async def get_info_md_content(repository):
return "" return ""
info = info.content.replace("<svg", "<disabled").replace("</svg", "</disabled") info = info.content.replace("<svg", "<disabled").replace("</svg", "</disabled")
return render_template(info, repository) return render_template(info, repository)
except (AIOGitHubException, Exception): # pylint: disable=broad-except except (AIOGitHubAPIException, Exception): # pylint: disable=broad-except
if repository.hacs.action: if repository.hacs.action:
raise HacsException("No info file found") raise HacsException("No info file found")
return "" return ""
@ -38,10 +38,10 @@ async def get_info_md_content(repository):
async def get_repository(session, token, repository_full_name): async def get_repository(session, token, repository_full_name):
"""Return a repository object or None.""" """Return a repository object or None."""
try: try:
github = AIOGitHub(token, session) github = GitHub(token, session)
repository = await github.get_repo(repository_full_name) repository = await github.get_repo(repository_full_name)
return repository return repository
except AIOGitHubException as exception: except (AIOGitHubAPIException, Exception) as exception:
raise HacsException(exception) raise HacsException(exception)
@ -50,7 +50,7 @@ async def get_tree(repository, ref):
try: try:
tree = await repository.get_tree(ref) tree = await repository.get_tree(ref)
return tree return tree
except AIOGitHubException as exception: except AIOGitHubAPIException as exception:
raise HacsException(exception) raise HacsException(exception)
@ -59,7 +59,7 @@ async def get_releases(repository, prerelease=False, returnlimit=5):
try: try:
releases = await repository.get_releases(prerelease, returnlimit) releases = await repository.get_releases(prerelease, returnlimit)
return releases return releases
except AIOGitHubException as exception: except AIOGitHubAPIException as exception:
raise HacsException(exception) raise HacsException(exception)
@ -84,6 +84,7 @@ async def get_integration_manifest(repository):
repository.data.authors = manifest["codeowners"] repository.data.authors = manifest["codeowners"]
repository.data.domain = manifest["domain"] repository.data.domain = manifest["domain"]
repository.data.manifest_name = manifest["name"] repository.data.manifest_name = manifest["name"]
repository.data.config_flow = manifest.get("config_flow", False)
if repository.hacs.action: if repository.hacs.action:
if manifest.get("documentation") is None: if manifest.get("documentation") is None:
@ -115,6 +116,10 @@ def find_file_name(repository):
elif repository.data.category == "python_script": elif repository.data.category == "python_script":
get_file_name_python_script(repository) get_file_name_python_script(repository)
if repository.hacs.action:
repository.logger.info(f"filename {repository.data.file_name}")
repository.logger.info(f"location {repository.content.path.remote}")
def get_file_name_plugin(repository): def get_file_name_plugin(repository):
"""Get the filename to target.""" """Get the filename to target."""

View File

@ -26,7 +26,7 @@ async def install_repository(repository):
else: else:
repository.ref = f"tags/{version}" repository.ref = f"tags/{version}"
if repository.status.installed and repository.data.category == "netdaemon": if repository.data.installed and repository.data.category == "netdaemon":
persistent_directory = BackupNetDaemon(repository) persistent_directory = BackupNetDaemon(repository)
persistent_directory.create() persistent_directory.create()
@ -40,7 +40,7 @@ async def install_repository(repository):
) )
persistent_directory.create() persistent_directory.create()
if repository.status.installed and not repository.content.single: if repository.data.installed and not repository.content.single:
backup = Backup(repository.content.path.local) backup = Backup(repository.content.path.local)
backup.create() backup.create()
@ -52,10 +52,10 @@ async def install_repository(repository):
if repository.validate.errors: if repository.validate.errors:
for error in repository.validate.errors: for error in repository.validate.errors:
repository.logger.error(error) repository.logger.error(error)
if repository.status.installed and not repository.content.single: if repository.data.installed and not repository.content.single:
backup.restore() backup.restore()
if repository.status.installed and not repository.content.single: if repository.data.installed and not repository.content.single:
backup.cleanup() backup.cleanup()
if persistent_directory is not None: if persistent_directory is not None:
@ -66,13 +66,13 @@ async def install_repository(repository):
if repository.data.full_name not in repository.hacs.common.installed: if repository.data.full_name not in repository.hacs.common.installed:
if repository.data.full_name == "hacs/integration": if repository.data.full_name == "hacs/integration":
repository.hacs.common.installed.append(repository.data.full_name) repository.hacs.common.installed.append(repository.data.full_name)
repository.status.installed = True repository.data.installed = True
repository.versions.installed_commit = repository.versions.available_commit repository.data.installed_commit = repository.data.last_commit
if version == repository.data.default_branch: if version == repository.data.default_branch:
repository.versions.installed = None repository.data.installed_version = None
else: else:
repository.versions.installed = version repository.data.installed_version = version
await reload_after_install(repository) await reload_after_install(repository)
installation_complete(repository) installation_complete(repository)
@ -81,7 +81,7 @@ async def install_repository(repository):
async def reload_after_install(repository): async def reload_after_install(repository):
"""Reload action after installation success.""" """Reload action after installation success."""
if repository.data.category == "integration": if repository.data.category == "integration":
if repository.config_flow: if repository.data.config_flow:
if repository.data.full_name != "hacs/integration": if repository.data.full_name != "hacs/integration":
await repository.reload_custom_components() await repository.reload_custom_components()
repository.pending_restart = True repository.pending_restart = True
@ -113,18 +113,18 @@ def installation_complete(repository):
def version_to_install(repository): def version_to_install(repository):
"""Determine which version to isntall.""" """Determine which version to isntall."""
if repository.versions.available is not None: if repository.data.last_version is not None:
if repository.status.selected_tag is not None: if repository.data.selected_tag is not None:
if repository.status.selected_tag == repository.versions.available: if repository.data.selected_tag == repository.data.last_version:
repository.status.selected_tag = None repository.data.selected_tag = None
return repository.versions.available return repository.data.last_version
return repository.status.selected_tag return repository.data.selected_tag
return repository.versions.available return repository.data.last_version
if repository.status.selected_tag is not None: if repository.data.selected_tag is not None:
if repository.status.selected_tag == repository.data.default_branch: if repository.data.selected_tag == repository.data.default_branch:
return repository.data.default_branch return repository.data.default_branch
if repository.status.selected_tag in repository.releases.published_tags: if repository.data.selected_tag in repository.data.published_tags:
return repository.status.selected_tag return repository.data.selected_tag
if repository.data.default_branch is None: if repository.data.default_branch is None:
return "master" return "master"
return repository.data.default_branch return repository.data.default_branch

View File

@ -1,5 +1,5 @@
"""Register a repository.""" """Register a repository."""
from aiogithubapi import AIOGitHubException from aiogithubapi import AIOGitHubAPIException
from custom_components.hacs.globals import get_hacs from custom_components.hacs.globals import get_hacs
from custom_components.hacs.hacsbase.exceptions import ( from custom_components.hacs.hacsbase.exceptions import (
HacsException, HacsException,
@ -29,7 +29,7 @@ async def register_repository(full_name, category, check=True, ref=None, action=
try: try:
await repository.registration(ref) await repository.registration(ref)
if hacs.system.status.new: if hacs.system.status.new:
repository.status.new = False repository.data.new = False
if repository.validate.errors: if repository.validate.errors:
hacs.common.skip.append(repository.data.full_name) hacs.common.skip.append(repository.data.full_name)
if not hacs.system.status.startup: if not hacs.system.status.startup:
@ -41,18 +41,29 @@ async def register_repository(full_name, category, check=True, ref=None, action=
repository.logger.info("Validation complete") repository.logger.info("Validation complete")
else: else:
repository.logger.info("Registration complete") repository.logger.info("Registration complete")
except AIOGitHubException as exception: except AIOGitHubAPIException as exception:
hacs.common.skip.append(repository.data.full_name) hacs.common.skip.append(repository.data.full_name)
raise HacsException(f"Validation for {full_name} failed with {exception}.") raise HacsException(f"Validation for {full_name} failed with {exception}.")
if hacs.hass is not None: exists = (
hacs.hass.bus.async_fire( False
"hacs/repository", if str(repository.data.id) == "0"
{ else [x for x in hacs.repositories if str(x.data.id) == str(repository.data.id)]
"id": 1337, )
"action": "registration",
"repository": repository.data.full_name, if exists:
"repository_id": repository.information.uid, if exists[0] in hacs.repositories:
}, hacs.repositories.remove(exists[0])
)
else:
if hacs.hass is not None:
hacs.hass.bus.async_fire(
"hacs/repository",
{
"id": 1337,
"action": "registration",
"repository": repository.data.full_name,
"repository_id": repository.data.id,
},
)
hacs.repositories.append(repository) hacs.repositories.append(repository)

View File

@ -1,5 +1,5 @@
"""Helper to do common validation for repositories.""" """Helper to do common validation for repositories."""
from aiogithubapi import AIOGitHubException from aiogithubapi import AIOGitHubAPIException
from custom_components.hacs.globals import get_hacs, is_removed from custom_components.hacs.globals import get_hacs, is_removed
from custom_components.hacs.hacsbase.exceptions import HacsException from custom_components.hacs.hacsbase.exceptions import HacsException
from custom_components.hacs.helpers.install import version_to_install from custom_components.hacs.helpers.install import version_to_install
@ -10,19 +10,19 @@ from custom_components.hacs.helpers.information import (
) )
async def common_validate(repository): async def common_validate(repository, ignore_issues=False):
"""Common validation steps of the repository.""" """Common validation steps of the repository."""
repository.validate.errors = [] repository.validate.errors = []
# Make sure the repository exist. # Make sure the repository exist.
repository.logger.debug("Checking repository.") repository.logger.debug("Checking repository.")
await common_update_data(repository) await common_update_data(repository, ignore_issues)
# Step 6: Get the content of hacs.json # Step 6: Get the content of hacs.json
await repository.get_repository_manifest_content() await repository.get_repository_manifest_content()
async def common_update_data(repository): async def common_update_data(repository, ignore_issues=False):
"""Common update data.""" """Common update data."""
hacs = get_hacs() hacs = get_hacs()
try: try:
@ -31,19 +31,19 @@ async def common_update_data(repository):
) )
repository.repository_object = repository_object repository.repository_object = repository_object
repository.data.update_data(repository_object.attributes) repository.data.update_data(repository_object.attributes)
except (AIOGitHubException, HacsException) as exception: except (AIOGitHubAPIException, HacsException) as exception:
if not hacs.system.status.startup: if not hacs.system.status.startup:
repository.logger.error(exception) repository.logger.error(exception)
repository.validate.errors.append("Repository does not exist.") repository.validate.errors.append("Repository does not exist.")
raise HacsException(exception) raise HacsException(exception)
# Make sure the repository is not archived. # Make sure the repository is not archived.
if repository.data.archived: if repository.data.archived and not ignore_issues:
repository.validate.errors.append("Repository is archived.") repository.validate.errors.append("Repository is archived.")
raise HacsException("Repository is archived.") raise HacsException("Repository is archived.")
# Make sure the repository is not in the blacklist. # Make sure the repository is not in the blacklist.
if is_removed(repository.data.full_name): if is_removed(repository.data.full_name) and not ignore_issues:
repository.validate.errors.append("Repository is in the blacklist.") repository.validate.errors.append("Repository is in the blacklist.")
raise HacsException("Repository is in the blacklist.") raise HacsException("Repository is in the blacklist.")
@ -51,29 +51,29 @@ async def common_update_data(repository):
try: try:
releases = await get_releases( releases = await get_releases(
repository.repository_object, repository.repository_object,
repository.status.show_beta, repository.data.show_beta,
hacs.configuration.release_limit, hacs.configuration.release_limit,
) )
if releases: if releases:
repository.releases.releases = True repository.data.releases = True
repository.releases.objects = releases repository.releases.objects = releases
repository.releases.published_tags = [ repository.data.published_tags = [
x.tag_name for x in releases if not x.draft x.tag_name for x in releases if not x.draft
] ]
repository.versions.available = next(iter(releases)).tag_name repository.data.last_version = next(iter(releases)).tag_name
except (AIOGitHubException, HacsException): except (AIOGitHubAPIException, HacsException):
repository.releases.releases = False repository.data.releases = False
if not repository.force_branch: if not repository.force_branch:
repository.ref = version_to_install(repository) repository.ref = version_to_install(repository)
if repository.releases.releases: if repository.data.releases:
for release in releases: for release in releases:
if release.tag_name == repository.ref: if release.tag_name == repository.ref:
assets = release.assets assets = release.assets
if assets: if assets:
downloads = next(iter(assets)).attributes.get("download_count") downloads = next(iter(assets)).attributes.get("download_count")
repository.releases.downloads = downloads repository.data.downloads = downloads
repository.logger.debug( repository.logger.debug(
f"Running checks against {repository.ref.replace('tags/', '')}" f"Running checks against {repository.ref.replace('tags/', '')}"
@ -86,7 +86,7 @@ async def common_update_data(repository):
repository.treefiles = [] repository.treefiles = []
for treefile in repository.tree: for treefile in repository.tree:
repository.treefiles.append(treefile.full_path) repository.treefiles.append(treefile.full_path)
except (AIOGitHubException, HacsException) as exception: except (AIOGitHubAPIException, HacsException) as exception:
if not hacs.system.status.startup: if not hacs.system.status.startup:
repository.logger.error(exception) repository.logger.error(exception)
raise HacsException(exception) raise HacsException(exception)

View File

@ -20,24 +20,6 @@ class HacsFrontend(HomeAssistantView):
return await get_file_response(requested_file) return await get_file_response(requested_file)
class HacsPluginViewLegacy(HacsFrontend):
"""Alias for legacy, remove with 1.0"""
name = "community_plugin"
url = r"/community_plugin/{requested_file:.+}"
async def get(self, request, requested_file): # pylint: disable=unused-argument
"""DEPRECATED."""
hacs = get_hacs()
if hacs.system.ha_version.split(".")[1] >= "107":
logger = Logger("hacs.deprecated")
logger.warning(
"The '/community_plugin/*' is deprecated and will be removed in an upcoming version of HACS, it has been replaced by '/hacsfiles/*', if you use the UI to manage your lovelace configuration, you can update this by going to the settings tab in HACS, if you use YAML to manage your lovelace configuration, you manually need to replace the URL in your resources."
)
return await get_file_response(requested_file)
async def get_file_response(requested_file): async def get_file_response(requested_file):
"""Get file.""" """Get file."""
hacs = get_hacs() hacs = get_hacs()

View File

@ -1,20 +1,7 @@
const hacsIconPath = "m 20.064849,22.306912 c -0.0319,0.369835 -0.280561,0.707789 -0.656773,0.918212 -0.280572,0.153036 -0.605773,0.229553 -0.950094,0.229553 -0.0765,0 -0.146661,-0.0064 -0.216801,-0.01275 -0.605774,-0.05739 -1.135016,-0.344329 -1.402827,-0.7588 l 0.784304,-0.516495 c 0.0893,0.146659 0.344331,0.312448 0.707793,0.34433 0.235931,0.02551 0.471852,-0.01913 0.637643,-0.108401 0.101998,-0.05101 0.172171,-0.127529 0.17854,-0.191295 0.0065,-0.08289 -0.0255,-0.369835 -0.733293,-0.439975 -1.013854,-0.09565 -1.645127,-0.688661 -1.568606,-1.460214 0.0319,-0.382589 0.280561,-0.714165 0.663153,-0.930965 0.331571,-0.172165 0.752423,-0.25506 1.166895,-0.210424 0.599382,0.05739 1.128635,0.344329 1.402816,0.7588 l -0.784304,0.510118 c -0.0893,-0.140282 -0.344331,-0.299694 -0.707782,-0.331576 -0.235932,-0.02551 -0.471863,0.01913 -0.637654,0.10202 -0.0956,0.05739 -0.165791,0.133906 -0.17216,0.191295 -0.0255,0.293317 0.465482,0.420847 0.726913,0.439976 v 0.0064 c 1.020234,0.09565 1.638757,0.66953 1.562237,1.460213 z m -7.466854,-0.988354 c 0,-1.192401 0.962855,-2.155249 2.15525,-2.155249 0.599393,0 1.179645,0.25506 1.594117,0.707789 l -0.695033,0.624895 c -0.235931,-0.25506 -0.561133,-0.401718 -0.899084,-0.401718 -0.675903,0 -1.217906,0.542 -1.217906,1.217906 0,0.66953 0.542003,1.217908 1.217906,1.217908 0.337951,0 0.663153,-0.140283 0.899084,-0.401718 l 0.695033,0.631271 c -0.414472,0.452729 -0.988355,0.707788 -1.594117,0.707788 -1.192395,0 -2.15525,-0.969224 -2.15525,-2.148872 z M 8.6573365,23.461054 10.353474,19.14418 h 0.624893 l 1.568618,4.316874 H 11.52037 L 11.265308,22.734136 H 9.964513 l -0.274192,0.726918 z m 1.6833885,-1.68339 h 0.580263 L 10.646796,21.012487 Z M 8.1089536,19.156932 v 4.297745 H 7.1461095 v -1.645131 h -1.606867 v 1.645131 H 4.5763876 v -4.297745 h 0.9628549 v 1.696143 h 1.606867 V 19.156932 Z M 20.115859,4.2997436 C 20.090359,4.159461 19.969198,4.0574375 19.822548,4.0574375 H 14.141102 10.506516 4.8250686 c -0.14665,0 -0.2678112,0.1020202 -0.2933108,0.2423061 L 3.690064,8.8461703 c -0.00651,0.01913 -0.00651,0.03826 -0.00651,0.057391 v 1.5239797 c 0,0.165789 0.133911,0.299694 0.2996911,0.299694 H 4.5762579 20.0711 20.664112 c 0.165781,0 0.299691,-0.133905 0.299691,-0.299694 V 8.8971848 c 0,-0.01913 0,-0.03826 -0.0065,-0.05739 z M 4.5763876,17.358767 c 0,0.184917 0.1466608,0.331577 0.3315819,0.331577 h 5.5985465 3.634586 0.924594 c 0.184911,0 0.331571,-0.14666 0.331571,-0.331577 v -4.744098 c 0,-0.184918 0.146661,-0.331577 0.331582,-0.331577 h 2.894913 c 0.184921,0 0.331582,0.146659 0.331582,0.331577 v 4.744098 c 0,0.184917 0.146661,0.331577 0.331571,0.331577 h 0.446363 c 0.18491,0 0.331571,-0.14666 0.331571,-0.331577 v -5.636804 c 0,-0.184918 -0.146661,-0.331577 -0.331571,-0.331577 H 4.9079695 c -0.1849211,0 -0.3315819,0.146659 -0.3315819,0.331577 z m 1.6578879,-4.852498 h 5.6495565 c 0.15303,0 0.280561,0.12753 0.280561,0.280564 v 3.513438 c 0,0.153036 -0.127531,0.280566 -0.280561,0.280566 H 6.2342755 c -0.1530412,0 -0.2805719,-0.12753 -0.2805719,-0.280566 v -3.513438 c 0,-0.159411 0.1275307,-0.280564 0.2805719,-0.280564 z M 19.790657,3.3879075 H 4.8569594 c -0.1530412,0 -0.2805718,-0.1275296 -0.2805718,-0.2805642 V 1.3665653 C 4.5763876,1.2135296 4.7039182,1.086 4.8569594,1.086 H 19.790657 c 0.153041,0 0.280572,0.1275296 0.280572,0.2805653 v 1.740778 c 0,0.1530346 -0.127531,0.2805642 -0.280572,0.2805642 z" window.customIconsets = window.customIconsets || {};
const haIconSet = window.customElements.get("ha-iconset-svg") window.customIconsets["hacs"] = async () => {
return {
if (haIconSet === undefined) { path:
window.customIconsets = window.customIconsets || {} "m 20.064849,22.306912 c -0.0319,0.369835 -0.280561,0.707789 -0.656773,0.918212 -0.280572,0.153036 -0.605773,0.229553 -0.950094,0.229553 -0.0765,0 -0.146661,-0.0064 -0.216801,-0.01275 -0.605774,-0.05739 -1.135016,-0.344329 -1.402827,-0.7588 l 0.784304,-0.516495 c 0.0893,0.146659 0.344331,0.312448 0.707793,0.34433 0.235931,0.02551 0.471852,-0.01913 0.637643,-0.108401 0.101998,-0.05101 0.172171,-0.127529 0.17854,-0.191295 0.0065,-0.08289 -0.0255,-0.369835 -0.733293,-0.439975 -1.013854,-0.09565 -1.645127,-0.688661 -1.568606,-1.460214 0.0319,-0.382589 0.280561,-0.714165 0.663153,-0.930965 0.331571,-0.172165 0.752423,-0.25506 1.166895,-0.210424 0.599382,0.05739 1.128635,0.344329 1.402816,0.7588 l -0.784304,0.510118 c -0.0893,-0.140282 -0.344331,-0.299694 -0.707782,-0.331576 -0.235932,-0.02551 -0.471863,0.01913 -0.637654,0.10202 -0.0956,0.05739 -0.165791,0.133906 -0.17216,0.191295 -0.0255,0.293317 0.465482,0.420847 0.726913,0.439976 v 0.0064 c 1.020234,0.09565 1.638757,0.66953 1.562237,1.460213 z m -7.466854,-0.988354 c 0,-1.192401 0.962855,-2.155249 2.15525,-2.155249 0.599393,0 1.179645,0.25506 1.594117,0.707789 l -0.695033,0.624895 c -0.235931,-0.25506 -0.561133,-0.401718 -0.899084,-0.401718 -0.675903,0 -1.217906,0.542 -1.217906,1.217906 0,0.66953 0.542003,1.217908 1.217906,1.217908 0.337951,0 0.663153,-0.140283 0.899084,-0.401718 l 0.695033,0.631271 c -0.414472,0.452729 -0.988355,0.707788 -1.594117,0.707788 -1.192395,0 -2.15525,-0.969224 -2.15525,-2.148872 z M 8.6573365,23.461054 10.353474,19.14418 h 0.624893 l 1.568618,4.316874 H 11.52037 L 11.265308,22.734136 H 9.964513 l -0.274192,0.726918 z m 1.6833885,-1.68339 h 0.580263 L 10.646796,21.012487 Z M 8.1089536,19.156932 v 4.297745 H 7.1461095 v -1.645131 h -1.606867 v 1.645131 H 4.5763876 v -4.297745 h 0.9628549 v 1.696143 h 1.606867 V 19.156932 Z M 20.115859,4.2997436 C 20.090359,4.159461 19.969198,4.0574375 19.822548,4.0574375 H 14.141102 10.506516 4.8250686 c -0.14665,0 -0.2678112,0.1020202 -0.2933108,0.2423061 L 3.690064,8.8461703 c -0.00651,0.01913 -0.00651,0.03826 -0.00651,0.057391 v 1.5239797 c 0,0.165789 0.133911,0.299694 0.2996911,0.299694 H 4.5762579 20.0711 20.664112 c 0.165781,0 0.299691,-0.133905 0.299691,-0.299694 V 8.8971848 c 0,-0.01913 0,-0.03826 -0.0065,-0.05739 z M 4.5763876,17.358767 c 0,0.184917 0.1466608,0.331577 0.3315819,0.331577 h 5.5985465 3.634586 0.924594 c 0.184911,0 0.331571,-0.14666 0.331571,-0.331577 v -4.744098 c 0,-0.184918 0.146661,-0.331577 0.331582,-0.331577 h 2.894913 c 0.184921,0 0.331582,0.146659 0.331582,0.331577 v 4.744098 c 0,0.184917 0.146661,0.331577 0.331571,0.331577 h 0.446363 c 0.18491,0 0.331571,-0.14666 0.331571,-0.331577 v -5.636804 c 0,-0.184918 -0.146661,-0.331577 -0.331571,-0.331577 H 4.9079695 c -0.1849211,0 -0.3315819,0.146659 -0.3315819,0.331577 z m 1.6578879,-4.852498 h 5.6495565 c 0.15303,0 0.280561,0.12753 0.280561,0.280564 v 3.513438 c 0,0.153036 -0.127531,0.280566 -0.280561,0.280566 H 6.2342755 c -0.1530412,0 -0.2805719,-0.12753 -0.2805719,-0.280566 v -3.513438 c 0,-0.159411 0.1275307,-0.280564 0.2805719,-0.280564 z M 19.790657,3.3879075 H 4.8569594 c -0.1530412,0 -0.2805718,-0.1275296 -0.2805718,-0.2805642 V 1.3665653 C 4.5763876,1.2135296 4.7039182,1.086 4.8569594,1.086 H 19.790657 c 0.153041,0 0.280572,0.1275296 0.280572,0.2805653 v 1.740778 c 0,0.1530346 -0.127531,0.2805642 -0.280572,0.2805642 z",
window.customIconsets["hacs"] = async () => { return { hacs: { path: hacsIconPath } } } };
} else { };
const iconset = document.createElement("ha-iconset-svg");
iconset.name = "hacs";
iconset.size = "24";
iconset.innerHTML = `
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg height="24" width="24" viewBox="0 0 24 24" id="hacs" version="1.1">
<g>
<path style="stroke-width:0.0637648" d="${hacsIconPath}" id="path103" />
</g>
</svg>
`
document.body.appendChild(iconset);
}

View File

@ -1,24 +1,22 @@
{ {
"codeowners": [ "codeowners": ["@ludeeus"],
"@ludeeus" "config_flow": true,
], "dependencies": [
"config_flow": true, "websocket_api",
"dependencies": [ "frontend",
"websocket_api", "persistent_notification",
"frontend", "lovelace"
"persistent_notification", ],
"lovelace" "documentation": "https://hacs.xyz/docs/configuration/start",
], "domain": "hacs",
"documentation": "https://hacs.xyz/docs/configuration/start", "name": "HACS",
"domain": "hacs", "requirements": [
"name": "HACS (Home Assistant Community Store)", "aiofiles==0.5.0",
"requirements": [ "aiogithubapi==1.0.4",
"aiofiles==0.5.0", "backoff==1.10.0",
"aiogithubapi==0.5.0", "hacs_frontend==20200521162326",
"backoff==1.10.0", "integrationhelper==0.2.2",
"hacs_frontend==20200426112021", "semantic_version==2.8.5",
"integrationhelper==0.2.2", "queueman==0.5"
"semantic_version==2.8.5", ]
"queueman==0.5" }
]
}

View File

@ -1,5 +1,5 @@
"""Class for appdaemon apps in HACS.""" """Class for appdaemon apps in HACS."""
from aiogithubapi import AIOGitHubException from aiogithubapi import AIOGitHubAPIException
from integrationhelper import Logger from integrationhelper import Logger
from .repository import HacsRepository from .repository import HacsRepository
@ -30,7 +30,7 @@ class HacsAppdaemon(HacsRepository):
# Custom step 1: Validate content. # Custom step 1: Validate content.
try: try:
addir = await self.repository_object.get_contents("apps", self.ref) addir = await self.repository_object.get_contents("apps", self.ref)
except AIOGitHubException: except AIOGitHubAPIException:
raise HacsException( raise HacsException(
f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant" f"Repostitory structure for {self.ref.replace('tags/','')} is not compliant"
) )
@ -64,11 +64,9 @@ class HacsAppdaemon(HacsRepository):
# Set local path # Set local path
self.content.path.local = self.localpath self.content.path.local = self.localpath
async def update_repository(self): async def update_repository(self, ignore_issues=False):
"""Update.""" """Update."""
if self.hacs.github.ratelimits.remaining == 0: await self.common_update(ignore_issues)
return
await self.common_update()
# Get appdaemon objects. # Get appdaemon objects.
if self.repository_manifest: if self.repository_manifest:

View File

@ -74,11 +74,9 @@ class HacsIntegration(HacsRepository):
# Set local path # Set local path
self.content.path.local = self.localpath self.content.path.local = self.localpath
async def update_repository(self): async def update_repository(self, ignore_issues=False):
"""Update.""" """Update."""
if self.hacs.github.ratelimits.remaining == 0: await self.common_update(ignore_issues)
return
await self.common_update()
if self.data.content_in_root: if self.data.content_in_root:
self.content.path.remote = "" self.content.path.remote = ""

View File

@ -72,11 +72,9 @@ class HacsNetdaemon(HacsRepository):
# Set local path # Set local path
self.content.path.local = self.localpath self.content.path.local = self.localpath
async def update_repository(self): async def update_repository(self, ignore_issues=False):
"""Update.""" """Update."""
if self.hacs.github.ratelimits.remaining == 0: await self.common_update(ignore_issues)
return
await self.common_update()
# Get appdaemon objects. # Get appdaemon objects.
if self.repository_manifest: if self.repository_manifest:

View File

@ -57,19 +57,13 @@ class HacsPlugin(HacsRepository):
# Run common registration steps. # Run common registration steps.
await self.common_registration() await self.common_registration()
async def update_repository(self): async def update_repository(self, ignore_issues=False):
"""Update.""" """Update."""
if self.hacs.github.ratelimits.remaining == 0: await self.common_update(ignore_issues)
return
# Run common update steps.
await self.common_update()
# Get plugin objects. # Get plugin objects.
find_file_name(self) find_file_name(self)
# Get JS type
await self.parse_readme_for_jstype()
if self.content.path.remote is None: if self.content.path.remote is None:
self.validate.errors.append("Repostitory structure not compliant") self.validate.errors.append("Repostitory structure not compliant")
@ -86,25 +80,3 @@ class HacsPlugin(HacsRepository):
self.data.authors = package["author"] self.data.authors = package["author"]
except Exception: # pylint: disable=broad-except except Exception: # pylint: disable=broad-except
pass 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

View File

@ -63,12 +63,9 @@ class HacsPythonScript(HacsRepository):
# Set name # Set name
find_file_name(self) find_file_name(self)
async def update_repository(self): # lgtm[py/similar-function] async def update_repository(self, ignore_issues=False):
"""Update.""" """Update."""
if self.hacs.github.ratelimits.remaining == 0: await self.common_update(ignore_issues)
return
# Run common update steps.
await self.common_update()
# Get python_script objects. # Get python_script objects.
if self.data.content_in_root: if self.data.content_in_root:

View File

@ -5,7 +5,7 @@ import os
import tempfile import tempfile
import zipfile import zipfile
from integrationhelper import Validate from integrationhelper import Validate
from aiogithubapi import AIOGitHubException from aiogithubapi import AIOGitHubAPIException
from .manifest import HacsManifest from .manifest import HacsManifest
from ..helpers.misc import get_repository_name from ..helpers.misc import get_repository_name
from ..handler.download import async_download_file, async_save_file from ..handler.download import async_download_file, async_save_file
@ -13,6 +13,7 @@ from ..helpers.misc import version_left_higher_then_right
from ..helpers.install import install_repository, version_to_install from ..helpers.install import install_repository, version_to_install
from custom_components.hacs.hacsbase.exceptions import HacsException from custom_components.hacs.hacsbase.exceptions import HacsException
from custom_components.hacs.store import async_remove_store
from custom_components.hacs.globals import get_hacs from custom_components.hacs.globals import get_hacs
from custom_components.hacs.helpers.information import ( from custom_components.hacs.helpers.information import (
get_info_md_content, get_info_md_content,
@ -126,31 +127,22 @@ class HacsRepository:
"""Return pending upgrade.""" """Return pending upgrade."""
if not self.can_install: if not self.can_install:
return False return False
if self.status.installed: if self.data.installed:
if self.status.selected_tag is not None: if self.data.selected_tag is not None:
if self.status.selected_tag == self.data.default_branch: if self.data.selected_tag == self.data.default_branch:
if self.versions.installed_commit != self.versions.available_commit: if self.data.installed_commit != self.data.last_commit:
return True return True
return False return False
if self.display_installed_version != self.display_available_version: if self.display_installed_version != self.display_available_version:
return True return True
return False return False
@property
def config_flow(self):
"""Return bool if integration has config_flow."""
if self.integration_manifest:
if self.data.full_name == "hacs/integration":
return False
return self.integration_manifest.get("config_flow", False)
return False
@property @property
def custom(self): def custom(self):
"""Return flag if the repository is custom.""" """Return flag if the repository is custom."""
if self.data.full_name.split("/")[0] in ["custom-components", "custom-cards"]: if self.data.full_name.split("/")[0] in ["custom-components", "custom-cards"]:
return False return False
if self.data.full_name.lower() in [x.lower() for x in self.hacs.common.default]: if str(self.data.id) in [str(x) for x in self.hacs.common.default]:
return False return False
if self.data.full_name == "hacs/integration": if self.data.full_name == "hacs/integration":
return False return False
@ -160,14 +152,13 @@ class HacsRepository:
def can_install(self): def can_install(self):
"""Return bool if repository can be installed.""" """Return bool if repository can be installed."""
target = None target = None
if self.information.homeassistant_version is not None: if self.data.homeassistant is not None:
target = self.information.homeassistant_version target = self.data.homeassistant
if self.repository_manifest is not None: if self.data.homeassistant is not None:
if self.data.homeassistant is not None: target = self.data.homeassistant
target = self.data.homeassistant
if target is not None: if target is not None:
if self.releases.releases: if self.data.releases:
if not version_left_higher_then_right( if not version_left_higher_then_right(
self.hacs.system.ha_version, target self.hacs.system.ha_version, target
): ):
@ -182,13 +173,13 @@ class HacsRepository:
@property @property
def display_status(self): def display_status(self):
"""Return display_status.""" """Return display_status."""
if self.status.new: if self.data.new:
status = "new" status = "new"
elif self.pending_restart: elif self.pending_restart:
status = "pending-restart" status = "pending-restart"
elif self.pending_upgrade: elif self.pending_upgrade:
status = "pending-upgrade" status = "pending-upgrade"
elif self.status.installed: elif self.data.installed:
status = "installed" status = "installed"
else: else:
status = "default" status = "default"
@ -209,11 +200,11 @@ class HacsRepository:
@property @property
def display_installed_version(self): def display_installed_version(self):
"""Return display_authors""" """Return display_authors"""
if self.versions.installed is not None: if self.data.installed_version is not None:
installed = self.versions.installed installed = self.data.installed_version
else: else:
if self.versions.installed_commit is not None: if self.data.installed_commit is not None:
installed = self.versions.installed_commit installed = self.data.installed_commit
else: else:
installed = "" installed = ""
return installed return installed
@ -221,11 +212,11 @@ class HacsRepository:
@property @property
def display_available_version(self): def display_available_version(self):
"""Return display_authors""" """Return display_authors"""
if self.versions.available is not None: if self.data.last_version is not None:
available = self.versions.available available = self.data.last_version
else: else:
if self.versions.available_commit is not None: if self.data.last_commit is not None:
available = self.versions.available_commit available = self.data.last_commit
else: else:
available = "" available = ""
return available return available
@ -233,7 +224,7 @@ class HacsRepository:
@property @property
def display_version_or_commit(self): def display_version_or_commit(self):
"""Does the repositoriy use releases or commits?""" """Does the repositoriy use releases or commits?"""
if self.releases.releases: if self.data.releases:
version_or_commit = "version" version_or_commit = "version"
else: else:
version_or_commit = "commit" version_or_commit = "commit"
@ -251,9 +242,9 @@ class HacsRepository:
} }
return actions[self.display_status] return actions[self.display_status]
async def common_validate(self): async def common_validate(self, ignore_issues=False):
"""Common validation steps of the repository.""" """Common validation steps of the repository."""
await common_validate(self) await common_validate(self, ignore_issues)
async def common_registration(self): async def common_registration(self):
"""Common registration steps of the repository.""" """Common registration steps of the repository."""
@ -264,9 +255,6 @@ class HacsRepository:
) )
self.data.update_data(self.repository_object.attributes) self.data.update_data(self.repository_object.attributes)
# Set id
self.information.uid = str(self.data.id)
# Set topics # Set topics
self.data.topics = self.data.topics self.data.topics = self.data.topics
@ -280,21 +268,19 @@ class HacsRepository:
if self.data.description is None or len(self.data.description) == 0: if self.data.description is None or len(self.data.description) == 0:
raise HacsException("Missing repository description") raise HacsException("Missing repository description")
async def common_update(self): async def common_update(self, ignore_issues=False):
"""Common information update steps of the repository.""" """Common information update steps of the repository."""
self.logger.debug("Getting repository information") self.logger.debug("Getting repository information")
# Attach repository # Attach repository
await common_update_data(self) await common_update_data(self, ignore_issues)
# Update last updaeted # Update last updaeted
self.information.last_updated = self.repository_object.attributes.get( self.data.last_updated = self.repository_object.attributes.get("pushed_at", 0)
"pushed_at", 0
)
# Update last available commit # Update last available commit
await self.repository_object.set_last_commit() await self.repository_object.set_last_commit()
self.versions.available_commit = self.repository_object.last_commit self.data.last_commit = self.repository_object.last_commit
# Get the content of hacs.json # Get the content of hacs.json
await self.get_repository_manifest_content() await self.get_repository_manifest_content()
@ -357,30 +343,30 @@ class HacsRepository:
raise HacsException("No hacs.json file in the root of the repository.") raise HacsException("No hacs.json file in the root of the repository.")
return return
if self.hacs.action: if self.hacs.action:
self.logger.debug("Found hacs.json") self.logger.info("Found hacs.json")
self.ref = version_to_install(self)
if self.ref is None:
self.ref = version_to_install(self)
try: try:
manifest = await self.repository_object.get_contents("hacs.json", self.ref) manifest = await self.repository_object.get_contents("hacs.json", self.ref)
self.repository_manifest = HacsManifest.from_dict( self.repository_manifest = HacsManifest.from_dict(
json.loads(manifest.content) json.loads(manifest.content)
) )
self.data.update_data(json.loads(manifest.content)) self.data.update_data(json.loads(manifest.content))
except (AIOGitHubException, Exception) as exception: # Gotta Catch 'Em All except (AIOGitHubAPIException, Exception) as exception: # Gotta Catch 'Em All
if self.hacs.action: if self.hacs.action:
raise HacsException(f"hacs.json file is not valid ({exception}).") raise HacsException(f"hacs.json file is not valid ({exception}).")
if self.hacs.action: if self.hacs.action:
self.logger.debug("hacs.json is valid") self.logger.info("hacs.json is valid")
def remove(self): def remove(self):
"""Run remove tasks.""" """Run remove tasks."""
self.logger.info("Starting removal") self.logger.info("Starting removal")
if self.information.uid in self.hacs.common.installed: if self.data.id in self.hacs.common.installed:
self.hacs.common.installed.remove(self.information.uid) self.hacs.common.installed.remove(self.data.id)
for repository in self.hacs.repositories: for repository in self.hacs.repositories:
if repository.information.uid == self.information.uid: if repository.data.id == self.data.id:
self.hacs.repositories.remove(repository) self.hacs.repositories.remove(repository)
async def uninstall(self): async def uninstall(self):
@ -388,9 +374,9 @@ class HacsRepository:
self.logger.info("Uninstalling") self.logger.info("Uninstalling")
if not await self.remove_local_directory(): if not await self.remove_local_directory():
raise HacsException("Could not uninstall") raise HacsException("Could not uninstall")
self.status.installed = False self.data.installed = False
if self.data.category == "integration": if self.data.category == "integration":
if self.config_flow: if self.data.config_flow:
await self.reload_custom_components() await self.reload_custom_components()
else: else:
self.pending_restart = True self.pending_restart = True
@ -403,8 +389,11 @@ class HacsRepository:
pass pass
if self.data.full_name in self.hacs.common.installed: if self.data.full_name in self.hacs.common.installed:
self.hacs.common.installed.remove(self.data.full_name) self.hacs.common.installed.remove(self.data.full_name)
self.versions.installed = None
self.versions.installed_commit = None await async_remove_store(self.hacs.hass, f"hacs/{self.data.id}.hacs")
self.data.installed_version = None
self.data.installed_commit = None
self.hacs.hass.bus.async_fire( self.hacs.hass.bus.async_fire(
"hacs/repository", "hacs/repository",
{"id": 1337, "action": "uninstall", "repository": self.data.full_name}, {"id": 1337, "action": "uninstall", "repository": self.data.full_name},

View File

@ -8,32 +8,52 @@ import attr
class RepositoryData: class RepositoryData:
"""RepositoryData class.""" """RepositoryData class."""
id: int = 0
full_name: str = ""
pushed_at: str = ""
category: str = ""
archived: bool = False archived: bool = False
description: str = ""
manifest_name: str = None
topics: List[str] = []
fork: bool = False
domain: str = ""
default_branch: str = None
stargazers_count: int = 0
last_commit: str = ""
file_name: str = ""
content_in_root: bool = False
zip_release: bool = False
filename: str = ""
render_readme: bool = False
hide_default_branch: bool = False
domains: List[str] = []
country: List[str] = []
authors: List[str] = [] authors: List[str] = []
homeassistant: str = None # Minimum Home Assistant version category: str = ""
content_in_root: bool = False
country: List[str] = []
config_flow: bool = False
default_branch: str = None
description: str = ""
domain: str = ""
domains: List[str] = []
downloads: int = 0
file_name: str = ""
filename: str = ""
first_install: bool = False
fork: bool = False
full_name: str = ""
hacs: str = None # Minimum HACS version hacs: str = None # Minimum HACS version
persistent_directory: str = None hide: bool = False
hide_default_branch: bool = False
homeassistant: str = None # Minimum Home Assistant version
id: int = 0
iot_class: str = None iot_class: str = None
installed: bool = False
installed_commit: str = None
installed_version: str = None
open_issues: int = 0
last_commit: str = None
last_version: str = None
last_updated: str = 0
manifest_name: str = None
new: bool = True
persistent_directory: str = None
pushed_at: str = ""
releases: bool = False
render_readme: bool = False
published_tags: List[str] = []
selected_tag: str = None
show_beta: bool = False
stargazers_count: int = 0
topics: List[str] = []
zip_release: bool = False
@property
def stars(self):
"""Return the stargazers count."""
return self.stargazers_count or 0
@property @property
def name(self): def name(self):
@ -53,9 +73,20 @@ class RepositoryData:
for key in source: for key in source:
if key in data.__dict__: if key in data.__dict__:
if key == "pushed_at": if key == "pushed_at":
setattr( if "Z" in source[key]:
data, key, datetime.strptime(source[key], "%Y-%m-%dT%H:%M:%SZ") setattr(
) data,
key,
datetime.strptime(source[key], "%Y-%m-%dT%H:%M:%SZ"),
)
else:
setattr(
data,
key,
datetime.strptime(source[key], "%Y-%m-%dT%H:%M:%S"),
)
elif key == "id":
setattr(data, key, str(source[key]))
elif key == "county": elif key == "county":
if isinstance(source[key], str): if isinstance(source[key], str):
setattr(data, key, [source[key]]) setattr(data, key, [source[key]])
@ -70,9 +101,18 @@ class RepositoryData:
for key in data: for key in data:
if key in self.__dict__: if key in self.__dict__:
if key == "pushed_at": if key == "pushed_at":
setattr( if "Z" in data[key]:
self, key, datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%SZ") setattr(
) self,
key,
datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%SZ"),
)
else:
setattr(
self, key, datetime.strptime(data[key], "%Y-%m-%dT%H:%M:%S")
)
elif key == "id":
setattr(self, key, str(data[key]))
elif key == "county": elif key == "county":
if isinstance(data[key], str): if isinstance(data[key], str):
setattr(self, key, [data[key]]) setattr(self, key, [data[key]])

View File

@ -59,12 +59,9 @@ class HacsTheme(HacsRepository):
find_file_name(self) find_file_name(self)
self.content.path.local = f"{self.hacs.system.config_path}/themes/{self.data.file_name.replace('.yaml', '')}" self.content.path.local = f"{self.hacs.system.config_path}/themes/{self.data.file_name.replace('.yaml', '')}"
async def update_repository(self): # lgtm[py/similar-function] async def update_repository(self, ignore_issues=False):
"""Update.""" """Update."""
if self.hacs.github.ratelimits.remaining == 0: await self.common_update(ignore_issues)
return
# Run common update steps.
await self.common_update()
# Get theme objects. # Get theme objects.
if self.data.content_in_root: if self.data.content_in_root:

View File

@ -89,8 +89,8 @@ class HACSSensor(HACSDevice):
{ {
"name": repository.data.full_name, "name": repository.data.full_name,
"display_name": repository.display_name, "display_name": repository.display_name,
"installed version": repository.display_installed_version, "installed_version": repository.display_installed_version,
"available version": repository.display_available_version, "available_version": repository.display_available_version,
} }
) )
return {"repositories": data} return {"repositories": data}

View File

@ -21,9 +21,9 @@ async def load_hacs_repository():
repository = hacs.get_by_name("hacs/integration") repository = hacs.get_by_name("hacs/integration")
if repository is None: if repository is None:
raise HacsException("Unknown error") raise HacsException("Unknown error")
repository.status.installed = True repository.data.installed = True
repository.versions.installed = VERSION repository.data.installed_version = VERSION
repository.status.new = False repository.data.new = False
hacs.repo = repository.repository_object hacs.repo = repository.repository_object
hacs.data_repo = await get_repository( hacs.data_repo = await get_repository(
hacs.session, hacs.configuration.token, "hacs/default" hacs.session, hacs.configuration.token, "hacs/default"
@ -72,7 +72,7 @@ def add_sensor():
async def setup_frontend(): async def setup_frontend():
"""Configure the HACS frontend elements.""" """Configure the HACS frontend elements."""
from .http import HacsFrontend, HacsPluginViewLegacy from .http import HacsFrontend
from .ws_api_handlers import setup_ws_api from .ws_api_handlers import setup_ws_api
hacs = get_hacs() hacs = get_hacs()
@ -80,9 +80,6 @@ async def setup_frontend():
hacs.hass.http.register_view(HacsFrontend()) hacs.hass.http.register_view(HacsFrontend())
hacs.frontend.version_running = FE_VERSION hacs.frontend.version_running = FE_VERSION
# Legacy views, remove with 2.0
hacs.hass.http.register_view(HacsPluginViewLegacy())
# Add to sidepanel # Add to sidepanel
custom_panel_config = { custom_panel_config = {
"name": "hacs-frontend", "name": "hacs-frontend",

View File

@ -6,7 +6,8 @@ from .hacsbase.const import STORAGE_VERSION
async def async_load_from_store(hass, key): async def async_load_from_store(hass, key):
"""Load the retained data from store and return de-serialized data.""" """Load the retained data from store and return de-serialized data."""
store = Store(hass, STORAGE_VERSION, f"hacs.{key}", encoder=JSONEncoder) key = key if "/" in key else f"hacs.{key}"
store = Store(hass, STORAGE_VERSION, key, encoder=JSONEncoder)
restored = await store.async_load() restored = await store.async_load()
if restored is None: if restored is None:
return {} return {}
@ -15,5 +16,14 @@ async def async_load_from_store(hass, key):
async def async_save_to_store(hass, key, data): async def async_save_to_store(hass, key, data):
"""Generate dynamic data to store and save it to the filesystem.""" """Generate dynamic data to store and save it to the filesystem."""
store = Store(hass, STORAGE_VERSION, f"hacs.{key}", encoder=JSONEncoder) key = key if "/" in key else f"hacs.{key}"
store = Store(hass, STORAGE_VERSION, key, encoder=JSONEncoder)
await store.async_save(data) await store.async_save(data)
async def async_remove_store(hass, key):
"""Remove a store element that should no longer be used"""
if "/" not in key:
return
store = Store(hass, STORAGE_VERSION, key, encoder=JSONEncoder)
await store.async_remove()

View File

@ -3,7 +3,7 @@
import sys import sys
import os import os
import voluptuous as vol import voluptuous as vol
from aiogithubapi import AIOGitHubException from aiogithubapi import AIOGitHubAPIException
from homeassistant.components import websocket_api from homeassistant.components import websocket_api
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from .hacsbase.exceptions import HacsException from .hacsbase.exceptions import HacsException
@ -61,7 +61,7 @@ async def hacs_settings(hass, connection, msg):
hass.bus.async_fire("hacs/status", {}) hass.bus.async_fire("hacs/status", {})
for repository in hacs.repositories: for repository in hacs.repositories:
if repository.pending_upgrade: if repository.pending_upgrade:
repository.status.selected_tag = None repository.data.selected_tag = None
await repository.install() await repository.install()
hacs.system.status.upgrading_all = False hacs.system.status.upgrading_all = False
hacs.system.status.background_task = False hacs.system.status.background_task = False
@ -70,14 +70,14 @@ async def hacs_settings(hass, connection, msg):
elif action == "clear_new": elif action == "clear_new":
for repo in hacs.repositories: for repo in hacs.repositories:
if msg.get("category") == repo.data.category: if repo.data.new:
if repo.status.new: hacs.logger.debug(f"Clearing new flag from '{repo.data.full_name}'")
hacs.logger.debug(f"Clearing new flag from '{repo.data.full_name}'") repo.data.new = False
repo.status.new = False
else: else:
hacs.logger.error(f"WS action '{action}' is not valid") hacs.logger.error(f"WS action '{action}' is not valid")
hass.bus.async_fire("hacs/config", {}) hass.bus.async_fire("hacs/config", {})
await hacs.data.async_write() await hacs.data.async_write()
connection.send_message(websocket_api.result_message(msg["id"], {}))
@websocket_api.async_response @websocket_api.async_response
@ -113,6 +113,7 @@ async def hacs_status(hass, connection, msg):
"reloading_data": hacs.system.status.reloading_data, "reloading_data": hacs.system.status.reloading_data,
"upgrading_all": hacs.system.status.upgrading_all, "upgrading_all": hacs.system.status.upgrading_all,
"disabled": hacs.system.disabled, "disabled": hacs.system.disabled,
"has_pending_tasks": hacs.queue.has_pending_tasks,
} }
connection.send_message(websocket_api.result_message(msg["id"], content)) connection.send_message(websocket_api.result_message(msg["id"], content))
@ -130,35 +131,36 @@ async def hacs_repositories(hass, connection, msg):
"additional_info": repo.information.additional_info, "additional_info": repo.information.additional_info,
"authors": repo.data.authors, "authors": repo.data.authors,
"available_version": repo.display_available_version, "available_version": repo.display_available_version,
"beta": repo.status.show_beta, "beta": repo.data.show_beta,
"can_install": repo.can_install, "can_install": repo.can_install,
"category": repo.data.category, "category": repo.data.category,
"country": repo.data.country, "country": repo.data.country,
"config_flow": repo.config_flow, "config_flow": repo.data.config_flow,
"custom": repo.custom, "custom": repo.custom,
"default_branch": repo.data.default_branch, "default_branch": repo.data.default_branch,
"description": repo.data.description, "description": repo.data.description,
"domain": repo.integration_manifest.get("domain"), "domain": repo.data.domain,
"downloads": repo.releases.downloads, "downloads": repo.data.downloads,
"file_name": repo.data.file_name, "file_name": repo.data.file_name,
"first_install": repo.status.first_install, "first_install": repo.status.first_install,
"full_name": repo.data.full_name, "full_name": repo.data.full_name,
"hide": repo.status.hide, "hide": repo.data.hide,
"hide_default_branch": repo.data.hide_default_branch, "hide_default_branch": repo.data.hide_default_branch,
"homeassistant": repo.data.homeassistant, "homeassistant": repo.data.homeassistant,
"id": repo.information.uid, "id": repo.data.id,
"info": repo.information.info, "info": repo.information.info,
"installed_version": repo.display_installed_version, "installed_version": repo.display_installed_version,
"installed": repo.status.installed, "installed": repo.data.installed,
"issues": repo.data.open_issues,
"javascript_type": repo.information.javascript_type, "javascript_type": repo.information.javascript_type,
"last_updated": repo.information.last_updated, "last_updated": repo.data.last_updated,
"local_path": repo.content.path.local, "local_path": repo.content.path.local,
"main_action": repo.main_action, "main_action": repo.main_action,
"name": repo.display_name, "name": repo.display_name,
"new": repo.status.new, "new": repo.data.new,
"pending_upgrade": repo.pending_upgrade, "pending_upgrade": repo.pending_upgrade,
"releases": repo.releases.published_tags, "releases": repo.data.published_tags,
"selected_tag": repo.status.selected_tag, "selected_tag": repo.data.selected_tag,
"stars": repo.data.stargazers_count, "stars": repo.data.stargazers_count,
"state": repo.state, "state": repo.state,
"status_description": repo.display_status_description, "status_description": repo.display_status_description,
@ -195,42 +197,48 @@ async def hacs_repository(hass, connection, msg):
hacs.logger.debug(f"Running {action} for {repository.data.full_name}") hacs.logger.debug(f"Running {action} for {repository.data.full_name}")
if action == "update": if action == "update":
await repository.update_repository() await repository.update_repository(True)
repository.status.updated_info = True repository.status.updated_info = True
repository.status.new = False
elif action == "install": elif action == "install":
was_installed = repository.status.installed was_installed = repository.data.installed
await repository.install() await repository.install()
if not was_installed: if not was_installed:
hass.bus.async_fire("hacs/reload", {"force": True}) hass.bus.async_fire("hacs/reload", {"force": True})
elif action == "not_new":
repository.data.new = False
elif action == "uninstall": elif action == "uninstall":
await repository.uninstall() await repository.uninstall()
elif action == "hide": elif action == "hide":
repository.status.hide = True repository.data.hide = True
elif action == "unhide": elif action == "unhide":
repository.status.hide = False repository.data.hide = False
elif action == "show_beta": elif action == "show_beta":
repository.status.show_beta = True repository.data.show_beta = True
await repository.update_repository() await repository.update_repository()
elif action == "hide_beta": elif action == "hide_beta":
repository.status.show_beta = False repository.data.show_beta = False
await repository.update_repository()
elif action == "toggle_beta":
repository.data.show_beta = not repository.data.show_beta
await repository.update_repository() await repository.update_repository()
elif action == "delete": elif action == "delete":
repository.status.show_beta = False repository.data.show_beta = False
repository.remove() repository.remove()
elif action == "set_version": elif action == "set_version":
if msg["version"] == repository.data.default_branch: if msg["version"] == repository.data.default_branch:
repository.status.selected_tag = None repository.data.selected_tag = None
else: else:
repository.status.selected_tag = msg["version"] repository.data.selected_tag = msg["version"]
await repository.update_repository() await repository.update_repository()
hass.bus.async_fire("hacs/reload", {"force": True}) hass.bus.async_fire("hacs/reload", {"force": True})
@ -240,11 +248,11 @@ async def hacs_repository(hass, connection, msg):
await hacs.data.async_write() await hacs.data.async_write()
message = None message = None
except AIOGitHubException as exception: except AIOGitHubAPIException as exception:
message = str(exception) message = str(exception)
hass.bus.async_fire("hacs/error", {"message": str(exception)}) hass.bus.async_fire("hacs/error", {"message": str(exception)})
except AttributeError as exception: except AttributeError as exception:
message = f"Could not use repository with ID {repo_id}" message = f"Could not use repository with ID {repo_id} ({exception})"
except Exception as exception: # pylint: disable=broad-except except Exception as exception: # pylint: disable=broad-except
message = str(exception) message = str(exception)
@ -253,6 +261,7 @@ async def hacs_repository(hass, connection, msg):
hass.bus.async_fire("hacs/error", {"message": message}) hass.bus.async_fire("hacs/error", {"message": message})
repository.state = None repository.state = None
connection.send_message(websocket_api.result_message(msg["id"], {}))
@websocket_api.async_response @websocket_api.async_response
@ -318,10 +327,19 @@ async def hacs_repository_data(hass, connection, msg):
repository.state = data repository.state = data
elif action == "set_version": elif action == "set_version":
repository.status.selected_tag = data repository.data.selected_tag = data
await repository.update_repository() await repository.update_repository()
repository.state = None repository.state = None
elif action == "install":
was_installed = repository.data.installed
repository.data.selected_tag = data
await repository.update_repository()
await repository.install()
repository.state = None
if not was_installed:
hass.bus.async_fire("hacs/reload", {"force": True})
elif action == "add": elif action == "add":
repository.state = None repository.state = None
@ -330,6 +348,7 @@ async def hacs_repository_data(hass, connection, msg):
hacs.logger.error(f"WS action '{action}' is not valid") hacs.logger.error(f"WS action '{action}' is not valid")
await hacs.data.async_write() await hacs.data.async_write()
connection.send_message(websocket_api.result_message(msg["id"], {}))
@websocket_api.async_response @websocket_api.async_response