mirror of
https://github.com/ajnart/homarr.git
synced 2026-03-06 12:21:06 +01:00
🔀 Version 0.14.2
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"license": "MIT",
|
||||
"description": "This package.json is used for the migration script the dependencies are only installed within the Dockerfile.",
|
||||
"scripts": {
|
||||
"db:migrate": "ts-node ./migrate.ts"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homarr",
|
||||
"version": "0.14.1",
|
||||
"version": "0.14.2",
|
||||
"description": "Homarr - A homepage for your server.",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
@@ -233,4 +233,4 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "宽度",
|
||||
"height": "高度"
|
||||
}
|
||||
},
|
||||
"public": "公开",
|
||||
"restricted": "限制"
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
"key": "快捷键",
|
||||
"action": "操作",
|
||||
"keybinds": "热键绑定",
|
||||
"translators": "翻译 ({{count}})",
|
||||
"translators": "翻译者 ({{count}})",
|
||||
"translatorsDescription": "感谢这些人,Homarr 现已支持 {{languages}} 种语言!想要帮助将 Homarr 翻译成您的语言吗?请阅读<a>此处</a>了解如何执行此操作 。",
|
||||
"contributors": "贡献者 ({{count}})",
|
||||
"contributorsDescription": "这些人构建了让 homarr 工作的代码!想帮助建造 Homarr 吗?请阅读<a>此处</a>了解如何操作",
|
||||
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "crwdns3910:0crwdne3910:0",
|
||||
"height": "crwdns3912:0crwdne3912:0"
|
||||
}
|
||||
},
|
||||
"public": "crwdns4034:0crwdne4034:0",
|
||||
"restricted": "crwdns4036:0crwdne4036:0"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "",
|
||||
"height": ""
|
||||
}
|
||||
},
|
||||
"public": "",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -36,7 +36,7 @@
|
||||
"footer": {
|
||||
"error": "",
|
||||
"lastUpdated": "Naposledy aktualizováno před {{time}}",
|
||||
"ratioGlobal": "Globální poměr",
|
||||
"ratioGlobal": "Obecný poměr",
|
||||
"ratioWithFilter": "Filtrovaný poměr"
|
||||
},
|
||||
"table": {
|
||||
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Bredde",
|
||||
"height": "Højde"
|
||||
}
|
||||
},
|
||||
"public": "Offentlig",
|
||||
"restricted": "Begrænset"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Breite",
|
||||
"height": "Höhe"
|
||||
}
|
||||
},
|
||||
"public": "Öffentlich sichtbar",
|
||||
"restricted": "Eingeschränkt"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Πλάτος",
|
||||
"height": "Ύψος"
|
||||
}
|
||||
},
|
||||
"public": "Δημόσιο",
|
||||
"restricted": "Περιορισμένη πρόσβαση"
|
||||
}
|
||||
17
public/locales/en/modules/smart-home/entity-state.json
Normal file
17
public/locales/en/modules/smart-home/entity-state.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"entityNotFound": "Entity not found",
|
||||
"descriptor": {
|
||||
"name": "Home Assistant entity",
|
||||
"description": "Current state of an entity in Home Assistant",
|
||||
"settings": {
|
||||
"title": "Entity state",
|
||||
"entityId": {
|
||||
"label": "Entity ID",
|
||||
"info": "Unique entity ID in Home Assistant. Copy by clicking on entity > Click on cog icon > Click on copy button at 'Entity ID'. Some custom entities may not be supported."
|
||||
},
|
||||
"displayName": {
|
||||
"label": "Display name"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Ancho",
|
||||
"height": "Alto"
|
||||
}
|
||||
},
|
||||
"public": "Pública",
|
||||
"restricted": "Restringido"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Largeur",
|
||||
"height": "Hauteur"
|
||||
}
|
||||
},
|
||||
"public": "Public",
|
||||
"restricted": "Restreint"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "רוחב",
|
||||
"height": "גובה"
|
||||
}
|
||||
},
|
||||
"public": "ציבורי",
|
||||
"restricted": "מוגבל"
|
||||
}
|
||||
@@ -19,26 +19,26 @@
|
||||
"label": "רקע"
|
||||
},
|
||||
"backgroundImageAttachment": {
|
||||
"label": "",
|
||||
"label": "צירוף תמונת רקע",
|
||||
"options": {
|
||||
"fixed": "",
|
||||
"scroll": ""
|
||||
"fixed": "קבוע - הרקע נשאר באותו מיקום (מומלץ)",
|
||||
"scroll": "גלילה - גלילה ברקע עם העכבר"
|
||||
}
|
||||
},
|
||||
"backgroundImageSize": {
|
||||
"label": "",
|
||||
"label": "גודל תמונת רקע",
|
||||
"options": {
|
||||
"cover": "",
|
||||
"contain": ""
|
||||
"cover": "כיסוי - קנה מידה קטן ככל האפשר של התמונה כדי לכסות את כל החלון על ידי חיתוך שטח מוגזם. (מוּמלָץ)",
|
||||
"contain": "מכיל - קנה מידה גדול ככל האפשר של התמונה בתוך המיכל שלה מבלי לחתוך או למתוח את התמונה."
|
||||
}
|
||||
},
|
||||
"backgroundImageRepeat": {
|
||||
"label": "",
|
||||
"label": "צירוף תמונת רקע",
|
||||
"options": {
|
||||
"repeat": "",
|
||||
"no-repeat": "",
|
||||
"repeat-x": "",
|
||||
"repeat-y": ""
|
||||
"repeat": "חזור - התמונה חוזרת על עצמה ככל שנדרש כדי לכסות את כל אזור ציור תמונת הרקע.",
|
||||
"no-repeat": "ללא חזרה - התמונה אינה חוזרת על עצמה וייתכן שלא תמלא את כל החלל (מומלץ)",
|
||||
"repeat-x": "חזור X - זהה ל'חזרה' אבל רק על הציר האופקי.",
|
||||
"repeat-y": "חזור Y - זהה ל'חזרה' אבל רק על הציר האנכי."
|
||||
}
|
||||
},
|
||||
"customCSS": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"title": "דוקר",
|
||||
"alerts": {
|
||||
"notConfigured": {
|
||||
"text": ""
|
||||
"text": "למופע ה-Homarr שלך לא הוגדר Docker או שהוא נכשל באחזור קונטיינרים. אנא עיין בתיעוד כיצד להגדיר את האינטגרציה."
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Širina",
|
||||
"height": "Visina"
|
||||
}
|
||||
},
|
||||
"public": "Javno",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Szélesség",
|
||||
"height": "Magasság"
|
||||
}
|
||||
},
|
||||
"public": "Nyilvános",
|
||||
"restricted": "Korlátozott"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Larghezza",
|
||||
"height": "Altezza"
|
||||
}
|
||||
},
|
||||
"public": "Pubblico",
|
||||
"restricted": "Limitato"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "幅",
|
||||
"height": "高さ"
|
||||
}
|
||||
},
|
||||
"public": "公開",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "너비",
|
||||
"height": "높이"
|
||||
}
|
||||
},
|
||||
"public": "공개",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Platums",
|
||||
"height": "Augstums"
|
||||
}
|
||||
},
|
||||
"public": "Publisks",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Breedte",
|
||||
"height": "Hoogte"
|
||||
}
|
||||
},
|
||||
"public": "Openbaar",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Bredde",
|
||||
"height": "Høyde"
|
||||
}
|
||||
},
|
||||
"public": "Offentlig",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"save": "Zapisz",
|
||||
"apply": "Zastosuj",
|
||||
"insert": "",
|
||||
"insert": "Wstaw",
|
||||
"about": "O programie",
|
||||
"cancel": "Anuluj",
|
||||
"close": "Zamknij",
|
||||
@@ -45,11 +45,13 @@
|
||||
"seeMore": "Zobacz więcej...",
|
||||
"position": {
|
||||
"left": "Lewo",
|
||||
"center": "",
|
||||
"center": "Wyśrodkowany",
|
||||
"right": "Prawo"
|
||||
},
|
||||
"attributes": {
|
||||
"width": "Szerokość",
|
||||
"height": "Wysokość"
|
||||
}
|
||||
},
|
||||
"public": "Publiczna",
|
||||
"restricted": "Ograniczone"
|
||||
}
|
||||
@@ -20,11 +20,11 @@
|
||||
"lines": {
|
||||
"nothingAfterPort": "W większości, jeśli nie we wszystkich przypadkach, nie należy wprowadzać żadnej ścieżki po porcie. (Nawet '/admin' dla pihole lub '/web' dla plex)",
|
||||
"protocolCheck": "Zawsze upewnij się, że adres URL jest poprzedzony przez http lub https, i upewnij się, że używasz odpowiedniego adresu.",
|
||||
"preferIP": "",
|
||||
"enablePings": "",
|
||||
"wget": "",
|
||||
"iframe": "",
|
||||
"clearCache": ""
|
||||
"preferIP": "Zalecane jest użycie bezpośredniego adresu IP maszyny lub kontenera, z którym próbujesz się komunikować.",
|
||||
"enablePings": "Sprawdź, czy adres IP jest prawidłowy, włączając polecenia ping. Dostosuj tablicę -> Układ -> Włącz pingi. Na kafelkach aplikacji pojawi się mała czerwona lub zielona dymka, a najechanie na nią spowoduje wyświetlenie kodu odpowiedzi (w większości przypadków oczekiwana jest zielona dymka z kodem 200).",
|
||||
"wget": "Aby mieć pewność, że homarr będzie mógł komunikować się z innymi aplikacjami, wykonaj wget/curl/ping IP:port aplikacji.",
|
||||
"iframe": "Jeśli chodzi o ramki iframe, powinny one zawsze używać tego samego protokołu (http/s), co Homarr.",
|
||||
"clearCache": "Niektóre informacje są rejestrowane w pamięci podręcznej, więc integracja może nie działać, jeśli nie wyczyścisz pamięci podręcznej w ogólnych opcjach Homarr."
|
||||
},
|
||||
"footer": "Aby uzyskać więcej informacji na temat rozwiązywania problemów, skontaktuj się z nami na naszym {{discord}}."
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"name": "Kalendarz",
|
||||
"description": "Wyświetla kalendarz z nadchodzącymi wydaniami, z obsługiwanych integracji.",
|
||||
"settings": {
|
||||
"title": "Ustawienia dla widżetu Kalendarz",
|
||||
"title": "Ustawienia dla widżetu Kalendarza",
|
||||
"radarrReleaseType": {
|
||||
"label": "Rodzaj premiery w Radarr",
|
||||
"data": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"descriptor": {
|
||||
"name": "Dash.",
|
||||
"name": "Dach.",
|
||||
"description": "Wyświetla wykresy zewnętrznego Dash. Instancja wewnątrz Homarr.",
|
||||
"settings": {
|
||||
"title": "Ustawienia dla widgetu Dash.",
|
||||
@@ -82,7 +82,7 @@
|
||||
}
|
||||
},
|
||||
"card": {
|
||||
"title": "Dash.",
|
||||
"title": "Dach.",
|
||||
"errors": {
|
||||
"noService": "Nie znaleziono usługi Dash. Proszę dodać ją do pulpitu Homarra lub ustawić adres URL usługi Dash. w opcjach modułu",
|
||||
"noInformation": "Nie można uzyskać informacji z dash. - używasz najnowszej wersji?",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"label": "Pokaż pasek narzędzi ułatwiający pisanie w markdown"
|
||||
},
|
||||
"allowReadOnlyCheck": {
|
||||
"label": ""
|
||||
"label": "Zezwalaj na sprawdzanie w trybie tylko do odczytu"
|
||||
},
|
||||
"content": {
|
||||
"label": "Zawartość notatnika"
|
||||
@@ -39,7 +39,7 @@
|
||||
"image": "Osadź obraz",
|
||||
"addTable": "Dodaj tabelę",
|
||||
"deleteTable": "Usuń tabelę",
|
||||
"colorCell": "",
|
||||
"colorCell": "Kolor Komórki",
|
||||
"mergeCell": "Przełączanie scalania komórek",
|
||||
"addColumnLeft": "Dodaj kolumnę przed",
|
||||
"addColumnRight": "Dodaj kolumnę po",
|
||||
|
||||
@@ -11,10 +11,10 @@
|
||||
"label": "Wyświetlanie ukończonych torrentów"
|
||||
},
|
||||
"displayActiveTorrents": {
|
||||
"label": ""
|
||||
"label": "Wyświetl aktywne torrenty"
|
||||
},
|
||||
"speedLimitOfActiveTorrents": {
|
||||
"label": ""
|
||||
"label": "Prędkość wysyłania pozwalająca uznać torrent za aktywny (kB/s)"
|
||||
},
|
||||
"displayStaleTorrents": {
|
||||
"label": "Wyświetlanie nieaktualnych torrentów"
|
||||
|
||||
@@ -19,26 +19,26 @@
|
||||
"label": "Tło"
|
||||
},
|
||||
"backgroundImageAttachment": {
|
||||
"label": "",
|
||||
"label": "Ustawienie obrazu tła",
|
||||
"options": {
|
||||
"fixed": "",
|
||||
"scroll": ""
|
||||
"fixed": "Fixed – Tło pozostaje w tej samej pozycji (zalecane)",
|
||||
"scroll": "Scroll - Tło przewija się za pomocą myszy"
|
||||
}
|
||||
},
|
||||
"backgroundImageSize": {
|
||||
"label": "",
|
||||
"label": "Rozmiar obrazu tła",
|
||||
"options": {
|
||||
"cover": "",
|
||||
"contain": ""
|
||||
"cover": "Cover — skaluje obraz do najmniejszego możliwego rozmiaru, aby pokryć całe okno poprzez przycięcie nadmiernej przestrzeni. (Zalecana)",
|
||||
"contain": "Contain — skaluje obraz do największego możliwego rozmiaru w jego kontenerze, bez przycinania i rozciągania obrazu."
|
||||
}
|
||||
},
|
||||
"backgroundImageRepeat": {
|
||||
"label": "",
|
||||
"label": "Ustawienie obrazu tła",
|
||||
"options": {
|
||||
"repeat": "",
|
||||
"no-repeat": "",
|
||||
"repeat-x": "",
|
||||
"repeat-y": ""
|
||||
"repeat": "Repeat — obraz jest powtarzany tak często, jak to konieczne, aby pokryć cały obszar obrazu tła.",
|
||||
"no-repeat": "No repeat — obraz nie jest powtarzany i może nie wypełniać całej przestrzeni (zalecane)",
|
||||
"repeat-x": "Repeat X — to samo co „Powtórz”, ale tylko na osi poziomej.",
|
||||
"repeat-y": "Repeat Y — to samo co „Powtórz”, ale tylko w osi pionowej."
|
||||
}
|
||||
},
|
||||
"customCSS": {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"title": "Docker",
|
||||
"alerts": {
|
||||
"notConfigured": {
|
||||
"text": ""
|
||||
"text": "Twoja instancja Homarr nie ma skonfigurowanego Dockera lub nie była w stanie pobrać listę kontenerów. Sprawdź dokumentację, aby dowiedzieć się, jak skonfigurować integrację."
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"save": "Salvar",
|
||||
"apply": "",
|
||||
"insert": "",
|
||||
"apply": "Aplicar",
|
||||
"insert": "Inserir",
|
||||
"about": "Sobre",
|
||||
"cancel": "Cancelar",
|
||||
"close": "Fechar",
|
||||
@@ -45,11 +45,13 @@
|
||||
"seeMore": "Veja mais...",
|
||||
"position": {
|
||||
"left": "Esquerda",
|
||||
"center": "",
|
||||
"center": "Centralizado",
|
||||
"right": "Certo"
|
||||
},
|
||||
"attributes": {
|
||||
"width": "Largura",
|
||||
"height": "Altura"
|
||||
}
|
||||
},
|
||||
"public": "Público",
|
||||
"restricted": "Restrito"
|
||||
}
|
||||
@@ -5,10 +5,10 @@
|
||||
"key": "Tecla de atalho",
|
||||
"action": "Ação",
|
||||
"keybinds": "Ligações de teclas",
|
||||
"translators": "",
|
||||
"translatorsDescription": "",
|
||||
"contributors": "",
|
||||
"contributorsDescription": "",
|
||||
"translators": "Tradutores ({{count}})",
|
||||
"translatorsDescription": "Graças a essas pessoas, o Homarr está disponível em {{languages}} idiomas! Quer ajudar a traduzir o Homarr para seu idioma? Veja como fazer isso <a>aqui</a>.",
|
||||
"contributors": "Colaboradores ({{count}})",
|
||||
"contributorsDescription": "Essas pessoas criaram o código que faz o homarr funcionar! Quer ajudar a construir o Homarr? Veja como fazer isso <a>aqui</a>",
|
||||
"actions": {
|
||||
"toggleTheme": "Alternar o modo claro/escuro",
|
||||
"focusSearchBar": "Foco na barra de pesquisa",
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
"metrics": {
|
||||
"domainsOnAdlist": "Domínios em adlists",
|
||||
"queriesToday": "Consultas hoje",
|
||||
"queriesBlockedTodayPercentage": "",
|
||||
"queriesBlockedToday": ""
|
||||
"queriesBlockedTodayPercentage": "Bloqueado hoje",
|
||||
"queriesBlockedToday": "Bloqueado hoje"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"label": "Mostrar a barra de ferramentas para ajudá-lo a escrever markdown"
|
||||
},
|
||||
"allowReadOnlyCheck": {
|
||||
"label": ""
|
||||
"label": "Permitir a verificação no modo de leitura"
|
||||
},
|
||||
"content": {
|
||||
"label": "O conteúdo do notebook"
|
||||
@@ -17,43 +17,43 @@
|
||||
},
|
||||
"card": {
|
||||
"controls": {
|
||||
"bold": "",
|
||||
"italic": "",
|
||||
"strikethrough": "",
|
||||
"underline": "",
|
||||
"colorText": "",
|
||||
"colorHighlight": "",
|
||||
"code": "",
|
||||
"clear": "",
|
||||
"heading": "",
|
||||
"align": "",
|
||||
"blockquote": "",
|
||||
"horizontalLine": "",
|
||||
"bulletList": "",
|
||||
"orderedList": "",
|
||||
"checkList": "",
|
||||
"increaseIndent": "",
|
||||
"decreaseIndent": "",
|
||||
"link": "",
|
||||
"unlink": "",
|
||||
"image": "",
|
||||
"addTable": "",
|
||||
"deleteTable": "",
|
||||
"colorCell": "",
|
||||
"mergeCell": "",
|
||||
"addColumnLeft": "",
|
||||
"addColumnRight": "",
|
||||
"deleteColumn": "",
|
||||
"addRowTop": "",
|
||||
"addRowBelow": "",
|
||||
"deleteRow": ""
|
||||
"bold": "Negrito",
|
||||
"italic": "Itálico",
|
||||
"strikethrough": "Riscar",
|
||||
"underline": "Sublinhar",
|
||||
"colorText": "Cor do texto",
|
||||
"colorHighlight": "Texto colorido em destaque",
|
||||
"code": "Código",
|
||||
"clear": "Limpar formatação",
|
||||
"heading": "Cabeçalho {{level}}",
|
||||
"align": "Alinhar texto: {{position}}",
|
||||
"blockquote": "Bloco de Citação",
|
||||
"horizontalLine": "Linha horizontal",
|
||||
"bulletList": "Lista de marcadores",
|
||||
"orderedList": "Lista ordenada",
|
||||
"checkList": "Lista de verificação",
|
||||
"increaseIndent": "Aumentar recuo",
|
||||
"decreaseIndent": "Diminuir recuo",
|
||||
"link": "Link",
|
||||
"unlink": "Remover link",
|
||||
"image": "Incorporar imagem",
|
||||
"addTable": "Adicionar tabela",
|
||||
"deleteTable": "Excluir tabela",
|
||||
"colorCell": "Cor da Célula",
|
||||
"mergeCell": "Ativar/desativar mesclagem de células",
|
||||
"addColumnLeft": "Adicionar coluna antes",
|
||||
"addColumnRight": "Adicionar coluna depois",
|
||||
"deleteColumn": "Excluir coluna",
|
||||
"addRowTop": "Adicionar linha antes",
|
||||
"addRowBelow": "Adicionar linha depois",
|
||||
"deleteRow": "Excluir linha"
|
||||
},
|
||||
"modals": {
|
||||
"clearColor": "",
|
||||
"source": "",
|
||||
"widthPlaceholder": "",
|
||||
"columns": "",
|
||||
"rows": ""
|
||||
"clearColor": "Limpar cor",
|
||||
"source": "Fonte",
|
||||
"widthPlaceholder": "Valor em % ou pixels",
|
||||
"columns": "Colunas",
|
||||
"rows": "Linhas"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,10 @@
|
||||
"label": "Mostrar torrentes completas"
|
||||
},
|
||||
"displayActiveTorrents": {
|
||||
"label": ""
|
||||
"label": "Exibir torrents ativos"
|
||||
},
|
||||
"speedLimitOfActiveTorrents": {
|
||||
"label": ""
|
||||
"label": "Velocidade de upload para considerar um torrent como ativo (kB/s)"
|
||||
},
|
||||
"displayStaleTorrents": {
|
||||
"label": "Exibição de torrentes envelhecidas"
|
||||
@@ -27,8 +27,8 @@
|
||||
"description": "Quando a opção \"is whitelist\" estiver marcada, ela funcionará como uma lista de permissões. Se não estiver marcada, será uma lista negra. Não fará nada quando estiver vazia"
|
||||
},
|
||||
"displayRatioWithFilter": {
|
||||
"label": "",
|
||||
"info": ""
|
||||
"label": "Exibir proporção da lista de torrents filtrados",
|
||||
"info": "Se estiver desativado, somente a proporção global será exibida. A proporção global ainda usará os rótulos se estiver definida"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -36,8 +36,8 @@
|
||||
"footer": {
|
||||
"error": "Erro",
|
||||
"lastUpdated": "Última actualização {{time}} atrás",
|
||||
"ratioGlobal": "",
|
||||
"ratioWithFilter": ""
|
||||
"ratioGlobal": "Proporção global",
|
||||
"ratioWithFilter": "Proporção com filtro"
|
||||
},
|
||||
"table": {
|
||||
"header": {
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"description": "Configure o Homarr para usuários com deficiência ou incapacitados"
|
||||
},
|
||||
"access": {
|
||||
"name": "",
|
||||
"name": "Acesso",
|
||||
"description": "Configure quem tem acesso ao seu quadro"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,26 +19,26 @@
|
||||
"label": "Antecedentes"
|
||||
},
|
||||
"backgroundImageAttachment": {
|
||||
"label": "",
|
||||
"label": "Anexo de imagem de fundo",
|
||||
"options": {
|
||||
"fixed": "",
|
||||
"scroll": ""
|
||||
"fixed": "Fixado - O plano de fundo permanece na mesma posição (recomendado)",
|
||||
"scroll": "Rolagem - O plano de fundo rola com seu mouse"
|
||||
}
|
||||
},
|
||||
"backgroundImageSize": {
|
||||
"label": "",
|
||||
"label": "Tamanho da imagem de fundo",
|
||||
"options": {
|
||||
"cover": "",
|
||||
"contain": ""
|
||||
"cover": "Capa - Dimensiona a imagem o menor possível para cobrir toda a janela cortando o espaço excessivo. (recomendado)",
|
||||
"contain": "Conter - Dimensiona a imagem o máximo possível dentro de seu contêiner, sem cortar ou esticar a imagem."
|
||||
}
|
||||
},
|
||||
"backgroundImageRepeat": {
|
||||
"label": "",
|
||||
"label": "Anexo de imagem de fundo",
|
||||
"options": {
|
||||
"repeat": "",
|
||||
"no-repeat": "",
|
||||
"repeat-x": "",
|
||||
"repeat-y": ""
|
||||
"repeat": "Repetir - A imagem é repetida o quanto for necessário para cobrir toda a área de pintura da imagem de fundo.",
|
||||
"no-repeat": "Sem repetição - A imagem não se repete e pode não preencher todo o espaço (recomendado)",
|
||||
"repeat-x": "Repetir X - O mesmo que 'Repetir', mas apenas no eixo horizontal.",
|
||||
"repeat-y": "Repetir Y - O mesmo que 'Repetir', mas apenas no eixo vertical."
|
||||
}
|
||||
},
|
||||
"customCSS": {
|
||||
|
||||
@@ -2,16 +2,16 @@
|
||||
"title": "Docker",
|
||||
"alerts": {
|
||||
"notConfigured": {
|
||||
"text": ""
|
||||
"text": "Sua instância do Homarr não possui o Docker configurado ou falhou em buscar contêineres. Por favor, verifique a documentação sobre como configurar a integração."
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
"selectBoard": {
|
||||
"title": "Escolha uma placa",
|
||||
"title": "Escolha um quadro",
|
||||
"text": "Escolha o quadro em que deseja adicionar os aplicativos para os contêineres do Docker selecionados.",
|
||||
"form": {
|
||||
"board": {
|
||||
"label": "Diretoria"
|
||||
"label": "Quadro"
|
||||
},
|
||||
"submit": "Adicionar aplicativos"
|
||||
}
|
||||
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Ширина",
|
||||
"height": "Высота"
|
||||
}
|
||||
},
|
||||
"public": "Публичный",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Šírka",
|
||||
"height": "Výška"
|
||||
}
|
||||
},
|
||||
"public": "Verejné",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Širina",
|
||||
"height": "Višina"
|
||||
}
|
||||
},
|
||||
"public": "Javna stran",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Bredd",
|
||||
"height": "Höjd"
|
||||
}
|
||||
},
|
||||
"public": "Publik",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Genişlik",
|
||||
"height": "Yükseklik"
|
||||
}
|
||||
},
|
||||
"public": "Herkese açık",
|
||||
"restricted": "Sınırlı"
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
"backgroundImageAttachment": {
|
||||
"label": "Arkaplan resim ekle",
|
||||
"options": {
|
||||
"fixed": "Düzeltildi - Arka plan aynı konumda kalıyor (tavsiye edilen)",
|
||||
"fixed": "Sabit - Arka plan aynı konumda kalır (önerilir)",
|
||||
"scroll": "Kaydırma - Arka plan farenizle kaydırılır"
|
||||
}
|
||||
},
|
||||
@@ -36,7 +36,7 @@
|
||||
"label": "Arkaplan resim ekle",
|
||||
"options": {
|
||||
"repeat": "Tekrarla - Resim, arka plan görüntü alanının tamamını kapsayacak şekilde gerektiği kadar tekrarlanır.",
|
||||
"no-repeat": "",
|
||||
"no-repeat": "Tekrarsız - Resim tekrarlanmaz ve tüm alanı doldurmayabilir (önerilir)",
|
||||
"repeat-x": "Tekrarla X - 'Tekrarla' ile aynıdır ancak yalnızca yatay eksende.",
|
||||
"repeat-y": "Tekrar Y - 'Tekrarla' ile aynıdır, ancak yalnızca dikey eksende."
|
||||
}
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"label": "Gölge"
|
||||
"label": "Gölge "
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "寬度",
|
||||
"height": "高度"
|
||||
}
|
||||
},
|
||||
"public": "公開",
|
||||
"restricted": "受到限制"
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Ширина",
|
||||
"height": "Висота"
|
||||
}
|
||||
},
|
||||
"public": "Публічний",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -51,5 +51,7 @@
|
||||
"attributes": {
|
||||
"width": "Chiều rộng",
|
||||
"height": "Chiều cao"
|
||||
}
|
||||
},
|
||||
"public": "Công khai",
|
||||
"restricted": ""
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
{
|
||||
"name": "Homarr",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"icons": [
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
echo "Exporting hostname..."
|
||||
export NEXTAUTH_URL_INTERNAL="http://$HOSTNAME:7575"
|
||||
export NEXTAUTH_URL_INTERNAL="http://$HOSTNAME:${PORT:-7575}"
|
||||
|
||||
echo "Migrating database..."
|
||||
cd ./migrate; yarn db:migrate & PID=$!
|
||||
|
||||
@@ -24,7 +24,6 @@ export const GeneralTab = ({ form, openTab }: GeneralTabProps) => {
|
||||
t('general.internalAddress.troubleshoot.lines.enablePings'),
|
||||
t('general.internalAddress.troubleshoot.lines.wget'),
|
||||
t('general.internalAddress.troubleshoot.lines.iframe'),
|
||||
t('general.internalAddress.troubleshoot.lines.clearCache'),
|
||||
];
|
||||
|
||||
return (
|
||||
|
||||
@@ -183,4 +183,9 @@ export const availableIntegrations = [
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/adguard-home.png',
|
||||
label: 'AdGuard Home',
|
||||
},
|
||||
{
|
||||
value: 'homeAssistant',
|
||||
image: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons@master/png/home-assistant.png',
|
||||
label: 'Home Assistant'
|
||||
}
|
||||
] as const satisfies Readonly<SelectItem[]>;
|
||||
|
||||
@@ -2,17 +2,13 @@ import { Button, Global, Text, Title, Tooltip, clsx } from '@mantine/core';
|
||||
import { useHotkeys, useWindowEvent } from '@mantine/hooks';
|
||||
import { openContextModal } from '@mantine/modals';
|
||||
import { hideNotification, showNotification } from '@mantine/notifications';
|
||||
import {
|
||||
IconApps,
|
||||
IconEditCircle,
|
||||
IconEditCircleOff,
|
||||
IconSettings
|
||||
} from '@tabler/icons-react';
|
||||
import { IconApps, IconEditCircle, IconEditCircleOff, IconSettings } from '@tabler/icons-react';
|
||||
import Consola from 'consola';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { Trans, useTranslation } from 'next-i18next';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import { env } from 'process';
|
||||
import { useEditModeStore } from '~/components/Dashboard/Views/useEditModeStore';
|
||||
import { useNamedWrapperColumnCount } from '~/components/Dashboard/Wrappers/gridstack/store';
|
||||
import { BoardHeadOverride } from '~/components/layout/Meta/BoardHeadOverride';
|
||||
@@ -20,7 +16,6 @@ import { HeaderActionButton } from '~/components/layout/header/ActionButton';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { api } from '~/utils/api';
|
||||
|
||||
import { env } from 'process';
|
||||
import { MainLayout } from './MainLayout';
|
||||
|
||||
type BoardLayoutProps = {
|
||||
@@ -32,10 +27,7 @@ export const BoardLayout = ({ children }: BoardLayoutProps) => {
|
||||
const { data: session } = useSession();
|
||||
|
||||
return (
|
||||
<MainLayout
|
||||
autoFocusSearch={session?.user.autoFocusSearch}
|
||||
headerActions={<HeaderActions />}
|
||||
>
|
||||
<MainLayout autoFocusSearch={session?.user.autoFocusSearch} headerActions={<HeaderActions />}>
|
||||
<BoardHeadOverride />
|
||||
<BackgroundImage />
|
||||
{children}
|
||||
@@ -135,7 +127,7 @@ const ToggleEditModeButton = () => {
|
||||
<Text
|
||||
component="a"
|
||||
style={{ color: 'inherit', textDecoration: 'underline' }}
|
||||
href="https://homarr.dev/docs/customizations/layout"
|
||||
href="https://homarr.dev/docs/customizations/board-customization#screen-sizes"
|
||||
target="_blank"
|
||||
/>
|
||||
),
|
||||
@@ -205,7 +197,7 @@ const BackgroundImage = () => {
|
||||
backgroundPosition: 'center center',
|
||||
backgroundSize: config?.settings.customization.backgroundImageSize ?? 'cover',
|
||||
backgroundRepeat: config?.settings.customization.backgroundImageRepeat ?? 'no-repeat',
|
||||
backgroundAttachment: config?.settings.customization.backgroundImageAttachment ?? 'fixed'
|
||||
backgroundAttachment: config?.settings.customization.backgroundImageAttachment ?? 'fixed',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -229,7 +229,7 @@ export default function CustomizationPage({
|
||||
)}
|
||||
</Transition>
|
||||
</Affix>
|
||||
<Container>
|
||||
<Container pb="6rem">
|
||||
<Paper p="xl" py="sm" mih="100%" withBorder>
|
||||
<Stack>
|
||||
<Group position="apart">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createTRPCRouter } from '~/server/api/trpc';
|
||||
|
||||
import { appRouter } from './routers/app';
|
||||
import { boardRouter } from './routers/board';
|
||||
import { calendarRouter } from './routers/calendar';
|
||||
@@ -16,6 +15,7 @@ import { notebookRouter } from './routers/notebook';
|
||||
import { overseerrRouter } from './routers/overseerr';
|
||||
import { passwordRouter } from './routers/password';
|
||||
import { rssRouter } from './routers/rss';
|
||||
import { smartHomeEntityStateRouter } from './routers/smart-home/entity-state';
|
||||
import { timezoneRouter } from './routers/timezone';
|
||||
import { usenetRouter } from './routers/usenet/router';
|
||||
import { userRouter } from './routers/user';
|
||||
@@ -47,6 +47,7 @@ export const rootRouter = createTRPCRouter({
|
||||
boards: boardRouter,
|
||||
password: passwordRouter,
|
||||
notebook: notebookRouter,
|
||||
smartHomeEntityState: smartHomeEntityStateRouter
|
||||
});
|
||||
|
||||
// export type definition of API
|
||||
|
||||
53
src/server/api/routers/smart-home/entity-state.ts
Normal file
53
src/server/api/routers/smart-home/entity-state.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { ZodError, z } from 'zod';
|
||||
|
||||
|
||||
import { createTRPCRouter, protectedProcedure } from '../../trpc';
|
||||
|
||||
import { findAppProperty } from '~/tools/client/app-properties';
|
||||
import { getConfig } from '~/tools/config/getConfig';
|
||||
import { HomeAssistantSingleton } from '~/tools/singleton/HomeAssistantSingleton';
|
||||
|
||||
export const smartHomeEntityStateRouter = createTRPCRouter({
|
||||
retrieveStatus: protectedProcedure
|
||||
.input(
|
||||
z.object({
|
||||
configName: z.string(),
|
||||
entityId: z.string().regex(/^[A-Za-z0-9-_\.]+$/)
|
||||
})
|
||||
)
|
||||
.query(async ({ input }) => {
|
||||
const config = getConfig(input.configName);
|
||||
|
||||
const instances = config.apps.filter((app) => app.integration?.type == 'homeAssistant');
|
||||
|
||||
for (var instance of instances) {
|
||||
const url = new URL(instance.url);
|
||||
const client = HomeAssistantSingleton.getOrSet(url, findAppProperty(instance, 'apiKey'));
|
||||
const state = await client.getEntityState(input.entityId);
|
||||
|
||||
if (!state.success) {
|
||||
if (!(state.error instanceof ZodError)) {
|
||||
continue;
|
||||
}
|
||||
// Consola.error('Unable to handle entity state: ', state.error);
|
||||
throw new TRPCError({
|
||||
code: 'NOT_IMPLEMENTED',
|
||||
message: `Unable to handle Home Assistant entity state. This may be due to malformed response or unknown entity type. Check log for details`
|
||||
});
|
||||
}
|
||||
|
||||
if(!state.data) {
|
||||
throw new TRPCError({
|
||||
code: 'INTERNAL_SERVER_ERROR',
|
||||
message: `Home Assistant: Unable to connect to app '${instance.id}'. Check logs for details`
|
||||
});
|
||||
}
|
||||
|
||||
return state.data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}),
|
||||
});
|
||||
41
src/tools/server/sdk/homeassistant/HomeAssistant.ts
Normal file
41
src/tools/server/sdk/homeassistant/HomeAssistant.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import Consola from 'consola';
|
||||
import { appendPath } from '~/tools/shared/strings';
|
||||
import { entityStateSchema } from './models/EntityState';
|
||||
|
||||
export class HomeAssistant {
|
||||
public readonly basePath: URL;
|
||||
private readonly token: string;
|
||||
|
||||
constructor(url: URL, token: string) {
|
||||
if (!url.pathname.endsWith('/')) {
|
||||
url.pathname += "/";
|
||||
}
|
||||
url.pathname += 'api';
|
||||
this.basePath = url;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
async getEntityState(entityId: string) {
|
||||
try {
|
||||
const response = await fetch(appendPath(this.basePath, `/states/${entityId}`), {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${this.token}`
|
||||
}
|
||||
});
|
||||
const body = await response.json();
|
||||
if (!response.ok) {
|
||||
return {
|
||||
success: false as const,
|
||||
error: body
|
||||
};
|
||||
}
|
||||
return entityStateSchema.safeParseAsync(body);
|
||||
} catch (err) {
|
||||
Consola.error(`Failed to fetch from '${this.basePath}': ${err}`);
|
||||
return {
|
||||
success: false as const,
|
||||
error: err
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/tools/server/sdk/homeassistant/models/EntityState.ts
Normal file
12
src/tools/server/sdk/homeassistant/models/EntityState.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
|
||||
export const entityStateSchema = z.object({
|
||||
attributes: z.record(z.union([z.string(), z.number(), z.boolean()])),
|
||||
entity_id: z.string(),
|
||||
last_changed: z.string().pipe(z.coerce.date()),
|
||||
last_updated: z.string().pipe(z.coerce.date()),
|
||||
state: z.string(),
|
||||
});
|
||||
|
||||
export type EntityState = z.infer<typeof entityStateSchema>;
|
||||
@@ -29,6 +29,7 @@ export const boardNamespaces = [
|
||||
'modules/dns-hole-controls',
|
||||
'modules/bookmark',
|
||||
'modules/notebook',
|
||||
'modules/smart-home/entity-state',
|
||||
'widgets/error-boundary',
|
||||
'widgets/draggable-list',
|
||||
'widgets/location',
|
||||
|
||||
@@ -12,3 +12,9 @@ export const trimStringEnding = (original: string, toTrimIfExists: string[]) =>
|
||||
export const firstUpperCase = (str: string) => {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
};
|
||||
|
||||
export const appendPath = (url: URL, path: string) => {
|
||||
const newUrl = new URL(url);
|
||||
newUrl.pathname += path;
|
||||
return newUrl;
|
||||
}
|
||||
|
||||
20
src/tools/singleton/HomeAssistantSingleton.ts
Normal file
20
src/tools/singleton/HomeAssistantSingleton.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { HomeAssistant } from '../server/sdk/homeassistant/HomeAssistant';
|
||||
|
||||
export class HomeAssistantSingleton {
|
||||
private static _instances: HomeAssistant[] = [];
|
||||
|
||||
public static getOrSet(url: URL, token: string): HomeAssistant {
|
||||
const match = this._instances.find(
|
||||
(instance) =>
|
||||
instance.basePath.hostname === url.hostname && instance.basePath.port === url.port
|
||||
);
|
||||
|
||||
if (!match) {
|
||||
const instance = new HomeAssistant(url, token);
|
||||
this._instances.push(instance);
|
||||
return instance;
|
||||
}
|
||||
|
||||
return match;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Icon, IconKey, IconPassword, IconUser } from '@tabler/icons-react';
|
||||
|
||||
import { Property } from 'csstype';
|
||||
|
||||
import { TileBaseType } from './tile';
|
||||
@@ -55,7 +56,8 @@ export type IntegrationType =
|
||||
| 'jellyfin'
|
||||
| 'nzbGet'
|
||||
| 'pihole'
|
||||
| 'adGuardHome';
|
||||
| 'adGuardHome'
|
||||
| 'homeAssistant';
|
||||
|
||||
export type AppIntegrationType = {
|
||||
type: IntegrationType | null;
|
||||
@@ -97,6 +99,7 @@ export const integrationFieldProperties: {
|
||||
plex: ['apiKey'],
|
||||
pihole: ['apiKey'],
|
||||
adGuardHome: ['username', 'password'],
|
||||
homeAssistant: ['apiKey']
|
||||
};
|
||||
|
||||
export type IntegrationFieldDefinitionType = {
|
||||
|
||||
@@ -11,6 +11,7 @@ import mediaRequestsStats from './media-requests/MediaRequestStatsTile';
|
||||
import mediaServer from './media-server/MediaServerTile';
|
||||
import notebook from './notebook/NotebookWidgetTile';
|
||||
import rss from './rss/RssWidgetTile';
|
||||
import smartHomeEntityState from './smart-home/entity-state/entity-state.widget';
|
||||
import torrent from './torrent/TorrentTile';
|
||||
import usenet from './useNet/UseNetTile';
|
||||
import videoStream from './video/VideoStreamTile';
|
||||
@@ -34,4 +35,5 @@ export default {
|
||||
'dns-hole-controls': dnsHoleControls,
|
||||
bookmark,
|
||||
notebook,
|
||||
'smart-home/entity-state': smartHomeEntityState
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ import StarterKit from '@tiptap/starter-kit';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { Dispatch, SetStateAction, useState } from 'react';
|
||||
import { useEditModeStore } from '~/components/Dashboard/Views/useEditModeStore';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { useConfigStore } from '~/config/store';
|
||||
import { api } from '~/utils/api';
|
||||
@@ -62,9 +63,10 @@ import { INotebookWidget } from './NotebookWidgetTile';
|
||||
export function Editor({ widget }: { widget: INotebookWidget }) {
|
||||
const [content, setContent] = useState(widget.properties.content);
|
||||
const [toSaveContent, setToSaveContent] = useState(content);
|
||||
const isEditMode = useEditModeStore((x) => x.enabled);
|
||||
|
||||
const { data: sessionData } = useSession();
|
||||
const enabled = !!sessionData?.user.isAdmin;
|
||||
const enabled = !!sessionData?.user.isAdmin && !isEditMode;
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
|
||||
const { config, name: configName } = useConfigContext();
|
||||
|
||||
98
src/widgets/smart-home/entity-state/entity-state.widget.tsx
Normal file
98
src/widgets/smart-home/entity-state/entity-state.widget.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Center, Loader, Stack, Text, Tooltip } from '@mantine/core';
|
||||
import { IconAlertHexagon, IconBinaryTree, IconExclamationMark } from '@tabler/icons-react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useConfigContext } from '~/config/provider';
|
||||
import { api } from '~/utils/api';
|
||||
import { defineWidget } from '~/widgets/helper';
|
||||
import { WidgetLoading } from '~/widgets/loading';
|
||||
import { IWidget } from '~/widgets/widgets';
|
||||
|
||||
const definition = defineWidget({
|
||||
id: 'smart-home/entity-state',
|
||||
icon: IconBinaryTree,
|
||||
options: {
|
||||
entityId: {
|
||||
type: 'text',
|
||||
defaultValue: 'sun.sun',
|
||||
info: true,
|
||||
},
|
||||
displayName: {
|
||||
type: 'text',
|
||||
defaultValue: 'Sun',
|
||||
},
|
||||
},
|
||||
gridstack: {
|
||||
minWidth: 1,
|
||||
minHeight: 1,
|
||||
maxWidth: 12,
|
||||
maxHeight: 12,
|
||||
},
|
||||
component: EntityStateTile,
|
||||
});
|
||||
|
||||
export type ISmartHomeEntityStateWidget = IWidget<(typeof definition)['id'], typeof definition>;
|
||||
|
||||
interface SmartHomeEntityStateWidgetProps {
|
||||
widget: ISmartHomeEntityStateWidget;
|
||||
}
|
||||
|
||||
function EntityStateTile({ widget }: SmartHomeEntityStateWidgetProps) {
|
||||
const { t } = useTranslation('modules/smart-home/entity-state');
|
||||
const { name: configName } = useConfigContext();
|
||||
|
||||
const { data, isInitialLoading, isLoading, isError, error } =
|
||||
api.smartHomeEntityState.retrieveStatus.useQuery(
|
||||
{
|
||||
configName: configName!,
|
||||
entityId: widget.properties.entityId,
|
||||
},
|
||||
{
|
||||
enabled: !!configName,
|
||||
refetchInterval: 2 * 60 * 1000
|
||||
}
|
||||
);
|
||||
|
||||
let dataComponent = null;
|
||||
|
||||
if (isError) {
|
||||
dataComponent = (
|
||||
<Tooltip label={error.message} withArrow withinPortal>
|
||||
<IconAlertHexagon color="red" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (!dataComponent && isInitialLoading) {
|
||||
dataComponent = <WidgetLoading />;
|
||||
}
|
||||
|
||||
if (!dataComponent && !data) {
|
||||
dataComponent = (
|
||||
<Tooltip label={t('entityNotFound')} withArrow withinPortal>
|
||||
<IconExclamationMark color="red" />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
if (!dataComponent) {
|
||||
dataComponent = (
|
||||
<Text align="center">
|
||||
{data?.state}
|
||||
{isLoading && <Loader ml="xs" size={10} />}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Center h="100%" w="100%">
|
||||
<Stack align="center" spacing={3}>
|
||||
<Text align="center" weight="bold" size="lg">
|
||||
{widget.properties.displayName}
|
||||
</Text>
|
||||
{dataComponent}
|
||||
</Stack>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
export default definition;
|
||||
Reference in New Issue
Block a user